Compare commits
9 Commits
v1.0.0-alp
...
v1.0.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35571760d6 | ||
|
|
a4d4418963 | ||
|
|
b2ee8a1036 | ||
|
|
fd5bc5271b | ||
|
|
43b66f60f3 | ||
|
|
0c13d589d8 | ||
|
|
2c7c44ecbc | ||
|
|
12470a1d8b | ||
|
|
42e95a83f5 |
1
.github/workflows/pint.yml
vendored
1
.github/workflows/pint.yml
vendored
@@ -2,6 +2,7 @@ name: Pint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [1.x]
|
||||
paths:
|
||||
- '**.php'
|
||||
|
||||
|
||||
24
README.md
24
README.md
@@ -61,12 +61,12 @@ class Project extends Model implements Commentable
|
||||
Add the commenter trait to your User model:
|
||||
|
||||
```php
|
||||
use Relaticle\Comments\Concerns\IsCommenter;
|
||||
use Relaticle\Comments\Contracts\Commenter;
|
||||
use Relaticle\Comments\Concerns\CanComment;
|
||||
use Relaticle\Comments\Contracts\Commentator;
|
||||
|
||||
class User extends Authenticatable implements Commenter
|
||||
class User extends Authenticatable implements Commentator
|
||||
{
|
||||
use IsCommenter;
|
||||
use CanComment;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -134,20 +134,20 @@ public static function infolist(Infolist $infolist): Infolist
|
||||
<tr>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
### Custom Fields
|
||||
[<img src="https://github.com/Relaticle/custom-fields/raw/2.x/art/preview.png" width="100%" />](https://relaticle.github.io/custom-fields)
|
||||
### FilaForms
|
||||
[<img src="https://filaforms.app/img/og-image.png" width="100%" />](https://filaforms.app/)
|
||||
|
||||
Let users add custom fields to any model without code changes.
|
||||
[Learn more ->](https://relaticle.github.io/custom-fields)
|
||||
Visual form builder for all your public-facing forms.
|
||||
[Learn more ->](https://filaforms.app)
|
||||
|
||||
</td>
|
||||
<td width="50%" valign="top">
|
||||
|
||||
### Flowforge
|
||||
[<img src="https://github.com/Relaticle/flowforge/raw/4.x/art/preview.png" width="100%" />](https://relaticle.github.io/flowforge)
|
||||
### Custom Fields
|
||||
[<img src="https://github.com/Relaticle/custom-fields/raw/3.x/art/preview.png" width="100%" />](https://relaticle.github.io/custom-fields)
|
||||
|
||||
Transform any Laravel model into a drag-and-drop Kanban board.
|
||||
[Learn more ->](https://relaticle.github.io/flowforge)
|
||||
Let users add custom fields to any model without code changes.
|
||||
[Learn more ->](https://relaticle.github.io/custom-fields)
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,19 +1,29 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Models\User;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Mentions\DefaultMentionResolver;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Policies\CommentPolicy;
|
||||
|
||||
return [
|
||||
'tables' => [
|
||||
'comments' => 'comments',
|
||||
],
|
||||
|
||||
'models' => [
|
||||
'comment' => Comment::class,
|
||||
],
|
||||
|
||||
'table_names' => [
|
||||
'comments' => 'comments',
|
||||
'reactions' => 'comment_reactions',
|
||||
'mentions' => 'comment_mentions',
|
||||
'subscriptions' => 'comment_subscriptions',
|
||||
'attachments' => 'comment_attachments',
|
||||
],
|
||||
|
||||
'column_names' => [
|
||||
'commenter_morph' => 'commenter',
|
||||
],
|
||||
|
||||
'commenter' => [
|
||||
'model' => User::class,
|
||||
],
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Relaticle\Comments\Database\Factories;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
|
||||
class CommentFactory extends Factory
|
||||
{
|
||||
|
||||
@@ -8,10 +8,10 @@ return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('comment_attachments', function (Blueprint $table) {
|
||||
Schema::create(config('comments.table_names.attachments', 'comment_attachments'), function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('comment_id')
|
||||
->constrained(config('comments.tables.comments', 'comments'))
|
||||
->constrained(config('comments.table_names.comments', 'comments'))
|
||||
->cascadeOnDelete();
|
||||
$table->string('file_path');
|
||||
$table->string('original_name');
|
||||
|
||||
@@ -8,15 +8,15 @@ return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('comment_mentions', function (Blueprint $table) {
|
||||
Schema::create(config('comments.table_names.mentions', 'comment_mentions'), function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('comment_id')
|
||||
->constrained(config('comments.tables.comments', 'comments'))
|
||||
->constrained(config('comments.table_names.comments', 'comments'))
|
||||
->cascadeOnDelete();
|
||||
$table->morphs('user');
|
||||
$table->morphs('commenter');
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['comment_id', 'user_id', 'user_type']);
|
||||
$table->unique(['comment_id', 'commenter_id', 'commenter_type']);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,16 +8,16 @@ return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('comment_reactions', function (Blueprint $table) {
|
||||
Schema::create(config('comments.table_names.reactions', 'comment_reactions'), function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('comment_id')
|
||||
->constrained(config('comments.tables.comments', 'comments'))
|
||||
->constrained(config('comments.table_names.comments', 'comments'))
|
||||
->cascadeOnDelete();
|
||||
$table->morphs('user');
|
||||
$table->morphs('commenter');
|
||||
$table->string('reaction');
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['comment_id', 'user_id', 'user_type', 'reaction']);
|
||||
$table->unique(['comment_id', 'commenter_id', 'commenter_type', 'reaction']);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,13 +8,13 @@ return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('comment_subscriptions', function (Blueprint $table) {
|
||||
Schema::create(config('comments.table_names.subscriptions', 'comment_subscriptions'), function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('commentable');
|
||||
$table->morphs('user');
|
||||
$table->morphs('commenter');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
|
||||
$table->unique(['commentable_type', 'commentable_id', 'user_type', 'user_id'], 'comment_subscriptions_unique');
|
||||
$table->unique(['commentable_type', 'commentable_id', 'commenter_type', 'commenter_id'], 'comment_subscriptions_unique');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,13 +8,13 @@ return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create(config('comments.tables.comments', 'comments'), function (Blueprint $table) {
|
||||
Schema::create(config('comments.table_names.comments', 'comments'), function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('commentable');
|
||||
$table->morphs('user');
|
||||
$table->morphs('commenter');
|
||||
$table->foreignId('parent_id')
|
||||
->nullable()
|
||||
->constrained(config('comments.tables.comments', 'comments'))
|
||||
->constrained(config('comments.table_names.comments', 'comments'))
|
||||
->cascadeOnDelete();
|
||||
$table->text('body');
|
||||
$table->timestamp('edited_at')->nullable();
|
||||
|
||||
BIN
docs/.data/content/contents.sqlite
Normal file
BIN
docs/.data/content/contents.sqlite
Normal file
Binary file not shown.
103
docs/components/AppHeader.vue
Normal file
103
docs/components/AppHeader.vue
Normal file
@@ -0,0 +1,103 @@
|
||||
<script setup lang="ts">
|
||||
import { useDocusI18n } from '#imports'
|
||||
|
||||
const appConfig = useAppConfig()
|
||||
const site = useSiteConfig()
|
||||
|
||||
const { localePath, isEnabled, locales } = useDocusI18n()
|
||||
const { currentVersion, isOldVersion, loadVersions } = useVersions()
|
||||
|
||||
onMounted(() => loadVersions())
|
||||
|
||||
const links = computed(() => appConfig.github && appConfig.github.url
|
||||
? [
|
||||
{
|
||||
'icon': 'i-simple-icons-github',
|
||||
'to': appConfig.github.url,
|
||||
'target': '_blank',
|
||||
'aria-label': 'GitHub',
|
||||
},
|
||||
]
|
||||
: [])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sticky top-0 z-50">
|
||||
<!-- Version Warning Banner -->
|
||||
<div
|
||||
v-if="isOldVersion"
|
||||
class="bg-amber-100 dark:bg-amber-900/50 text-amber-800 dark:text-amber-200 px-4 py-2 text-center text-sm border-b border-amber-200 dark:border-amber-800"
|
||||
>
|
||||
You are viewing documentation for Comments {{ currentVersion }}.
|
||||
<a
|
||||
href="/comments/"
|
||||
class="underline font-medium hover:text-amber-900 dark:hover:text-amber-100"
|
||||
>
|
||||
View the latest version →
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Original Docus Header -->
|
||||
<UHeader
|
||||
:ui="{ center: 'flex-1' }"
|
||||
:to="localePath('/')"
|
||||
:title="appConfig.header?.title || site.name"
|
||||
>
|
||||
<AppHeaderCenter />
|
||||
|
||||
<template #title>
|
||||
<AppHeaderLogo class="h-6 w-auto shrink-0" />
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
<AppVersionSwitcher />
|
||||
<AppHeaderCTA />
|
||||
|
||||
<template v-if="isEnabled && locales.length > 1">
|
||||
<ClientOnly>
|
||||
<LanguageSelect />
|
||||
|
||||
<template #fallback>
|
||||
<div class="h-8 w-8 animate-pulse bg-neutral-200 dark:bg-neutral-800 rounded-md" />
|
||||
</template>
|
||||
</ClientOnly>
|
||||
|
||||
<USeparator
|
||||
orientation="vertical"
|
||||
class="h-8"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<UContentSearchButton class="lg:hidden" />
|
||||
|
||||
<ClientOnly>
|
||||
<UColorModeButton />
|
||||
|
||||
<template #fallback>
|
||||
<div class="h-8 w-8 animate-pulse bg-neutral-200 dark:bg-neutral-800 rounded-md" />
|
||||
</template>
|
||||
</ClientOnly>
|
||||
|
||||
<template v-if="links?.length">
|
||||
<UButton
|
||||
v-for="(link, index) of links"
|
||||
:key="index"
|
||||
v-bind="{ color: 'neutral', variant: 'ghost', ...link }"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template #toggle="{ open, toggle }">
|
||||
<IconMenuToggle
|
||||
:open="open"
|
||||
class="lg:hidden"
|
||||
@click="toggle"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #body>
|
||||
<AppHeaderBody />
|
||||
</template>
|
||||
</UHeader>
|
||||
</div>
|
||||
</template>
|
||||
16
docs/components/AppHeaderLogo.vue
Normal file
16
docs/components/AppHeaderLogo.vue
Normal file
@@ -0,0 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
const appConfig = useAppConfig()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<UColorModeImage
|
||||
v-if="appConfig.docus?.header?.logo?.dark || appConfig.docus?.header?.logo?.light"
|
||||
:light="appConfig.docus?.header?.logo?.light || appConfig.docus?.header?.logo?.dark"
|
||||
:dark="appConfig.docus?.header?.logo?.dark || appConfig.docus?.header?.logo?.light"
|
||||
:alt="appConfig.docus?.header?.logo?.alt || appConfig.docus?.title"
|
||||
class="h-8 w-auto shrink-0"
|
||||
/>
|
||||
<span v-else class="text-lg font-semibold">
|
||||
{{ appConfig.docus?.title || 'Comments' }}
|
||||
</span>
|
||||
</template>
|
||||
38
docs/components/AppVersionSwitcher.vue
Normal file
38
docs/components/AppVersionSwitcher.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<script setup lang="ts">
|
||||
const { versions, currentVersion, currentTitle, loadVersions } = useVersions()
|
||||
|
||||
onMounted(() => loadVersions())
|
||||
|
||||
function switchVersion(version: { version: string; path: string }): void {
|
||||
if (version.version !== currentVersion) {
|
||||
window.location.href = version.path
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="versions.length > 1" class="relative" @click.stop>
|
||||
<UPopover>
|
||||
<UButton
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
:label="currentTitle"
|
||||
trailing-icon="i-lucide-chevron-down"
|
||||
/>
|
||||
<template #content>
|
||||
<div class="p-1">
|
||||
<button
|
||||
v-for="version in versions"
|
||||
:key="version.version"
|
||||
class="w-full px-3 py-2 text-left text-sm rounded hover:bg-gray-100 dark:hover:bg-gray-800 flex items-center gap-2"
|
||||
:class="{ 'font-medium text-primary': version.version === currentVersion }"
|
||||
@click="switchVersion(version)"
|
||||
>
|
||||
{{ version.title }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
</UPopover>
|
||||
</div>
|
||||
<UBadge v-else-if="currentVersion" variant="subtle" color="neutral">{{ currentVersion }}</UBadge>
|
||||
</template>
|
||||
60
docs/composables/useVersions.ts
Normal file
60
docs/composables/useVersions.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
interface Version {
|
||||
version: string
|
||||
title: string
|
||||
path: string
|
||||
branch: string
|
||||
isLatest: boolean
|
||||
}
|
||||
|
||||
const versions = ref<Version[]>([])
|
||||
const isLoaded = ref(false)
|
||||
const isLoading = ref(false)
|
||||
|
||||
export function useVersions() {
|
||||
const config = useRuntimeConfig()
|
||||
const currentVersion = config.public.docsVersion || '1.x'
|
||||
|
||||
async function loadVersions() {
|
||||
if (isLoaded.value || isLoading.value) return
|
||||
isLoading.value = true
|
||||
|
||||
try {
|
||||
const res = await fetch('/comments/versions.json')
|
||||
if (res.ok) {
|
||||
versions.value = await res.json()
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failed to load versions.json:', e)
|
||||
} finally {
|
||||
isLoaded.value = true
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const latestVersion = computed(() =>
|
||||
versions.value.find(v => v.isLatest)
|
||||
)
|
||||
|
||||
const currentVersionInfo = computed(() =>
|
||||
versions.value.find(v => v.version === currentVersion)
|
||||
)
|
||||
|
||||
const isOldVersion = computed(() => {
|
||||
if (!isLoaded.value) return false
|
||||
return currentVersionInfo.value?.isLatest === false
|
||||
})
|
||||
|
||||
const currentTitle = computed(() =>
|
||||
currentVersionInfo.value?.title || currentVersion
|
||||
)
|
||||
|
||||
return {
|
||||
versions,
|
||||
currentVersion,
|
||||
currentTitle,
|
||||
latestVersion,
|
||||
isOldVersion,
|
||||
isLoaded,
|
||||
loadVersions,
|
||||
}
|
||||
}
|
||||
@@ -73,15 +73,15 @@ class Project extends Model implements Commentable
|
||||
}
|
||||
```
|
||||
|
||||
Add the `IsCommenter` trait to your User model:
|
||||
Add the `CanComment` trait to your User model:
|
||||
|
||||
```php [app/Models/User.php]
|
||||
use Relaticle\Comments\Concerns\IsCommenter;
|
||||
use Relaticle\Comments\Contracts\Commenter;
|
||||
use Relaticle\Comments\Concerns\CanComment;
|
||||
use Relaticle\Comments\Contracts\Commentator;
|
||||
|
||||
class User extends Authenticatable implements Commenter
|
||||
class User extends Authenticatable implements Commentator
|
||||
{
|
||||
use IsCommenter;
|
||||
use CanComment;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
---
|
||||
title: Introduction
|
||||
description: A full-featured commenting system for Filament panels.
|
||||
navigation:
|
||||
icon: i-lucide-home
|
||||
seo:
|
||||
title: Introduction
|
||||
description: Learn about Comments - a full-featured commenting system for Filament panels with threaded replies, @mentions, emoji reactions, and real-time updates.
|
||||
ogImage: /preview.png
|
||||
---
|
||||
|
||||
Welcome to **Comments**, a powerful Laravel package that adds a full-featured commenting system to any Filament panel.
|
||||
|
||||
## What is Comments?
|
||||
|
||||
Comments provides polymorphic commenting on any Eloquent model with deep Filament integration. Add threaded discussions, @mentions, emoji reactions, file attachments, and real-time notifications to your admin panel with minimal setup.
|
||||
|
||||
## Why Choose Comments?
|
||||
|
||||
::card-group
|
||||
:::card
|
||||
---
|
||||
icon: i-lucide-messages-square
|
||||
title: Threaded Discussions
|
||||
---
|
||||
Nested replies with configurable depth limits keep conversations organized and easy to follow.
|
||||
:::
|
||||
|
||||
:::card
|
||||
---
|
||||
icon: i-lucide-clock
|
||||
title: Quick Setup
|
||||
---
|
||||
Add traits to your models, register the plugin, and you have a working comment system in minutes.
|
||||
:::
|
||||
|
||||
:::card
|
||||
---
|
||||
icon: i-lucide-puzzle
|
||||
title: 3 Integration Patterns
|
||||
---
|
||||
Use as a slide-over action, table row action, or inline infolist entry - whatever fits your resource.
|
||||
:::
|
||||
|
||||
:::card
|
||||
---
|
||||
icon: i-lucide-bell
|
||||
title: Built-in Notifications
|
||||
---
|
||||
Database and mail notifications with subscription management and auto-subscribe for authors and mentioned users.
|
||||
:::
|
||||
::
|
||||
@@ -15,21 +15,34 @@ php artisan vendor:publish --tag=comments-config
|
||||
|
||||
This creates `config/comments.php` with all available options.
|
||||
|
||||
## Table Name
|
||||
## Table Names
|
||||
|
||||
```php
|
||||
'tables' => [
|
||||
'table_names' => [
|
||||
'comments' => 'comments',
|
||||
'reactions' => 'comment_reactions',
|
||||
'mentions' => 'comment_mentions',
|
||||
'subscriptions' => 'comment_subscriptions',
|
||||
'attachments' => 'comment_attachments',
|
||||
],
|
||||
```
|
||||
|
||||
Change the table name if it conflicts with your application.
|
||||
Change the table names if they conflict with your application.
|
||||
|
||||
## Column Names
|
||||
|
||||
```php
|
||||
'column_names' => [
|
||||
'commenter_id' => 'commenter_id',
|
||||
'commenter_type' => 'commenter_type',
|
||||
],
|
||||
```
|
||||
|
||||
## Models
|
||||
|
||||
```php
|
||||
'models' => [
|
||||
'comment' => \Relaticle\Comments\Comment::class,
|
||||
'comment' => \Relaticle\Comments\Models\Comment::class,
|
||||
],
|
||||
|
||||
'commenter' => [
|
||||
@@ -178,10 +191,10 @@ When broadcasting is disabled, the Livewire component polls for new comments at
|
||||
Override how the authenticated user is resolved:
|
||||
|
||||
```php
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
|
||||
// In AppServiceProvider::boot()
|
||||
Config::resolveAuthenticatedUserUsing(function () {
|
||||
CommentsConfig::resolveAuthenticatedUserUsing(function () {
|
||||
return auth()->user();
|
||||
});
|
||||
```
|
||||
|
||||
@@ -26,34 +26,34 @@ Create your own policy to customize authorization:
|
||||
```php
|
||||
namespace App\Policies;
|
||||
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Contracts\Commenter;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Contracts\Commentator;
|
||||
|
||||
class CustomCommentPolicy
|
||||
{
|
||||
public function viewAny(Commenter $user): bool
|
||||
public function viewAny(Commentator $user): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function create(Commenter $user): bool
|
||||
public function create(Commentator $user): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function update(Commenter $user, Comment $comment): bool
|
||||
public function update(Commentator $user, Comment $comment): bool
|
||||
{
|
||||
return $comment->user_id === $user->getKey()
|
||||
&& $comment->user_type === $user->getMorphClass();
|
||||
return $comment->commenter_id === $user->getKey()
|
||||
&& $comment->commenter_type === $user->getMorphClass();
|
||||
}
|
||||
|
||||
public function delete(Commenter $user, Comment $comment): bool
|
||||
public function delete(Commentator $user, Comment $comment): bool
|
||||
{
|
||||
return $comment->user_id === $user->getKey()
|
||||
return $comment->commenter_id === $user->getKey()
|
||||
|| $user->hasRole('admin');
|
||||
}
|
||||
|
||||
public function reply(Commenter $user, Comment $comment): bool
|
||||
public function reply(Commentator $user, Comment $comment): bool
|
||||
{
|
||||
return $comment->canReply();
|
||||
}
|
||||
|
||||
@@ -48,4 +48,4 @@ Keys are stored in the database. If you change a key, existing reactions with th
|
||||
|
||||
## Storage
|
||||
|
||||
Reactions are stored in the `comment_reactions` table with a unique constraint on `(comment_id, user_id, user_type, reaction)`, ensuring one reaction of each type per user per comment.
|
||||
Reactions are stored in the `comment_reactions` table with a unique constraint on `(comment_id, commenter_id, commenter_type, reaction)`, ensuring one reaction of each type per user per comment.
|
||||
|
||||
@@ -63,7 +63,7 @@ When a comment is deleted, its attachments are cascade deleted from the database
|
||||
|
||||
## Helper Methods
|
||||
|
||||
The `CommentAttachment` model provides:
|
||||
The `Attachment` model (`Relaticle\Comments\Models\Attachment`) provides:
|
||||
|
||||
```php
|
||||
$attachment->isImage(); // Check if attachment is an image
|
||||
|
||||
@@ -61,17 +61,17 @@ Users can toggle their subscription using the subscribe/unsubscribe button in th
|
||||
### Programmatic Access
|
||||
|
||||
```php
|
||||
use Relaticle\Comments\CommentSubscription;
|
||||
use Relaticle\Comments\Models\Subscription;
|
||||
|
||||
// Check subscription status
|
||||
CommentSubscription::isSubscribed($commentable, $user);
|
||||
Subscription::isSubscribed($commentable, $user);
|
||||
|
||||
// Subscribe/unsubscribe
|
||||
CommentSubscription::subscribe($commentable, $user);
|
||||
CommentSubscription::unsubscribe($commentable, $user);
|
||||
Subscription::subscribe($commentable, $user);
|
||||
Subscription::unsubscribe($commentable, $user);
|
||||
|
||||
// Get all subscribers for a commentable
|
||||
$subscribers = CommentSubscription::subscribersFor($commentable);
|
||||
$subscribers = Subscription::subscribersFor($commentable);
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
@@ -20,8 +20,8 @@ The main comments table with polymorphic relationships and threading support.
|
||||
| `id` | bigint | Primary key |
|
||||
| `commentable_type` | string | Polymorphic model type |
|
||||
| `commentable_id` | bigint | Polymorphic model ID |
|
||||
| `user_type` | string | Commenter model type |
|
||||
| `user_id` | bigint | Commenter model ID |
|
||||
| `commenter_type` | string | Commenter model type |
|
||||
| `commenter_id` | bigint | Commenter model ID |
|
||||
| `parent_id` | bigint (nullable) | Parent comment for replies |
|
||||
| `body` | text | HTML comment content |
|
||||
| `edited_at` | timestamp (nullable) | When the comment was last edited |
|
||||
@@ -39,12 +39,12 @@ Tracks emoji reactions per user per comment.
|
||||
|--------|------|-------------|
|
||||
| `id` | bigint | Primary key |
|
||||
| `comment_id` | bigint | Foreign key to comments |
|
||||
| `user_type` | string | Reactor model type |
|
||||
| `user_id` | bigint | Reactor model ID |
|
||||
| `commenter_type` | string | Reactor model type |
|
||||
| `commenter_id` | bigint | Reactor model ID |
|
||||
| `reaction` | string | Reaction key (e.g., `thumbs_up`) |
|
||||
| `created_at` | timestamp | |
|
||||
|
||||
**Unique constraint:** `(comment_id, user_id, user_type, reaction)`
|
||||
**Unique constraint:** `(comment_id, commenter_id, commenter_type, reaction)`
|
||||
|
||||
### comment_mentions
|
||||
|
||||
@@ -54,11 +54,11 @@ Tracks @mentioned users per comment.
|
||||
|--------|------|-------------|
|
||||
| `id` | bigint | Primary key |
|
||||
| `comment_id` | bigint | Foreign key to comments |
|
||||
| `user_type` | string | Mentioned user model type |
|
||||
| `user_id` | bigint | Mentioned user model ID |
|
||||
| `commenter_type` | string | Mentioned user model type |
|
||||
| `commenter_id` | bigint | Mentioned user model ID |
|
||||
| `created_at` | timestamp | |
|
||||
|
||||
**Unique constraint:** `(comment_id, user_id, user_type)`
|
||||
**Unique constraint:** `(comment_id, commenter_id, commenter_type)`
|
||||
|
||||
### comment_subscriptions
|
||||
|
||||
@@ -69,11 +69,11 @@ Tracks which users are subscribed to comment threads on specific models.
|
||||
| `id` | bigint | Primary key |
|
||||
| `commentable_type` | string | Subscribed model type |
|
||||
| `commentable_id` | bigint | Subscribed model ID |
|
||||
| `user_type` | string | Subscriber model type |
|
||||
| `user_id` | bigint | Subscriber model ID |
|
||||
| `commenter_type` | string | Subscriber model type |
|
||||
| `commenter_id` | bigint | Subscriber model ID |
|
||||
| `created_at` | timestamp | |
|
||||
|
||||
**Unique constraint:** `(commentable_type, commentable_id, user_type, user_id)`
|
||||
**Unique constraint:** `(commentable_type, commentable_id, commenter_type, commenter_id)`
|
||||
|
||||
### comment_attachments
|
||||
|
||||
@@ -96,11 +96,11 @@ Stores file attachment metadata for comments.
|
||||
```
|
||||
Commentable Model (e.g., Project)
|
||||
└── comments (morphMany)
|
||||
├── user (morphTo → User)
|
||||
├── commenter (morphTo → User)
|
||||
├── parent (belongsTo → Comment)
|
||||
├── replies (hasMany → Comment)
|
||||
├── reactions (hasMany → CommentReaction)
|
||||
├── attachments (hasMany → CommentAttachment)
|
||||
├── reactions (hasMany → Reaction)
|
||||
├── attachments (hasMany → Attachment)
|
||||
└── mentions (morphToMany → User)
|
||||
```
|
||||
|
||||
|
||||
@@ -37,6 +37,14 @@ Drop-in integration with any Filament resource.
|
||||
:::
|
||||
::
|
||||
|
||||
::callout{icon="i-lucide-triangle-alert" color="amber"}
|
||||
**Alpha Software** -- This package is currently in alpha. The API is not stable and breaking changes may occur between releases without prior notice. Do not use in production unless you are prepared to handle upgrades manually.
|
||||
::
|
||||
|
||||
<div class="max-w-5xl mx-auto mt-8">
|
||||
<img src="/preview.png" alt="Comments - threaded discussions in Filament" class="rounded-lg shadow-lg w-full" />
|
||||
</div>
|
||||
|
||||
::u-page-section
|
||||
#title
|
||||
Why choose Comments?
|
||||
@@ -125,6 +133,8 @@ Extend your Laravel applications with our ecosystem of complementary tools
|
||||
to: https://filaforms.app
|
||||
target: _blank
|
||||
---
|
||||
:img{src="https://filaforms.app/img/og-image.png" alt="FilaForms" class="mb-4 rounded-lg w-full pointer-events-none"}
|
||||
|
||||
Visual form builder for all your public-facing forms.
|
||||
:::
|
||||
|
||||
@@ -135,17 +145,9 @@ Extend your Laravel applications with our ecosystem of complementary tools
|
||||
to: https://relaticle.github.io/custom-fields
|
||||
target: _blank
|
||||
---
|
||||
Let users add custom fields to any model without code changes.
|
||||
:::
|
||||
:img{src="https://relaticle.github.io/custom-fields/og-image.png" alt="Custom Fields" class="mb-4 rounded-lg w-full pointer-events-none"}
|
||||
|
||||
:::card
|
||||
---
|
||||
title: Flowforge
|
||||
icon: i-lucide-kanban
|
||||
to: https://relaticle.github.io/flowforge
|
||||
target: _blank
|
||||
---
|
||||
Transform any Laravel model into a drag-and-drop Kanban board.
|
||||
Let users add custom fields to any model without code changes.
|
||||
:::
|
||||
::
|
||||
::
|
||||
|
||||
13
docs/public/logo-dark.svg
Normal file
13
docs/public/logo-dark.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 5.9 KiB |
13
docs/public/logo-light.svg
Normal file
13
docs/public/logo-light.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 5.9 KiB |
BIN
docs/public/preview.png
Normal file
BIN
docs/public/preview.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 330 KiB |
@@ -31,12 +31,12 @@ class Project extends Model implements Commentable
|
||||
```
|
||||
|
||||
```php
|
||||
use Relaticle\Comments\Concerns\IsCommenter;
|
||||
use Relaticle\Comments\Contracts\Commenter;
|
||||
use Relaticle\Comments\Concerns\CanComment;
|
||||
use Relaticle\Comments\Contracts\Commentator;
|
||||
|
||||
class User extends Authenticatable implements Commenter
|
||||
class User extends Authenticatable implements Commentator
|
||||
{
|
||||
use IsCommenter;
|
||||
use CanComment;
|
||||
}
|
||||
```
|
||||
|
||||
@@ -113,7 +113,7 @@ Publish config: `php artisan vendor:publish --tag=comments-config`
|
||||
|
||||
| Key | Default | Purpose |
|
||||
|-----|---------|---------|
|
||||
| `tables.comments` | `'comments'` | Main comments table name |
|
||||
| `table_names.comments` | `'comments'` | Main comments table name |
|
||||
| `models.comment` | `Comment::class` | Comment model class |
|
||||
| `commenter.model` | `User::class` | Commenter (user) model class |
|
||||
| `policy` | `CommentPolicy::class` | Authorization policy class |
|
||||
@@ -183,14 +183,14 @@ Default `CommentPolicy` methods:
|
||||
```php
|
||||
namespace App\Policies;
|
||||
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Contracts\Commenter;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Contracts\Commentator;
|
||||
|
||||
class CustomCommentPolicy
|
||||
{
|
||||
public function delete(Commenter $user, Comment $comment): bool
|
||||
public function delete(Commentator $user, Comment $comment): bool
|
||||
{
|
||||
return $comment->user_id === $user->getKey()
|
||||
return $comment->commenter_id === $user->getKey()
|
||||
|| $user->hasRole('admin');
|
||||
}
|
||||
}
|
||||
@@ -201,10 +201,10 @@ class CustomCommentPolicy
|
||||
### Scoped Comments (Multi-tenancy)
|
||||
|
||||
```php
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
|
||||
// In AppServiceProvider::boot()
|
||||
Config::resolveAuthenticatedUserUsing(function () {
|
||||
CommentsConfig::resolveAuthenticatedUserUsing(function () {
|
||||
return auth()->user();
|
||||
});
|
||||
```
|
||||
@@ -276,7 +276,7 @@ $model->commentCount(); // Total count
|
||||
|
||||
// On Comment model
|
||||
$comment->commentable(); // Parent model (morphTo)
|
||||
$comment->user(); // Commenter (morphTo)
|
||||
$comment->commenter(); // Commenter (morphTo)
|
||||
$comment->parent(); // Parent comment (belongsTo)
|
||||
$comment->replies(); // Child comments (hasMany)
|
||||
$comment->reactions(); // Reactions (hasMany)
|
||||
|
||||
@@ -1,9 +1,58 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'deleted_comment' => 'This comment was deleted.',
|
||||
'edited' => 'edited',
|
||||
'load_more' => 'Load more comments',
|
||||
'no_comments' => 'No comments yet.',
|
||||
'comment_placeholder' => 'Write a comment...',
|
||||
'comments' => [
|
||||
'deleted' => 'This comment was deleted.',
|
||||
'edited' => 'edited',
|
||||
'no_comments' => 'No comments yet.',
|
||||
'placeholder' => 'Write a comment...',
|
||||
'load_more' => 'Load more comments',
|
||||
'sort_newest' => 'Newest first',
|
||||
'sort_oldest' => 'Oldest first',
|
||||
],
|
||||
|
||||
'actions' => [
|
||||
'reply' => 'Reply',
|
||||
'edit' => 'Edit',
|
||||
'delete' => 'Delete',
|
||||
'cancel' => 'Cancel',
|
||||
'save' => 'Save',
|
||||
'submit' => 'Submit',
|
||||
],
|
||||
|
||||
'reactions' => [
|
||||
'thumbs_up' => 'Thumbs up',
|
||||
'heart' => 'Heart',
|
||||
'celebrate' => 'Celebrate',
|
||||
'laugh' => 'Laugh',
|
||||
'thinking' => 'Thinking',
|
||||
'sad' => 'Sad',
|
||||
'reacted_by' => ':names reacted with :reaction',
|
||||
'and_others' => 'and :count others',
|
||||
],
|
||||
|
||||
'subscriptions' => [
|
||||
'subscribe' => 'Subscribe to replies',
|
||||
'unsubscribe' => 'Unsubscribe from replies',
|
||||
'subscribed' => 'You will be notified of new replies.',
|
||||
'unsubscribed' => 'You will no longer be notified.',
|
||||
],
|
||||
|
||||
'mentions' => [
|
||||
'no_results' => 'No users found',
|
||||
],
|
||||
|
||||
'attachments' => [
|
||||
'add' => 'Add attachment',
|
||||
'remove' => 'Remove',
|
||||
'too_large' => 'File is too large. Maximum size: :max KB.',
|
||||
'invalid_type' => 'File type not allowed.',
|
||||
],
|
||||
|
||||
'notifications' => [
|
||||
'reply_subject' => 'New reply to your comment',
|
||||
'reply_body' => ':name replied to your comment.',
|
||||
'mention_subject' => 'You were mentioned in a comment',
|
||||
'mention_body' => ':name mentioned you in a comment.',
|
||||
],
|
||||
];
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<div class="shrink-0">
|
||||
@if ($comment->trashed())
|
||||
<div class="h-8 w-8 rounded-full bg-gray-200 dark:bg-gray-700"></div>
|
||||
@elseif ($comment->user?->getCommentAvatarUrl())
|
||||
<img src="{{ $comment->user->getCommentAvatarUrl() }}" alt="{{ $comment->user->getCommentName() }}" class="h-8 w-8 rounded-full object-cover">
|
||||
@elseif ($comment->commenter?->getCommentAvatarUrl())
|
||||
<img src="{{ $comment->commenter->getCommentAvatarUrl() }}" alt="{{ $comment->commenter->getCommentDisplayName() }}" class="h-8 w-8 rounded-full object-cover">
|
||||
@else
|
||||
<div class="flex h-8 w-8 items-center justify-center rounded-full bg-primary-100 text-sm font-medium text-primary-700 dark:bg-primary-800 dark:text-primary-300">
|
||||
{{ str($comment->user?->getCommentName() ?? '?')->substr(0, 1)->upper() }}
|
||||
{{ str($comment->commenter?->getCommentDisplayName() ?? '?')->substr(0, 1)->upper() }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@@ -20,7 +20,7 @@
|
||||
{{-- Header: name + timestamp --}}
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-sm font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ $comment->user?->getCommentName() ?? 'Unknown' }}
|
||||
{{ $comment->commenter?->getCommentDisplayName() ?? 'Unknown' }}
|
||||
</span>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400" title="{{ $comment->created_at->format('M j, Y g:i A') }}">
|
||||
{{ $comment->created_at->diffForHumans() }}
|
||||
@@ -33,9 +33,11 @@
|
||||
{{-- Body or edit form --}}
|
||||
@if ($isEditing)
|
||||
<form wire:submit="saveEdit" class="mt-1">
|
||||
<textarea wire:model="editBody" rows="3"
|
||||
class="block w-full rounded-lg border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 sm:text-sm"
|
||||
></textarea>
|
||||
<x-filament::input.wrapper>
|
||||
<textarea wire:model="editBody" rows="3"
|
||||
class="block w-full border-none bg-transparent px-3 py-1.5 text-sm leading-6 text-gray-950 outline-none transition duration-75 placeholder:text-gray-400 focus:ring-0 dark:text-white dark:placeholder:text-gray-500"
|
||||
></textarea>
|
||||
</x-filament::input.wrapper>
|
||||
@error('editBody')
|
||||
<p class="mt-1 text-sm text-danger-600 dark:text-danger-400">{{ $message }}</p>
|
||||
@enderror
|
||||
@@ -167,14 +169,16 @@
|
||||
});
|
||||
}
|
||||
}">
|
||||
<textarea x-ref="replyInput"
|
||||
wire:model="replyBody"
|
||||
@input="handleInput($event)"
|
||||
@keydown="handleKeydown($event)"
|
||||
rows="2"
|
||||
placeholder="Write a reply..."
|
||||
class="block w-full rounded-lg border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 dark:placeholder-gray-400 sm:text-sm"
|
||||
></textarea>
|
||||
<x-filament::input.wrapper>
|
||||
<textarea x-ref="replyInput"
|
||||
wire:model="replyBody"
|
||||
@input="handleInput($event)"
|
||||
@keydown="handleKeydown($event)"
|
||||
rows="2"
|
||||
placeholder="Write a reply..."
|
||||
class="block w-full border-none bg-transparent px-3 py-1.5 text-sm leading-6 text-gray-950 outline-none transition duration-75 placeholder:text-gray-400 focus:ring-0 dark:text-white dark:placeholder:text-gray-500"
|
||||
></textarea>
|
||||
</x-filament::input.wrapper>
|
||||
|
||||
{{-- Mention autocomplete dropdown --}}
|
||||
<div x-show="showMentions" x-cloak
|
||||
@@ -200,14 +204,14 @@
|
||||
<p class="mt-1 text-sm text-danger-600 dark:text-danger-400">{{ $message }}</p>
|
||||
@enderror
|
||||
|
||||
@if (\Relaticle\Comments\Config::areAttachmentsEnabled())
|
||||
@if (\Relaticle\Comments\CommentsConfig::areAttachmentsEnabled())
|
||||
<div class="mt-2">
|
||||
<label class="flex cursor-pointer items-center gap-2 text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
||||
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" />
|
||||
</svg>
|
||||
Attach files
|
||||
<input type="file" wire:model="replyAttachments" multiple class="hidden" accept="{{ implode(',', \Relaticle\Comments\Config::getAttachmentAllowedTypes()) }}" />
|
||||
<input type="file" wire:model="replyAttachments" multiple class="hidden" accept="{{ implode(',', \Relaticle\Comments\CommentsConfig::getAttachmentAllowedTypes()) }}" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="space-y-4"
|
||||
@if (!\Relaticle\Comments\Config::isBroadcastingEnabled())
|
||||
wire:poll.{{ \Relaticle\Comments\Config::getPollingInterval() }}
|
||||
@if (!\Relaticle\Comments\CommentsConfig::isBroadcastingEnabled())
|
||||
wire:poll.{{ \Relaticle\Comments\CommentsConfig::getPollingInterval() }}
|
||||
@endif
|
||||
>
|
||||
{{-- Sort toggle --}}
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
{{-- New comment form - only for authorized users --}}
|
||||
@auth
|
||||
@can('create', \Relaticle\Comments\Config::getCommentModel())
|
||||
@can('create', \Relaticle\Comments\CommentsConfig::getCommentModel())
|
||||
<form wire:submit="addComment" class="relative mt-4"
|
||||
x-data="{
|
||||
showMentions: false,
|
||||
@@ -118,15 +118,17 @@
|
||||
});
|
||||
}
|
||||
}">
|
||||
<textarea
|
||||
x-ref="commentInput"
|
||||
wire:model="newComment"
|
||||
@input="handleInput($event)"
|
||||
@keydown="handleKeydown($event)"
|
||||
rows="3"
|
||||
placeholder="Write a comment..."
|
||||
class="block w-full rounded-lg border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-300 dark:placeholder-gray-400 sm:text-sm"
|
||||
></textarea>
|
||||
<x-filament::input.wrapper>
|
||||
<textarea
|
||||
x-ref="commentInput"
|
||||
wire:model="newComment"
|
||||
@input="handleInput($event)"
|
||||
@keydown="handleKeydown($event)"
|
||||
rows="3"
|
||||
placeholder="Write a comment..."
|
||||
class="block w-full border-none bg-transparent px-3 py-1.5 text-sm leading-6 text-gray-950 outline-none transition duration-75 placeholder:text-gray-400 focus:ring-0 dark:text-white dark:placeholder:text-gray-500"
|
||||
></textarea>
|
||||
</x-filament::input.wrapper>
|
||||
|
||||
{{-- Mention autocomplete dropdown --}}
|
||||
<div x-show="showMentions" x-cloak
|
||||
@@ -152,14 +154,14 @@
|
||||
<p class="mt-1 text-sm text-danger-600 dark:text-danger-400">{{ $message }}</p>
|
||||
@enderror
|
||||
|
||||
@if (\Relaticle\Comments\Config::areAttachmentsEnabled())
|
||||
@if (\Relaticle\Comments\CommentsConfig::areAttachmentsEnabled())
|
||||
<div class="mt-2">
|
||||
<label class="flex cursor-pointer items-center gap-2 text-xs text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
||||
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" />
|
||||
</svg>
|
||||
Attach files
|
||||
<input type="file" wire:model="attachments" multiple class="hidden" accept="{{ implode(',', \Relaticle\Comments\Config::getAttachmentAllowedTypes()) }}" />
|
||||
<input type="file" wire:model="attachments" multiple class="hidden" accept="{{ implode(',', \Relaticle\Comments\CommentsConfig::getAttachmentAllowedTypes()) }}" />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
{{-- Emoji picker dropdown --}}
|
||||
<div x-show="open" x-cloak @click.outside="open = false"
|
||||
class="absolute bottom-full left-0 z-50 mb-1 flex gap-1 rounded-lg border border-gray-200 bg-white p-2 shadow-lg dark:border-gray-600 dark:bg-gray-800">
|
||||
@foreach (\Relaticle\Comments\Config::getReactionEmojiSet() as $key => $emoji)
|
||||
@foreach (\Relaticle\Comments\CommentsConfig::getReactionEmojiSet() as $key => $emoji)
|
||||
<button wire:click="toggleReaction('{{ $key }}')" type="button"
|
||||
class="rounded p-1 text-base hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
title="{{ str_replace('_', ' ', $key) }}">
|
||||
|
||||
@@ -5,9 +5,10 @@ namespace Relaticle\Comments;
|
||||
use App\Models\User;
|
||||
use Closure;
|
||||
use Relaticle\Comments\Mentions\DefaultMentionResolver;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Policies\CommentPolicy;
|
||||
|
||||
class Config
|
||||
class CommentsConfig
|
||||
{
|
||||
protected static ?Closure $resolveAuthenticatedUser = null;
|
||||
|
||||
@@ -23,7 +24,25 @@ class Config
|
||||
|
||||
public static function getCommentTable(): string
|
||||
{
|
||||
return config('comments.tables.comments', 'comments');
|
||||
return static::getTableName('comments');
|
||||
}
|
||||
|
||||
public static function getTableName(string $table): string
|
||||
{
|
||||
$defaults = [
|
||||
'comments' => 'comments',
|
||||
'reactions' => 'comment_reactions',
|
||||
'mentions' => 'comment_mentions',
|
||||
'subscriptions' => 'comment_subscriptions',
|
||||
'attachments' => 'comment_attachments',
|
||||
];
|
||||
|
||||
return config("comments.table_names.{$table}", $defaults[$table] ?? $table);
|
||||
}
|
||||
|
||||
public static function getCommenterMorphName(): string
|
||||
{
|
||||
return config('comments.column_names.commenter_morph', 'commenter');
|
||||
}
|
||||
|
||||
public static function getMaxDepth(): int
|
||||
@@ -42,20 +42,20 @@ class CommentsServiceProvider extends PackageServiceProvider
|
||||
public function packageRegistered(): void
|
||||
{
|
||||
Relation::morphMap([
|
||||
'comment' => Config::getCommentModel(),
|
||||
'comment' => CommentsConfig::getCommentModel(),
|
||||
]);
|
||||
|
||||
$this->app->bind(
|
||||
MentionResolver::class,
|
||||
fn () => new (Config::getMentionResolver())
|
||||
fn () => new (CommentsConfig::getMentionResolver())
|
||||
);
|
||||
}
|
||||
|
||||
public function packageBooted(): void
|
||||
{
|
||||
Gate::policy(
|
||||
Config::getCommentModel(),
|
||||
Config::getPolicyClass(),
|
||||
CommentsConfig::getCommentModel(),
|
||||
CommentsConfig::getPolicyClass(),
|
||||
);
|
||||
|
||||
Event::listen(CommentCreated::class, SendCommentRepliedNotification::class);
|
||||
|
||||
@@ -5,9 +5,9 @@ namespace Relaticle\Comments\Concerns;
|
||||
use Filament\Models\Contracts\HasAvatar;
|
||||
use Filament\Models\Contracts\HasName;
|
||||
|
||||
trait IsCommenter
|
||||
trait CanComment
|
||||
{
|
||||
public function getCommentName(): string
|
||||
public function getCommentDisplayName(): string
|
||||
{
|
||||
if ($this instanceof HasName) {
|
||||
return $this->getFilamentName();
|
||||
@@ -3,13 +3,13 @@
|
||||
namespace Relaticle\Comments\Concerns;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\MorphMany;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
|
||||
trait HasComments
|
||||
{
|
||||
public function comments(): MorphMany
|
||||
{
|
||||
return $this->morphMany(Config::getCommentModel(), 'commentable');
|
||||
return $this->morphMany(CommentsConfig::getCommentModel(), 'commentable');
|
||||
}
|
||||
|
||||
public function topLevelComments(): MorphMany
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
namespace Relaticle\Comments\Contracts;
|
||||
|
||||
interface Commenter
|
||||
interface Commentator
|
||||
{
|
||||
public function getKey();
|
||||
|
||||
public function getMorphClass();
|
||||
|
||||
public function getCommentName(): string;
|
||||
public function getCommentDisplayName(): string;
|
||||
|
||||
public function getCommentAvatarUrl(): ?string;
|
||||
}
|
||||
@@ -8,8 +8,8 @@ use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
|
||||
class CommentCreated implements ShouldBroadcast
|
||||
{
|
||||
@@ -27,7 +27,7 @@ class CommentCreated implements ShouldBroadcast
|
||||
/** @return array<int, PrivateChannel> */
|
||||
public function broadcastOn(): array
|
||||
{
|
||||
$prefix = Config::getBroadcastChannelPrefix();
|
||||
$prefix = CommentsConfig::getBroadcastChannelPrefix();
|
||||
|
||||
return [
|
||||
new PrivateChannel("{$prefix}.{$this->comment->commentable_type}.{$this->comment->commentable_id}"),
|
||||
@@ -36,7 +36,7 @@ class CommentCreated implements ShouldBroadcast
|
||||
|
||||
public function broadcastWhen(): bool
|
||||
{
|
||||
return Config::isBroadcastingEnabled();
|
||||
return CommentsConfig::isBroadcastingEnabled();
|
||||
}
|
||||
|
||||
/** @return array{comment_id: int|string, commentable_type: string, commentable_id: int|string} */
|
||||
|
||||
@@ -8,8 +8,8 @@ use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
|
||||
class CommentDeleted implements ShouldBroadcast
|
||||
{
|
||||
@@ -27,7 +27,7 @@ class CommentDeleted implements ShouldBroadcast
|
||||
/** @return array<int, PrivateChannel> */
|
||||
public function broadcastOn(): array
|
||||
{
|
||||
$prefix = Config::getBroadcastChannelPrefix();
|
||||
$prefix = CommentsConfig::getBroadcastChannelPrefix();
|
||||
|
||||
return [
|
||||
new PrivateChannel("{$prefix}.{$this->comment->commentable_type}.{$this->comment->commentable_id}"),
|
||||
@@ -36,7 +36,7 @@ class CommentDeleted implements ShouldBroadcast
|
||||
|
||||
public function broadcastWhen(): bool
|
||||
{
|
||||
return Config::isBroadcastingEnabled();
|
||||
return CommentsConfig::isBroadcastingEnabled();
|
||||
}
|
||||
|
||||
/** @return array{comment_id: int|string, commentable_type: string, commentable_id: int|string} */
|
||||
|
||||
@@ -7,8 +7,8 @@ use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
|
||||
class CommentReacted implements ShouldBroadcast
|
||||
{
|
||||
@@ -26,7 +26,7 @@ class CommentReacted implements ShouldBroadcast
|
||||
/** @return array<int, PrivateChannel> */
|
||||
public function broadcastOn(): array
|
||||
{
|
||||
$prefix = Config::getBroadcastChannelPrefix();
|
||||
$prefix = CommentsConfig::getBroadcastChannelPrefix();
|
||||
|
||||
return [
|
||||
new PrivateChannel("{$prefix}.{$this->comment->commentable_type}.{$this->comment->commentable_id}"),
|
||||
@@ -35,7 +35,7 @@ class CommentReacted implements ShouldBroadcast
|
||||
|
||||
public function broadcastWhen(): bool
|
||||
{
|
||||
return Config::isBroadcastingEnabled();
|
||||
return CommentsConfig::isBroadcastingEnabled();
|
||||
}
|
||||
|
||||
/** @return array{comment_id: int|string, reaction: string, action: string} */
|
||||
|
||||
@@ -8,8 +8,8 @@ use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
|
||||
class CommentUpdated implements ShouldBroadcast
|
||||
{
|
||||
@@ -27,7 +27,7 @@ class CommentUpdated implements ShouldBroadcast
|
||||
/** @return array<int, PrivateChannel> */
|
||||
public function broadcastOn(): array
|
||||
{
|
||||
$prefix = Config::getBroadcastChannelPrefix();
|
||||
$prefix = CommentsConfig::getBroadcastChannelPrefix();
|
||||
|
||||
return [
|
||||
new PrivateChannel("{$prefix}.{$this->comment->commentable_type}.{$this->comment->commentable_id}"),
|
||||
@@ -36,7 +36,7 @@ class CommentUpdated implements ShouldBroadcast
|
||||
|
||||
public function broadcastWhen(): bool
|
||||
{
|
||||
return Config::isBroadcastingEnabled();
|
||||
return CommentsConfig::isBroadcastingEnabled();
|
||||
}
|
||||
|
||||
/** @return array{comment_id: int|string, commentable_type: string, commentable_id: int|string} */
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Relaticle\Comments\Events;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Events\Dispatchable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
|
||||
class UserMentioned
|
||||
{
|
||||
|
||||
@@ -3,35 +3,35 @@
|
||||
namespace Relaticle\Comments\Listeners;
|
||||
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Relaticle\Comments\CommentSubscription;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Events\CommentCreated;
|
||||
use Relaticle\Comments\Models\Subscription;
|
||||
use Relaticle\Comments\Notifications\CommentRepliedNotification;
|
||||
|
||||
class SendCommentRepliedNotification
|
||||
{
|
||||
public function handle(CommentCreated $event): void
|
||||
{
|
||||
if (! Config::areNotificationsEnabled()) {
|
||||
if (! CommentsConfig::areNotificationsEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$comment = $event->comment;
|
||||
$commentable = $event->commentable;
|
||||
|
||||
if (Config::shouldAutoSubscribe()) {
|
||||
CommentSubscription::subscribe($commentable, $comment->user);
|
||||
if (CommentsConfig::shouldAutoSubscribe()) {
|
||||
Subscription::subscribe($commentable, $comment->commenter);
|
||||
}
|
||||
|
||||
if (! $comment->isReply()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subscribers = CommentSubscription::subscribersFor($commentable);
|
||||
$subscribers = Subscription::subscribersFor($commentable);
|
||||
|
||||
$recipients = $subscribers->filter(function ($user) use ($comment) {
|
||||
return ! ($user->getMorphClass() === $comment->user->getMorphClass()
|
||||
&& $user->getKey() === $comment->user->getKey());
|
||||
return ! ($user->getMorphClass() === $comment->commenter->getMorphClass()
|
||||
&& $user->getKey() === $comment->commenter->getKey());
|
||||
});
|
||||
|
||||
if ($recipients->isEmpty()) {
|
||||
|
||||
@@ -2,33 +2,33 @@
|
||||
|
||||
namespace Relaticle\Comments\Listeners;
|
||||
|
||||
use Relaticle\Comments\CommentSubscription;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Events\UserMentioned;
|
||||
use Relaticle\Comments\Models\Subscription;
|
||||
use Relaticle\Comments\Notifications\UserMentionedNotification;
|
||||
|
||||
class SendUserMentionedNotification
|
||||
{
|
||||
public function handle(UserMentioned $event): void
|
||||
{
|
||||
if (! Config::areNotificationsEnabled()) {
|
||||
if (! CommentsConfig::areNotificationsEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$comment = $event->comment;
|
||||
$mentionedUser = $event->mentionedUser;
|
||||
|
||||
if (Config::shouldAutoSubscribe()) {
|
||||
CommentSubscription::subscribe($comment->commentable, $mentionedUser);
|
||||
if (CommentsConfig::shouldAutoSubscribe()) {
|
||||
Subscription::subscribe($comment->commentable, $mentionedUser);
|
||||
}
|
||||
|
||||
$isSelf = $mentionedUser->getMorphClass() === $comment->user->getMorphClass()
|
||||
&& $mentionedUser->getKey() === $comment->user->getKey();
|
||||
$isSelf = $mentionedUser->getMorphClass() === $comment->commenter->getMorphClass()
|
||||
&& $mentionedUser->getKey() === $comment->commenter->getKey();
|
||||
|
||||
if ($isSelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
$mentionedUser->notify(new UserMentionedNotification($comment, $comment->user));
|
||||
$mentionedUser->notify(new UserMentionedNotification($comment, $comment->commenter));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ use Illuminate\Contracts\View\View;
|
||||
use Livewire\Component;
|
||||
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
|
||||
use Livewire\WithFileUploads;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Contracts\MentionResolver;
|
||||
use Relaticle\Comments\Events\CommentCreated;
|
||||
use Relaticle\Comments\Events\CommentDeleted;
|
||||
use Relaticle\Comments\Events\CommentUpdated;
|
||||
use Relaticle\Comments\Mentions\MentionParser;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
|
||||
class CommentItem extends Component
|
||||
{
|
||||
@@ -106,25 +106,25 @@ class CommentItem extends Component
|
||||
|
||||
$rules = ['replyBody' => ['required', 'string', 'min:1']];
|
||||
|
||||
if (Config::areAttachmentsEnabled()) {
|
||||
$maxSize = Config::getAttachmentMaxSize();
|
||||
$allowedTypes = implode(',', Config::getAttachmentAllowedTypes());
|
||||
if (CommentsConfig::areAttachmentsEnabled()) {
|
||||
$maxSize = CommentsConfig::getAttachmentMaxSize();
|
||||
$allowedTypes = implode(',', CommentsConfig::getAttachmentAllowedTypes());
|
||||
$rules['replyAttachments.*'] = ['nullable', 'file', "max:{$maxSize}", "mimetypes:{$allowedTypes}"];
|
||||
}
|
||||
|
||||
$this->validate($rules);
|
||||
|
||||
$user = Config::resolveAuthenticatedUser();
|
||||
$user = CommentsConfig::resolveAuthenticatedUser();
|
||||
|
||||
$reply = $this->comment->commentable->comments()->create([
|
||||
'body' => $this->replyBody,
|
||||
'parent_id' => $this->comment->id,
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
if (Config::areAttachmentsEnabled() && ! empty($this->replyAttachments)) {
|
||||
$disk = Config::getAttachmentDisk();
|
||||
if (CommentsConfig::areAttachmentsEnabled() && ! empty($this->replyAttachments)) {
|
||||
$disk = CommentsConfig::getAttachmentDisk();
|
||||
|
||||
foreach ($this->replyAttachments as $file) {
|
||||
$path = $file->store("comments/attachments/{$reply->id}", $disk);
|
||||
@@ -169,7 +169,7 @@ class CommentItem extends Component
|
||||
return $resolver->search($query)
|
||||
->map(fn ($user) => [
|
||||
'id' => $user->getKey(),
|
||||
'name' => $user->getCommentName(),
|
||||
'name' => $user->getCommentDisplayName(),
|
||||
'avatar_url' => $user->getCommentAvatarUrl(),
|
||||
])
|
||||
->values()
|
||||
|
||||
@@ -9,12 +9,12 @@ use Livewire\Attributes\Computed;
|
||||
use Livewire\Component;
|
||||
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
|
||||
use Livewire\WithFileUploads;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\CommentSubscription;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Contracts\MentionResolver;
|
||||
use Relaticle\Comments\Events\CommentCreated;
|
||||
use Relaticle\Comments\Mentions\MentionParser;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Models\Subscription;
|
||||
|
||||
class Comments extends Component
|
||||
{
|
||||
@@ -36,7 +36,7 @@ class Comments extends Component
|
||||
public function mount(Model $model): void
|
||||
{
|
||||
$this->model = $model;
|
||||
$this->perPage = Config::getPerPage();
|
||||
$this->perPage = CommentsConfig::getPerPage();
|
||||
$this->loadedCount = $this->perPage;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ class Comments extends Component
|
||||
{
|
||||
return $this->model
|
||||
->topLevelComments()
|
||||
->with(['user', 'mentions', 'attachments', 'reactions.user', 'replies.user', 'replies.mentions', 'replies.attachments', 'replies.reactions.user'])
|
||||
->with(['commenter', 'mentions', 'attachments', 'reactions.commenter', 'replies.commenter', 'replies.mentions', 'replies.attachments', 'replies.reactions.commenter'])
|
||||
->orderBy('created_at', $this->sortDirection)
|
||||
->take($this->loadedCount)
|
||||
->get();
|
||||
@@ -67,27 +67,27 @@ class Comments extends Component
|
||||
#[Computed]
|
||||
public function isSubscribed(): bool
|
||||
{
|
||||
$user = Config::resolveAuthenticatedUser();
|
||||
$user = CommentsConfig::resolveAuthenticatedUser();
|
||||
|
||||
if (! $user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CommentSubscription::isSubscribed($this->model, $user);
|
||||
return Subscription::isSubscribed($this->model, $user);
|
||||
}
|
||||
|
||||
public function toggleSubscription(): void
|
||||
{
|
||||
$user = Config::resolveAuthenticatedUser();
|
||||
$user = CommentsConfig::resolveAuthenticatedUser();
|
||||
|
||||
if (! $user) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->isSubscribed) {
|
||||
CommentSubscription::unsubscribe($this->model, $user);
|
||||
Subscription::unsubscribe($this->model, $user);
|
||||
} else {
|
||||
CommentSubscription::subscribe($this->model, $user);
|
||||
Subscription::subscribe($this->model, $user);
|
||||
}
|
||||
|
||||
unset($this->isSubscribed);
|
||||
@@ -97,26 +97,26 @@ class Comments extends Component
|
||||
{
|
||||
$rules = ['newComment' => ['required', 'string', 'min:1']];
|
||||
|
||||
if (Config::areAttachmentsEnabled()) {
|
||||
$maxSize = Config::getAttachmentMaxSize();
|
||||
$allowedTypes = implode(',', Config::getAttachmentAllowedTypes());
|
||||
if (CommentsConfig::areAttachmentsEnabled()) {
|
||||
$maxSize = CommentsConfig::getAttachmentMaxSize();
|
||||
$allowedTypes = implode(',', CommentsConfig::getAttachmentAllowedTypes());
|
||||
$rules['attachments.*'] = ['nullable', 'file', "max:{$maxSize}", "mimetypes:{$allowedTypes}"];
|
||||
}
|
||||
|
||||
$this->validate($rules);
|
||||
|
||||
$this->authorize('create', Config::getCommentModel());
|
||||
$this->authorize('create', CommentsConfig::getCommentModel());
|
||||
|
||||
$user = Config::resolveAuthenticatedUser();
|
||||
$user = CommentsConfig::resolveAuthenticatedUser();
|
||||
|
||||
$comment = $this->model->comments()->create([
|
||||
'body' => $this->newComment,
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
if (Config::areAttachmentsEnabled() && ! empty($this->attachments)) {
|
||||
$disk = Config::getAttachmentDisk();
|
||||
if (CommentsConfig::areAttachmentsEnabled() && ! empty($this->attachments)) {
|
||||
$disk = CommentsConfig::getAttachmentDisk();
|
||||
|
||||
foreach ($this->attachments as $file) {
|
||||
$path = $file->store("comments/attachments/{$comment->id}", $disk);
|
||||
@@ -163,8 +163,8 @@ class Comments extends Component
|
||||
'commentUpdated' => 'refreshComments',
|
||||
];
|
||||
|
||||
if (Config::isBroadcastingEnabled()) {
|
||||
$prefix = Config::getBroadcastChannelPrefix();
|
||||
if (CommentsConfig::isBroadcastingEnabled()) {
|
||||
$prefix = CommentsConfig::getBroadcastChannelPrefix();
|
||||
$type = $this->model->getMorphClass();
|
||||
$id = $this->model->getKey();
|
||||
$channel = "echo-private:{$prefix}.{$type}.{$id}";
|
||||
@@ -195,7 +195,7 @@ class Comments extends Component
|
||||
return $resolver->search($query)
|
||||
->map(fn ($user) => [
|
||||
'id' => $user->getKey(),
|
||||
'name' => $user->getCommentName(),
|
||||
'name' => $user->getCommentDisplayName(),
|
||||
'avatar_url' => $user->getCommentAvatarUrl(),
|
||||
])
|
||||
->values()
|
||||
|
||||
@@ -5,9 +5,9 @@ namespace Relaticle\Comments\Livewire;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Component;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Events\CommentReacted;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
|
||||
class Reactions extends Component
|
||||
{
|
||||
@@ -22,19 +22,19 @@ class Reactions extends Component
|
||||
|
||||
public function toggleReaction(string $reaction): void
|
||||
{
|
||||
$user = Config::resolveAuthenticatedUser();
|
||||
$user = CommentsConfig::resolveAuthenticatedUser();
|
||||
|
||||
if (! $user) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (! in_array($reaction, Config::getAllowedReactions())) {
|
||||
if (! in_array($reaction, CommentsConfig::getAllowedReactions())) {
|
||||
return;
|
||||
}
|
||||
|
||||
$existing = $this->comment->reactions()
|
||||
->where('user_id', $user->getKey())
|
||||
->where('user_type', $user->getMorphClass())
|
||||
->where('commenter_id', $user->getKey())
|
||||
->where('commenter_type', $user->getMorphClass())
|
||||
->where('reaction', $reaction)
|
||||
->first();
|
||||
|
||||
@@ -44,8 +44,8 @@ class Reactions extends Component
|
||||
event(new CommentReacted($this->comment, $user, $reaction, 'removed'));
|
||||
} else {
|
||||
$this->comment->reactions()->create([
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'reaction' => $reaction,
|
||||
]);
|
||||
|
||||
@@ -66,13 +66,13 @@ class Reactions extends Component
|
||||
#[Computed]
|
||||
public function reactionSummary(): array
|
||||
{
|
||||
$user = Config::resolveAuthenticatedUser();
|
||||
$user = CommentsConfig::resolveAuthenticatedUser();
|
||||
$userId = $user?->getKey();
|
||||
$userType = $user?->getMorphClass();
|
||||
|
||||
$reactions = $this->comment->reactions()->with('user')->get();
|
||||
$reactions = $this->comment->reactions()->with('commenter')->get();
|
||||
|
||||
$emojiSet = Config::getReactionEmojiSet();
|
||||
$emojiSet = CommentsConfig::getReactionEmojiSet();
|
||||
|
||||
return $reactions
|
||||
->groupBy('reaction')
|
||||
@@ -81,10 +81,10 @@ class Reactions extends Component
|
||||
'reaction' => $key,
|
||||
'emoji' => $emojiSet[$key] ?? $key,
|
||||
'count' => $group->count(),
|
||||
'names' => $group->pluck('user.name')->filter()->take(3)->values()->all(),
|
||||
'names' => $group->pluck('commenter.name')->filter()->take(3)->values()->all(),
|
||||
'total_reactors' => $group->count(),
|
||||
'reacted_by_user' => $group->contains(
|
||||
fn ($r) => $r->user_id == $userId && $r->user_type === $userType
|
||||
fn ($r) => $r->commenter_id == $userId && $r->commenter_type === $userType
|
||||
),
|
||||
];
|
||||
})
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Relaticle\Comments\Mentions;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Contracts\MentionResolver;
|
||||
|
||||
class DefaultMentionResolver implements MentionResolver
|
||||
@@ -12,18 +12,18 @@ class DefaultMentionResolver implements MentionResolver
|
||||
/** @return Collection<int, Model> */
|
||||
public function search(string $query): Collection
|
||||
{
|
||||
$model = Config::getCommenterModel();
|
||||
$model = CommentsConfig::getCommenterModel();
|
||||
|
||||
return $model::query()
|
||||
->where('name', 'like', "{$query}%")
|
||||
->limit(Config::getMentionMaxResults())
|
||||
->limit(CommentsConfig::getMentionMaxResults())
|
||||
->get();
|
||||
}
|
||||
|
||||
/** @return Collection<int, Model> */
|
||||
public function resolveByNames(array $names): Collection
|
||||
{
|
||||
$model = Config::getCommenterModel();
|
||||
$model = CommentsConfig::getCommenterModel();
|
||||
|
||||
return $model::query()
|
||||
->whereIn('name', $names)
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
namespace Relaticle\Comments\Mentions;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Contracts\MentionResolver;
|
||||
use Relaticle\Comments\Events\UserMentioned;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
|
||||
class MentionParser
|
||||
{
|
||||
@@ -33,13 +33,13 @@ class MentionParser
|
||||
public function syncMentions(Comment $comment): void
|
||||
{
|
||||
$newMentionIds = $this->parse($comment->body);
|
||||
$existingMentionIds = $comment->mentions()->pluck('comment_mentions.user_id');
|
||||
$existingMentionIds = $comment->mentions()->pluck('comment_mentions.commenter_id');
|
||||
|
||||
$addedIds = $newMentionIds->diff($existingMentionIds);
|
||||
|
||||
$comment->mentions()->sync($newMentionIds->all());
|
||||
|
||||
$commenterModel = Config::getCommenterModel();
|
||||
$commenterModel = CommentsConfig::getCommenterModel();
|
||||
|
||||
$addedIds->each(function ($userId) use ($comment, $commenterModel) {
|
||||
$mentionedUser = $commenterModel::find($userId);
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Relaticle\Comments;
|
||||
namespace Relaticle\Comments\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Number;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
|
||||
class CommentAttachment extends Model
|
||||
class Attachment extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'comment_id',
|
||||
@@ -20,12 +21,12 @@ class CommentAttachment extends Model
|
||||
|
||||
public function getTable(): string
|
||||
{
|
||||
return 'comment_attachments';
|
||||
return CommentsConfig::getTableName('attachments');
|
||||
}
|
||||
|
||||
public function comment(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Config::getCommentModel());
|
||||
return $this->belongsTo(CommentsConfig::getCommentModel());
|
||||
}
|
||||
|
||||
public function isImage(): bool
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace Relaticle\Comments;
|
||||
namespace Relaticle\Comments\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Str;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Database\Factories\CommentFactory;
|
||||
|
||||
class Comment extends Model
|
||||
@@ -35,14 +36,14 @@ class Comment extends Model
|
||||
protected $fillable = [
|
||||
'body',
|
||||
'parent_id',
|
||||
'user_id',
|
||||
'user_type',
|
||||
'commenter_id',
|
||||
'commenter_type',
|
||||
'edited_at',
|
||||
];
|
||||
|
||||
public function getTable(): string
|
||||
{
|
||||
return Config::getCommentTable();
|
||||
return CommentsConfig::getCommentTable();
|
||||
}
|
||||
|
||||
/** @return array<string, string> */
|
||||
@@ -63,39 +64,39 @@ class Comment extends Model
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
public function user(): MorphTo
|
||||
public function commenter(): MorphTo
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
public function parent(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Config::getCommentModel(), 'parent_id');
|
||||
return $this->belongsTo(CommentsConfig::getCommentModel(), 'parent_id');
|
||||
}
|
||||
|
||||
public function replies(): HasMany
|
||||
{
|
||||
return $this->hasMany(Config::getCommentModel(), 'parent_id');
|
||||
return $this->hasMany(CommentsConfig::getCommentModel(), 'parent_id');
|
||||
}
|
||||
|
||||
public function reactions(): HasMany
|
||||
{
|
||||
return $this->hasMany(CommentReaction::class);
|
||||
return $this->hasMany(Reaction::class);
|
||||
}
|
||||
|
||||
public function attachments(): HasMany
|
||||
{
|
||||
return $this->hasMany(CommentAttachment::class);
|
||||
return $this->hasMany(Attachment::class);
|
||||
}
|
||||
|
||||
public function mentions(): MorphToMany
|
||||
{
|
||||
return $this->morphedByMany(
|
||||
Config::getCommenterModel(),
|
||||
'user',
|
||||
'comment_mentions',
|
||||
CommentsConfig::getCommenterModel(),
|
||||
'commenter',
|
||||
CommentsConfig::getTableName('mentions'),
|
||||
'comment_id',
|
||||
'user_id',
|
||||
'commenter_id',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -121,7 +122,7 @@ class Comment extends Model
|
||||
|
||||
public function canReply(): bool
|
||||
{
|
||||
return $this->depth() < Config::getMaxDepth();
|
||||
return $this->depth() < CommentsConfig::getMaxDepth();
|
||||
}
|
||||
|
||||
public function depth(): int
|
||||
@@ -133,8 +134,8 @@ class Comment extends Model
|
||||
$comment = $comment->parent;
|
||||
$depth++;
|
||||
|
||||
if ($depth >= Config::getMaxDepth()) {
|
||||
return Config::getMaxDepth();
|
||||
if ($depth >= CommentsConfig::getMaxDepth()) {
|
||||
return CommentsConfig::getMaxDepth();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace Relaticle\Comments;
|
||||
namespace Relaticle\Comments\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
|
||||
class CommentReaction extends Model
|
||||
class Reaction extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'comment_id',
|
||||
'user_id',
|
||||
'user_type',
|
||||
'commenter_id',
|
||||
'commenter_type',
|
||||
'reaction',
|
||||
];
|
||||
|
||||
public function getTable(): string
|
||||
{
|
||||
return 'comment_reactions';
|
||||
return CommentsConfig::getTableName('reactions');
|
||||
}
|
||||
|
||||
public function comment(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Config::getCommentModel());
|
||||
return $this->belongsTo(CommentsConfig::getCommentModel());
|
||||
}
|
||||
|
||||
public function user(): MorphTo
|
||||
public function commenter(): MorphTo
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
@@ -1,25 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace Relaticle\Comments;
|
||||
namespace Relaticle\Comments\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Support\Collection;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
|
||||
class CommentSubscription extends Model
|
||||
class Subscription extends Model
|
||||
{
|
||||
public const UPDATED_AT = null;
|
||||
|
||||
protected $fillable = [
|
||||
'commentable_type',
|
||||
'commentable_id',
|
||||
'user_type',
|
||||
'user_id',
|
||||
'commenter_type',
|
||||
'commenter_id',
|
||||
];
|
||||
|
||||
public function getTable(): string
|
||||
{
|
||||
return 'comment_subscriptions';
|
||||
return CommentsConfig::getTableName('subscriptions');
|
||||
}
|
||||
|
||||
public function commentable(): MorphTo
|
||||
@@ -27,7 +28,7 @@ class CommentSubscription extends Model
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
public function user(): MorphTo
|
||||
public function commenter(): MorphTo
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
@@ -37,8 +38,8 @@ class CommentSubscription extends Model
|
||||
return static::where([
|
||||
'commentable_type' => $commentable->getMorphClass(),
|
||||
'commentable_id' => $commentable->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
])->exists();
|
||||
}
|
||||
|
||||
@@ -47,8 +48,8 @@ class CommentSubscription extends Model
|
||||
static::firstOrCreate([
|
||||
'commentable_type' => $commentable->getMorphClass(),
|
||||
'commentable_id' => $commentable->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -57,8 +58,8 @@ class CommentSubscription extends Model
|
||||
static::where([
|
||||
'commentable_type' => $commentable->getMorphClass(),
|
||||
'commentable_id' => $commentable->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
])->delete();
|
||||
}
|
||||
|
||||
@@ -68,6 +69,6 @@ class CommentSubscription extends Model
|
||||
return static::where([
|
||||
'commentable_type' => $commentable->getMorphClass(),
|
||||
'commentable_id' => $commentable->getKey(),
|
||||
])->with('user')->get()->pluck('user')->filter()->values();
|
||||
])->with('commenter')->get()->pluck('commenter')->filter()->values();
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ namespace Relaticle\Comments\Notifications;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Support\Str;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
|
||||
class CommentRepliedNotification extends Notification
|
||||
{
|
||||
@@ -15,7 +15,7 @@ class CommentRepliedNotification extends Notification
|
||||
/** @return array<int, string> */
|
||||
public function via(mixed $notifiable): array
|
||||
{
|
||||
return Config::getNotificationChannels();
|
||||
return CommentsConfig::getNotificationChannels();
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
@@ -25,14 +25,14 @@ class CommentRepliedNotification extends Notification
|
||||
'comment_id' => $this->comment->id,
|
||||
'commentable_type' => $this->comment->commentable_type,
|
||||
'commentable_id' => $this->comment->commentable_id,
|
||||
'commenter_name' => $this->comment->user->getCommentName(),
|
||||
'commenter_name' => $this->comment->commenter->getCommentDisplayName(),
|
||||
'body' => Str::limit(strip_tags($this->comment->body), 100),
|
||||
];
|
||||
}
|
||||
|
||||
public function toMail(mixed $notifiable): MailMessage
|
||||
{
|
||||
$commenterName = $this->comment->user->getCommentName();
|
||||
$commenterName = $this->comment->commenter->getCommentDisplayName();
|
||||
|
||||
return (new MailMessage)
|
||||
->subject('New reply to your comment')
|
||||
|
||||
@@ -6,8 +6,8 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
use Illuminate\Support\Str;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
|
||||
class UserMentionedNotification extends Notification
|
||||
{
|
||||
@@ -19,7 +19,7 @@ class UserMentionedNotification extends Notification
|
||||
/** @return array<int, string> */
|
||||
public function via(mixed $notifiable): array
|
||||
{
|
||||
return Config::getNotificationChannels();
|
||||
return CommentsConfig::getNotificationChannels();
|
||||
}
|
||||
|
||||
/** @return array<string, mixed> */
|
||||
@@ -29,14 +29,14 @@ class UserMentionedNotification extends Notification
|
||||
'comment_id' => $this->comment->id,
|
||||
'commentable_type' => $this->comment->commentable_type,
|
||||
'commentable_id' => $this->comment->commentable_id,
|
||||
'mentioner_name' => $this->mentionedBy->getCommentName(),
|
||||
'mentioner_name' => $this->mentionedBy->getCommentDisplayName(),
|
||||
'body' => Str::limit(strip_tags($this->comment->body), 100),
|
||||
];
|
||||
}
|
||||
|
||||
public function toMail(mixed $notifiable): MailMessage
|
||||
{
|
||||
$mentionerName = $this->mentionedBy->getCommentName();
|
||||
$mentionerName = $this->mentionedBy->getCommentDisplayName();
|
||||
|
||||
return (new MailMessage)
|
||||
->subject('You were mentioned in a comment')
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
namespace Relaticle\Comments\Policies;
|
||||
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
|
||||
class CommentPolicy
|
||||
{
|
||||
@@ -19,14 +19,14 @@ class CommentPolicy
|
||||
|
||||
public function update(Authenticatable $user, Comment $comment): bool
|
||||
{
|
||||
return $user->getKey() === $comment->user_id
|
||||
&& $user->getMorphClass() === $comment->user_type;
|
||||
return $user->getKey() === $comment->commenter_id
|
||||
&& $user->getMorphClass() === $comment->commenter_type;
|
||||
}
|
||||
|
||||
public function delete(Authenticatable $user, Comment $comment): bool
|
||||
{
|
||||
return $user->getKey() === $comment->user_id
|
||||
&& $user->getMorphClass() === $comment->user_type;
|
||||
return $user->getKey() === $comment->commenter_id
|
||||
&& $user->getMorphClass() === $comment->commenter_type;
|
||||
}
|
||||
|
||||
public function reply(Authenticatable $user, Comment $comment): bool
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Livewire\Livewire;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\CommentAttachment;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Livewire\CommentItem;
|
||||
use Relaticle\Comments\Livewire\Comments;
|
||||
use Relaticle\Comments\Models\Attachment;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -29,7 +29,7 @@ it('creates comment with file attachment via Livewire component', function () {
|
||||
->assertSet('attachments', []);
|
||||
|
||||
expect(Comment::count())->toBe(1);
|
||||
expect(CommentAttachment::count())->toBe(1);
|
||||
expect(Attachment::count())->toBe(1);
|
||||
});
|
||||
|
||||
it('stores attachment with correct metadata', function () {
|
||||
@@ -47,7 +47,7 @@ it('stores attachment with correct metadata', function () {
|
||||
->set('attachments', [$file])
|
||||
->call('addComment');
|
||||
|
||||
$attachment = CommentAttachment::first();
|
||||
$attachment = Attachment::first();
|
||||
$comment = Comment::first();
|
||||
|
||||
expect($attachment->original_name)->toBe('vacation.jpg')
|
||||
@@ -73,7 +73,7 @@ it('stores file on configured disk at comments/attachments/{comment_id}/ path',
|
||||
->set('attachments', [$file])
|
||||
->call('addComment');
|
||||
|
||||
$attachment = CommentAttachment::first();
|
||||
$attachment = Attachment::first();
|
||||
|
||||
Storage::disk('public')->assertExists($attachment->file_path);
|
||||
expect($attachment->file_path)->toContain("comments/attachments/{$attachment->comment_id}/");
|
||||
@@ -88,15 +88,15 @@ it('displays image attachment thumbnail in comment item view', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Image comment</p>',
|
||||
]);
|
||||
|
||||
$file = UploadedFile::fake()->image('photo.jpg', 100, 100);
|
||||
$path = $file->store("comments/attachments/{$comment->id}", 'public');
|
||||
|
||||
CommentAttachment::create([
|
||||
Attachment::create([
|
||||
'comment_id' => $comment->id,
|
||||
'file_path' => $path,
|
||||
'original_name' => 'photo.jpg',
|
||||
@@ -123,15 +123,15 @@ it('displays non-image attachment as download link', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>PDF comment</p>',
|
||||
]);
|
||||
|
||||
$file = UploadedFile::fake()->create('document.pdf', 2048, 'application/pdf');
|
||||
$path = $file->store("comments/attachments/{$comment->id}", 'public');
|
||||
|
||||
CommentAttachment::create([
|
||||
Attachment::create([
|
||||
'comment_id' => $comment->id,
|
||||
'file_path' => $path,
|
||||
'original_name' => 'document.pdf',
|
||||
@@ -157,7 +157,7 @@ it('rejects file exceeding max size', function () {
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
$oversizedFile = UploadedFile::fake()->create('big.pdf', Config::getAttachmentMaxSize() + 1, 'application/pdf');
|
||||
$oversizedFile = UploadedFile::fake()->create('big.pdf', CommentsConfig::getAttachmentMaxSize() + 1, 'application/pdf');
|
||||
|
||||
Livewire::test(Comments::class, ['model' => $post])
|
||||
->set('newComment', '<p>Oversized file</p>')
|
||||
@@ -166,7 +166,7 @@ it('rejects file exceeding max size', function () {
|
||||
->assertHasErrors('attachments.0');
|
||||
|
||||
expect(Comment::count())->toBe(0);
|
||||
expect(CommentAttachment::count())->toBe(0);
|
||||
expect(Attachment::count())->toBe(0);
|
||||
});
|
||||
|
||||
it('rejects disallowed file type', function () {
|
||||
@@ -186,7 +186,7 @@ it('rejects disallowed file type', function () {
|
||||
->assertHasErrors('attachments.0');
|
||||
|
||||
expect(Comment::count())->toBe(0);
|
||||
expect(CommentAttachment::count())->toBe(0);
|
||||
expect(Attachment::count())->toBe(0);
|
||||
});
|
||||
|
||||
it('accepts allowed file types', function () {
|
||||
@@ -206,7 +206,7 @@ it('accepts allowed file types', function () {
|
||||
->assertHasNoErrors('attachments.0');
|
||||
|
||||
expect(Comment::count())->toBe(1);
|
||||
expect(CommentAttachment::count())->toBe(1);
|
||||
expect(Attachment::count())->toBe(1);
|
||||
});
|
||||
|
||||
it('hides upload UI when attachments disabled', function () {
|
||||
@@ -248,9 +248,9 @@ it('creates comment with multiple file attachments', function () {
|
||||
->call('addComment');
|
||||
|
||||
expect(Comment::count())->toBe(1);
|
||||
expect(CommentAttachment::count())->toBe(2);
|
||||
expect(Attachment::count())->toBe(2);
|
||||
|
||||
$attachments = CommentAttachment::all();
|
||||
$attachments = Attachment::all();
|
||||
expect($attachments->pluck('original_name')->toArray())
|
||||
->toContain('photo1.jpg')
|
||||
->toContain('notes.pdf');
|
||||
@@ -265,8 +265,8 @@ it('creates reply with file attachment via CommentItem component', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Parent comment</p>',
|
||||
]);
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
use Illuminate\Broadcasting\PrivateChannel;
|
||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Events\CommentCreated;
|
||||
use Relaticle\Comments\Events\CommentDeleted;
|
||||
use Relaticle\Comments\Events\CommentReacted;
|
||||
use Relaticle\Comments\Events\CommentUpdated;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -17,8 +17,8 @@ it('CommentCreated event implements ShouldBroadcast', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$event = new CommentCreated($comment);
|
||||
@@ -33,8 +33,8 @@ it('CommentUpdated event implements ShouldBroadcast', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$event = new CommentUpdated($comment);
|
||||
@@ -49,8 +49,8 @@ it('CommentDeleted event implements ShouldBroadcast', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$event = new CommentDeleted($comment);
|
||||
@@ -65,8 +65,8 @@ it('CommentReacted event implements ShouldBroadcast', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$event = new CommentReacted($comment, $user, 'thumbs_up', 'added');
|
||||
@@ -81,8 +81,8 @@ it('broadcastOn returns PrivateChannel with correct channel name', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$event = new CommentCreated($comment);
|
||||
@@ -100,8 +100,8 @@ it('broadcastWhen returns false when broadcasting is disabled', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$event = new CommentCreated($comment);
|
||||
@@ -118,8 +118,8 @@ it('broadcastWhen returns true when broadcasting is enabled', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$event = new CommentCreated($comment);
|
||||
@@ -134,8 +134,8 @@ it('broadcastWith returns array with comment_id for CommentCreated', function ()
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$event = new CommentCreated($comment);
|
||||
@@ -154,8 +154,8 @@ it('broadcastWith returns array with comment_id, reaction, and action for Commen
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$event = new CommentReacted($comment, $user, 'thumbs_up', 'added');
|
||||
@@ -176,8 +176,8 @@ it('uses custom channel prefix from config in broadcastOn', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$event = new CommentCreated($comment);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\CommentAttachment;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Models\Attachment;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -13,12 +13,12 @@ it('creates a comment attachment with all metadata fields', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Test comment</p>',
|
||||
]);
|
||||
|
||||
$attachment = CommentAttachment::create([
|
||||
$attachment = Attachment::create([
|
||||
'comment_id' => $comment->id,
|
||||
'file_path' => 'comments/attachments/1/photo.jpg',
|
||||
'original_name' => 'photo.jpg',
|
||||
@@ -27,7 +27,7 @@ it('creates a comment attachment with all metadata fields', function () {
|
||||
'disk' => 'public',
|
||||
]);
|
||||
|
||||
expect($attachment)->toBeInstanceOf(CommentAttachment::class)
|
||||
expect($attachment)->toBeInstanceOf(Attachment::class)
|
||||
->and($attachment->file_path)->toBe('comments/attachments/1/photo.jpg')
|
||||
->and($attachment->original_name)->toBe('photo.jpg')
|
||||
->and($attachment->mime_type)->toBe('image/jpeg')
|
||||
@@ -42,12 +42,12 @@ it('belongs to a comment via comment() relationship', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Test</p>',
|
||||
]);
|
||||
|
||||
$attachment = CommentAttachment::create([
|
||||
$attachment = Attachment::create([
|
||||
'comment_id' => $comment->id,
|
||||
'file_path' => 'comments/attachments/1/test.png',
|
||||
'original_name' => 'test.png',
|
||||
@@ -67,12 +67,12 @@ it('has attachments() hasMany relationship on Comment', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Test</p>',
|
||||
]);
|
||||
|
||||
CommentAttachment::create([
|
||||
Attachment::create([
|
||||
'comment_id' => $comment->id,
|
||||
'file_path' => 'comments/attachments/1/file1.png',
|
||||
'original_name' => 'file1.png',
|
||||
@@ -81,7 +81,7 @@ it('has attachments() hasMany relationship on Comment', function () {
|
||||
'disk' => 'public',
|
||||
]);
|
||||
|
||||
CommentAttachment::create([
|
||||
Attachment::create([
|
||||
'comment_id' => $comment->id,
|
||||
'file_path' => 'comments/attachments/1/file2.pdf',
|
||||
'original_name' => 'file2.pdf',
|
||||
@@ -91,7 +91,7 @@ it('has attachments() hasMany relationship on Comment', function () {
|
||||
]);
|
||||
|
||||
expect($comment->attachments)->toHaveCount(2)
|
||||
->and($comment->attachments->first())->toBeInstanceOf(CommentAttachment::class);
|
||||
->and($comment->attachments->first())->toBeInstanceOf(Attachment::class);
|
||||
});
|
||||
|
||||
it('cascade deletes attachments when comment is force deleted', function () {
|
||||
@@ -101,12 +101,12 @@ it('cascade deletes attachments when comment is force deleted', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Test</p>',
|
||||
]);
|
||||
|
||||
CommentAttachment::create([
|
||||
Attachment::create([
|
||||
'comment_id' => $comment->id,
|
||||
'file_path' => 'comments/attachments/1/photo.jpg',
|
||||
'original_name' => 'photo.jpg',
|
||||
@@ -115,15 +115,15 @@ it('cascade deletes attachments when comment is force deleted', function () {
|
||||
'disk' => 'public',
|
||||
]);
|
||||
|
||||
expect(CommentAttachment::where('comment_id', $comment->id)->count())->toBe(1);
|
||||
expect(Attachment::where('comment_id', $comment->id)->count())->toBe(1);
|
||||
|
||||
$comment->forceDelete();
|
||||
|
||||
expect(CommentAttachment::where('comment_id', $comment->id)->count())->toBe(0);
|
||||
expect(Attachment::where('comment_id', $comment->id)->count())->toBe(0);
|
||||
});
|
||||
|
||||
it('correctly identifies image and non-image mime types via isImage()', function (string $mimeType, bool $expected) {
|
||||
$attachment = new CommentAttachment(['mime_type' => $mimeType]);
|
||||
$attachment = new Attachment(['mime_type' => $mimeType]);
|
||||
|
||||
expect($attachment->isImage())->toBe($expected);
|
||||
})->with([
|
||||
@@ -142,12 +142,12 @@ it('formats bytes into human-readable size via formattedSize()', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Test</p>',
|
||||
]);
|
||||
|
||||
$attachment = CommentAttachment::create([
|
||||
$attachment = Attachment::create([
|
||||
'comment_id' => $comment->id,
|
||||
'file_path' => 'comments/attachments/1/file.pdf',
|
||||
'original_name' => 'file.pdf',
|
||||
@@ -160,15 +160,15 @@ it('formats bytes into human-readable size via formattedSize()', function () {
|
||||
});
|
||||
|
||||
it('returns default attachment disk as public', function () {
|
||||
expect(Config::getAttachmentDisk())->toBe('public');
|
||||
expect(CommentsConfig::getAttachmentDisk())->toBe('public');
|
||||
});
|
||||
|
||||
it('returns default attachment max size as 10240', function () {
|
||||
expect(Config::getAttachmentMaxSize())->toBe(10240);
|
||||
expect(CommentsConfig::getAttachmentMaxSize())->toBe(10240);
|
||||
});
|
||||
|
||||
it('returns default allowed attachment types', function () {
|
||||
$allowedTypes = Config::getAttachmentAllowedTypes();
|
||||
$allowedTypes = CommentsConfig::getAttachmentAllowedTypes();
|
||||
|
||||
expect($allowedTypes)->toBeArray()
|
||||
->toContain('image/jpeg')
|
||||
@@ -181,17 +181,17 @@ it('respects custom config overrides for attachment settings', function () {
|
||||
config(['comments.attachments.max_size' => 5120]);
|
||||
config(['comments.attachments.allowed_types' => ['image/png']]);
|
||||
|
||||
expect(Config::getAttachmentDisk())->toBe('s3')
|
||||
->and(Config::getAttachmentMaxSize())->toBe(5120)
|
||||
->and(Config::getAttachmentAllowedTypes())->toBe(['image/png']);
|
||||
expect(CommentsConfig::getAttachmentDisk())->toBe('s3')
|
||||
->and(CommentsConfig::getAttachmentMaxSize())->toBe(5120)
|
||||
->and(CommentsConfig::getAttachmentAllowedTypes())->toBe(['image/png']);
|
||||
});
|
||||
|
||||
it('reports attachments as enabled by default', function () {
|
||||
expect(Config::areAttachmentsEnabled())->toBeTrue();
|
||||
expect(CommentsConfig::areAttachmentsEnabled())->toBeTrue();
|
||||
});
|
||||
|
||||
it('respects disabled attachments config', function () {
|
||||
config(['comments.attachments.enabled' => false]);
|
||||
|
||||
expect(Config::areAttachmentsEnabled())->toBeFalse();
|
||||
expect(CommentsConfig::areAttachmentsEnabled())->toBeFalse();
|
||||
});
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Livewire\Livewire;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Events\CommentCreated;
|
||||
use Relaticle\Comments\Events\CommentDeleted;
|
||||
use Relaticle\Comments\Events\CommentUpdated;
|
||||
use Relaticle\Comments\Livewire\CommentItem;
|
||||
use Relaticle\Comments\Livewire\Comments;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -38,8 +38,8 @@ it('fires CommentUpdated event when editing a comment', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Original</p>',
|
||||
]);
|
||||
|
||||
@@ -64,8 +64,8 @@ it('fires CommentDeleted event when deleting a comment', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
@@ -87,8 +87,8 @@ it('fires CommentCreated event when adding a reply', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
@@ -119,6 +119,6 @@ it('carries correct comment and commentable in event payload', function () {
|
||||
Event::assertDispatched(CommentCreated::class, function (CommentCreated $event) use ($post, $user) {
|
||||
return $event->comment instanceof Comment
|
||||
&& $event->commentable->id === $post->id
|
||||
&& $event->comment->user_id === $user->id;
|
||||
&& $event->comment->commenter_id === $user->id;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Livewire\Livewire;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Livewire\CommentItem;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -13,8 +13,8 @@ it('allows author to start and save edit on their comment', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Original body</p>',
|
||||
]);
|
||||
|
||||
@@ -42,8 +42,8 @@ it('marks edited comment with edited indicator', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Original</p>',
|
||||
]);
|
||||
|
||||
@@ -70,8 +70,8 @@ it('prevents non-author from editing a comment', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
'body' => '<p>Author comment</p>',
|
||||
]);
|
||||
|
||||
@@ -89,8 +89,8 @@ it('allows author to delete their own comment', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
@@ -108,8 +108,8 @@ it('preserves replies when parent comment is deleted', function () {
|
||||
$attrs = [
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
];
|
||||
|
||||
$parent = Comment::factory()->create($attrs);
|
||||
@@ -133,8 +133,8 @@ it('prevents non-author from deleting a comment', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
]);
|
||||
|
||||
$this->actingAs($otherUser);
|
||||
@@ -151,8 +151,8 @@ it('allows user to reply to a comment', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
@@ -169,7 +169,7 @@ it('allows user to reply to a comment', function () {
|
||||
|
||||
expect($reply)->not->toBeNull();
|
||||
expect($reply->body)->toBe('<p>My reply</p>');
|
||||
expect($reply->user_id)->toBe($user->id);
|
||||
expect($reply->commenter_id)->toBe($user->id);
|
||||
expect($reply->commentable_id)->toBe($post->id);
|
||||
});
|
||||
|
||||
@@ -179,8 +179,8 @@ it('respects max depth for replies', function () {
|
||||
$attrs = [
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
];
|
||||
|
||||
config(['comments.threading.max_depth' => 1]);
|
||||
@@ -202,8 +202,8 @@ it('resets state when cancelling edit', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Some body</p>',
|
||||
]);
|
||||
|
||||
@@ -224,8 +224,8 @@ it('resets state when cancelling reply', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
@@ -245,14 +245,14 @@ it('loads all replies within a thread eagerly', function () {
|
||||
$attrs = [
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
];
|
||||
|
||||
$parent = Comment::factory()->create($attrs);
|
||||
Comment::factory()->count(3)->withParent($parent)->create($attrs);
|
||||
|
||||
$parentWithReplies = Comment::with('replies.user')->find($parent->id);
|
||||
$parentWithReplies = Comment::with('replies.commenter')->find($parent->id);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\QueryException;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\CommentReaction;
|
||||
use Relaticle\Comments\Events\CommentReacted;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Models\Reaction;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -14,15 +14,15 @@ it('belongs to a comment via comment() relationship', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Test</p>',
|
||||
]);
|
||||
|
||||
$reaction = CommentReaction::create([
|
||||
$reaction = Reaction::create([
|
||||
'comment_id' => $comment->id,
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'reaction' => 'thumbs_up',
|
||||
]);
|
||||
|
||||
@@ -30,27 +30,27 @@ it('belongs to a comment via comment() relationship', function () {
|
||||
->and($reaction->comment->id)->toBe($comment->id);
|
||||
});
|
||||
|
||||
it('belongs to a user via polymorphic user() relationship', function () {
|
||||
it('belongs to a commenter via polymorphic commenter() relationship', function () {
|
||||
$user = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Test</p>',
|
||||
]);
|
||||
|
||||
$reaction = CommentReaction::create([
|
||||
$reaction = Reaction::create([
|
||||
'comment_id' => $comment->id,
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'reaction' => 'heart',
|
||||
]);
|
||||
|
||||
expect($reaction->user)->toBeInstanceOf(User::class)
|
||||
->and($reaction->user->id)->toBe($user->id);
|
||||
expect($reaction->commenter)->toBeInstanceOf(User::class)
|
||||
->and($reaction->commenter->id)->toBe($user->id);
|
||||
});
|
||||
|
||||
it('prevents duplicate reactions with unique constraint', function () {
|
||||
@@ -60,22 +60,22 @@ it('prevents duplicate reactions with unique constraint', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Test</p>',
|
||||
]);
|
||||
|
||||
CommentReaction::create([
|
||||
Reaction::create([
|
||||
'comment_id' => $comment->id,
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'reaction' => 'thumbs_up',
|
||||
]);
|
||||
|
||||
expect(fn () => CommentReaction::create([
|
||||
expect(fn () => Reaction::create([
|
||||
'comment_id' => $comment->id,
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'reaction' => 'thumbs_up',
|
||||
]))->toThrow(QueryException::class);
|
||||
});
|
||||
@@ -87,8 +87,8 @@ it('carries comment, user, reaction key, and action in CommentReacted event', fu
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Test</p>',
|
||||
]);
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\CommentSubscription;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Models\Subscription;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -10,98 +9,98 @@ it('has commentable morphTo relationship', function () {
|
||||
$user = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
$subscription = CommentSubscription::create([
|
||||
$subscription = Subscription::create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
expect($subscription->commentable)->toBeInstanceOf(Post::class)
|
||||
->and($subscription->commentable->id)->toBe($post->id);
|
||||
});
|
||||
|
||||
it('has user morphTo relationship', function () {
|
||||
it('has commenter morphTo relationship', function () {
|
||||
$user = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
$subscription = CommentSubscription::create([
|
||||
$subscription = Subscription::create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
expect($subscription->user)->toBeInstanceOf(User::class)
|
||||
->and($subscription->user->id)->toBe($user->id);
|
||||
expect($subscription->commenter)->toBeInstanceOf(User::class)
|
||||
->and($subscription->commenter->id)->toBe($user->id);
|
||||
});
|
||||
|
||||
it('returns database as default notification channel', function () {
|
||||
expect(Config::getNotificationChannels())->toBe(['database']);
|
||||
expect(CommentsConfig::getNotificationChannels())->toBe(['database']);
|
||||
});
|
||||
|
||||
it('returns custom channels when configured', function () {
|
||||
config()->set('comments.notifications.channels', ['database', 'mail']);
|
||||
|
||||
expect(Config::getNotificationChannels())->toBe(['database', 'mail']);
|
||||
expect(CommentsConfig::getNotificationChannels())->toBe(['database', 'mail']);
|
||||
});
|
||||
|
||||
it('returns true for shouldAutoSubscribe by default', function () {
|
||||
expect(Config::shouldAutoSubscribe())->toBeTrue();
|
||||
expect(CommentsConfig::shouldAutoSubscribe())->toBeTrue();
|
||||
});
|
||||
|
||||
it('returns false for shouldAutoSubscribe when configured', function () {
|
||||
config()->set('comments.subscriptions.auto_subscribe', false);
|
||||
|
||||
expect(Config::shouldAutoSubscribe())->toBeFalse();
|
||||
expect(CommentsConfig::shouldAutoSubscribe())->toBeFalse();
|
||||
});
|
||||
|
||||
it('checks if user is subscribed to a commentable via isSubscribed()', function () {
|
||||
$user = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
expect(CommentSubscription::isSubscribed($post, $user))->toBeFalse();
|
||||
expect(Subscription::isSubscribed($post, $user))->toBeFalse();
|
||||
|
||||
CommentSubscription::create([
|
||||
Subscription::create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
expect(CommentSubscription::isSubscribed($post, $user))->toBeTrue();
|
||||
expect(Subscription::isSubscribed($post, $user))->toBeTrue();
|
||||
});
|
||||
|
||||
it('creates subscription via subscribe() static method', function () {
|
||||
$user = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $user);
|
||||
Subscription::subscribe($post, $user);
|
||||
|
||||
expect(CommentSubscription::isSubscribed($post, $user))->toBeTrue();
|
||||
expect(Subscription::isSubscribed($post, $user))->toBeTrue();
|
||||
});
|
||||
|
||||
it('removes subscription via unsubscribe() static method', function () {
|
||||
$user = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $user);
|
||||
CommentSubscription::unsubscribe($post, $user);
|
||||
Subscription::subscribe($post, $user);
|
||||
Subscription::unsubscribe($post, $user);
|
||||
|
||||
expect(CommentSubscription::isSubscribed($post, $user))->toBeFalse();
|
||||
expect(Subscription::isSubscribed($post, $user))->toBeFalse();
|
||||
});
|
||||
|
||||
it('is idempotent when subscribing twice', function () {
|
||||
$user = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $user);
|
||||
CommentSubscription::subscribe($post, $user);
|
||||
Subscription::subscribe($post, $user);
|
||||
Subscription::subscribe($post, $user);
|
||||
|
||||
expect(CommentSubscription::where([
|
||||
expect(Subscription::where([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
])->count())->toBe(1);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -12,14 +12,14 @@ it('can be created with factory', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
]);
|
||||
|
||||
expect($comment)->toBeInstanceOf(Comment::class);
|
||||
expect($comment->body)->toBeString();
|
||||
expect($comment->commentable_id)->toBe($post->id);
|
||||
expect($comment->user_id)->toBe($user->id);
|
||||
expect($comment->commenter_id)->toBe($user->id);
|
||||
});
|
||||
|
||||
it('belongs to a commentable model via morphTo', function () {
|
||||
@@ -29,27 +29,27 @@ it('belongs to a commentable model via morphTo', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
]);
|
||||
|
||||
expect($comment->commentable)->toBeInstanceOf(Post::class);
|
||||
expect($comment->commentable->id)->toBe($post->id);
|
||||
});
|
||||
|
||||
it('belongs to a user via morphTo', function () {
|
||||
it('belongs to a commenter via morphTo', function () {
|
||||
$user = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
]);
|
||||
|
||||
expect($comment->user)->toBeInstanceOf(User::class);
|
||||
expect($comment->user->id)->toBe($user->id);
|
||||
expect($comment->commenter)->toBeInstanceOf(User::class);
|
||||
expect($comment->commenter->id)->toBe($user->id);
|
||||
});
|
||||
|
||||
it('supports threading with parent and replies', function () {
|
||||
@@ -59,15 +59,15 @@ it('supports threading with parent and replies', function () {
|
||||
$parent = Comment::factory()->create([
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
]);
|
||||
|
||||
$reply = Comment::factory()->withParent($parent)->create([
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
]);
|
||||
|
||||
expect($reply->parent->id)->toBe($parent->id);
|
||||
@@ -82,15 +82,15 @@ it('identifies top-level vs reply comments', function () {
|
||||
$topLevel = Comment::factory()->create([
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
]);
|
||||
|
||||
$reply = Comment::factory()->withParent($topLevel)->create([
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
]);
|
||||
|
||||
expect($topLevel->isTopLevel())->toBeTrue();
|
||||
@@ -105,8 +105,8 @@ it('calculates depth correctly', function () {
|
||||
$attrs = [
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
];
|
||||
|
||||
$level0 = Comment::factory()->create($attrs);
|
||||
@@ -124,8 +124,8 @@ it('checks canReply based on max depth', function () {
|
||||
$attrs = [
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
];
|
||||
|
||||
$level0 = Comment::factory()->create($attrs);
|
||||
@@ -144,8 +144,8 @@ it('supports soft deletes', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
]);
|
||||
|
||||
$comment->delete();
|
||||
@@ -162,8 +162,8 @@ it('tracks edited state', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
]);
|
||||
|
||||
expect($comment->isEdited())->toBeFalse();
|
||||
@@ -171,8 +171,8 @@ it('tracks edited state', function () {
|
||||
$edited = Comment::factory()->edited()->create([
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
]);
|
||||
|
||||
expect($edited->isEdited())->toBeTrue();
|
||||
@@ -185,8 +185,8 @@ it('detects when it has replies', function () {
|
||||
$attrs = [
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
];
|
||||
|
||||
$parent = Comment::factory()->create($attrs);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Filament\Actions\CommentsAction;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -42,8 +42,8 @@ it('shows badge with comment count when comments exist', function () {
|
||||
Comment::factory()->count(3)->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$action = CommentsAction::make('comments');
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Livewire\Livewire;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Livewire\Comments;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -33,8 +33,8 @@ it('associates new comment with the authenticated user', function () {
|
||||
|
||||
$comment = Comment::first();
|
||||
|
||||
expect($comment->user_id)->toBe($user->id);
|
||||
expect($comment->user_type)->toBe($user->getMorphClass());
|
||||
expect($comment->commenter_id)->toBe($user->id);
|
||||
expect($comment->commenter_type)->toBe($user->getMorphClass());
|
||||
expect($comment->commentable_id)->toBe($post->id);
|
||||
expect($comment->commentable_type)->toBe($post->getMorphClass());
|
||||
});
|
||||
@@ -71,8 +71,8 @@ it('paginates top-level comments with load more', function () {
|
||||
Comment::factory()->count(12)->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
@@ -99,8 +99,8 @@ it('hides load more button when all comments are loaded', function () {
|
||||
Comment::factory()->count(5)->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
@@ -130,8 +130,8 @@ it('returns comments in correct sort order via computed property', function () {
|
||||
$older = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Older comment</p>',
|
||||
'created_at' => now()->subHour(),
|
||||
]);
|
||||
@@ -139,8 +139,8 @@ it('returns comments in correct sort order via computed property', function () {
|
||||
$newer = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Newer comment</p>',
|
||||
'created_at' => now(),
|
||||
]);
|
||||
@@ -167,8 +167,8 @@ it('displays total comment count', function () {
|
||||
Comment::factory()->count(3)->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Filament\Actions\CommentsTableAction;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -30,8 +30,8 @@ it('shows badge with comment count for the record', function () {
|
||||
Comment::factory()->count(5)->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$action = CommentsTableAction::make('comments');
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Livewire\Livewire;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Livewire\Comments;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -13,8 +13,8 @@ it('strips script tags from comment body on create', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Hello</p><script>alert(1)</script>',
|
||||
]);
|
||||
|
||||
@@ -29,8 +29,8 @@ it('strips event handler attributes from comment body', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<img onerror="alert(1)" src="x">',
|
||||
]);
|
||||
|
||||
@@ -45,8 +45,8 @@ it('strips style tags from comment body', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Hi</p><style>body{display:none}</style>',
|
||||
]);
|
||||
|
||||
@@ -61,8 +61,8 @@ it('strips iframe tags from comment body', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Hi</p><iframe src="evil.com"></iframe>',
|
||||
]);
|
||||
|
||||
@@ -85,8 +85,8 @@ it('preserves safe HTML formatting through sanitization', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => $safeHtml,
|
||||
]);
|
||||
|
||||
@@ -108,8 +108,8 @@ it('sanitizes comment body on update', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Clean content</p>',
|
||||
]);
|
||||
|
||||
@@ -130,8 +130,8 @@ it('strips javascript protocol from link href', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<a href="javascript:alert(1)">click me</a>',
|
||||
]);
|
||||
|
||||
@@ -146,8 +146,8 @@ it('strips onclick handler from elements', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<div onclick="alert(1)">click me</div>',
|
||||
]);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -11,8 +11,8 @@ it('provides comments relationship on commentable model', function () {
|
||||
Comment::factory()->count(3)->create([
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
]);
|
||||
|
||||
expect($post->comments)->toHaveCount(3);
|
||||
@@ -25,8 +25,8 @@ it('provides topLevelComments excluding replies', function () {
|
||||
$attrs = [
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
];
|
||||
|
||||
$topLevel = Comment::factory()->create($attrs);
|
||||
@@ -46,8 +46,8 @@ it('provides comment count', function () {
|
||||
Comment::factory()->count(5)->create([
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'commentable_id' => $post->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
]);
|
||||
|
||||
expect($post->commentCount())->toBe(5);
|
||||
@@ -61,15 +61,15 @@ it('scopes comments to the specific commentable', function () {
|
||||
Comment::factory()->count(3)->create([
|
||||
'commentable_type' => $post1->getMorphClass(),
|
||||
'commentable_id' => $post1->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
]);
|
||||
|
||||
Comment::factory()->count(2)->create([
|
||||
'commentable_type' => $post2->getMorphClass(),
|
||||
'commentable_id' => $post2->id,
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'user_id' => $user->id,
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->id,
|
||||
]);
|
||||
|
||||
expect($post1->commentCount())->toBe(3);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Livewire\Livewire;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Livewire\CommentItem;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -14,12 +14,12 @@ it('renders mention with styled span', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>@Alice said hi</p>',
|
||||
]);
|
||||
|
||||
$comment->mentions()->attach($alice->id, ['user_type' => $alice->getMorphClass()]);
|
||||
$comment->mentions()->attach($alice->id, ['commenter_type' => $alice->getMorphClass()]);
|
||||
|
||||
$rendered = $comment->renderBodyWithMentions();
|
||||
|
||||
@@ -36,13 +36,13 @@ it('renders multiple mentions with styled spans', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>@Alice and @Bob</p>',
|
||||
]);
|
||||
|
||||
$comment->mentions()->attach($alice->id, ['user_type' => $alice->getMorphClass()]);
|
||||
$comment->mentions()->attach($bob->id, ['user_type' => $bob->getMorphClass()]);
|
||||
$comment->mentions()->attach($alice->id, ['commenter_type' => $alice->getMorphClass()]);
|
||||
$comment->mentions()->attach($bob->id, ['commenter_type' => $bob->getMorphClass()]);
|
||||
|
||||
$rendered = $comment->renderBodyWithMentions();
|
||||
|
||||
@@ -58,8 +58,8 @@ it('does not style non-mentioned @text', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>@ghost is not here</p>',
|
||||
]);
|
||||
|
||||
@@ -76,12 +76,12 @@ it('renders comment-mention class in Livewire component', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Hello @Alice</p>',
|
||||
]);
|
||||
|
||||
$comment->mentions()->attach($alice->id, ['user_type' => $alice->getMorphClass()]);
|
||||
$comment->mentions()->attach($alice->id, ['commenter_type' => $alice->getMorphClass()]);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Contracts\MentionResolver;
|
||||
use Relaticle\Comments\Events\UserMentioned;
|
||||
use Relaticle\Comments\Mentions\DefaultMentionResolver;
|
||||
use Relaticle\Comments\Mentions\MentionParser;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -62,8 +62,8 @@ it('stores mentions in comment_mentions table on create', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Hello @john and @jane</p>',
|
||||
]);
|
||||
|
||||
@@ -85,8 +85,8 @@ it('dispatches UserMentioned event for each mentioned user', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Hello @john and @jane</p>',
|
||||
]);
|
||||
|
||||
@@ -116,8 +116,8 @@ it('only dispatches UserMentioned for newly added mentions on update', function
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Hello @john</p>',
|
||||
]);
|
||||
|
||||
@@ -146,8 +146,8 @@ it('removes mentions from pivot when user removed from body', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Hello @john and @jane</p>',
|
||||
]);
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<?php
|
||||
|
||||
use Livewire\Livewire;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Livewire\CommentItem;
|
||||
use Relaticle\Comments\Livewire\Comments;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -92,8 +92,8 @@ it('stores mentions when editing comment with @mention', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Original comment</p>',
|
||||
]);
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\CommentSubscription;
|
||||
use Relaticle\Comments\Events\CommentCreated;
|
||||
use Relaticle\Comments\Events\UserMentioned;
|
||||
use Relaticle\Comments\Listeners\SendCommentRepliedNotification;
|
||||
use Relaticle\Comments\Listeners\SendUserMentionedNotification;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Models\Subscription;
|
||||
use Relaticle\Comments\Notifications\CommentRepliedNotification;
|
||||
use Relaticle\Comments\Notifications\UserMentionedNotification;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
@@ -19,21 +19,21 @@ it('sends CommentRepliedNotification to parent comment author when reply is crea
|
||||
$replyAuthor = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $parentAuthor);
|
||||
Subscription::subscribe($post, $parentAuthor);
|
||||
|
||||
$parentComment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $parentAuthor->getKey(),
|
||||
'user_type' => $parentAuthor->getMorphClass(),
|
||||
'commenter_id' => $parentAuthor->getKey(),
|
||||
'commenter_type' => $parentAuthor->getMorphClass(),
|
||||
'body' => '<p>Parent comment</p>',
|
||||
]);
|
||||
|
||||
$reply = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $replyAuthor->getKey(),
|
||||
'user_type' => $replyAuthor->getMorphClass(),
|
||||
'commenter_id' => $replyAuthor->getKey(),
|
||||
'commenter_type' => $replyAuthor->getMorphClass(),
|
||||
'parent_id' => $parentComment->id,
|
||||
'body' => '<p>A reply</p>',
|
||||
]);
|
||||
@@ -51,13 +51,13 @@ it('does NOT send reply notification for top-level comments', function () {
|
||||
$subscriber = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $subscriber);
|
||||
Subscription::subscribe($post, $subscriber);
|
||||
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
'body' => '<p>Top-level comment</p>',
|
||||
]);
|
||||
|
||||
@@ -73,21 +73,21 @@ it('does NOT send reply notification to the reply author', function () {
|
||||
$user = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $user);
|
||||
Subscription::subscribe($post, $user);
|
||||
|
||||
$parentComment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>My comment</p>',
|
||||
]);
|
||||
|
||||
$reply = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'parent_id' => $parentComment->id,
|
||||
'body' => '<p>My own reply</p>',
|
||||
]);
|
||||
@@ -108,8 +108,8 @@ it('sends UserMentionedNotification when a user is mentioned', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
'body' => '<p>Hey @someone</p>',
|
||||
]);
|
||||
|
||||
@@ -128,8 +128,8 @@ it('does NOT send mention notification to the comment author', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
'body' => '<p>Hey @myself</p>',
|
||||
]);
|
||||
|
||||
@@ -146,22 +146,22 @@ it('does NOT send reply notification to unsubscribed user', function () {
|
||||
$unsubscribedUser = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $unsubscribedUser);
|
||||
CommentSubscription::unsubscribe($post, $unsubscribedUser);
|
||||
Subscription::subscribe($post, $unsubscribedUser);
|
||||
Subscription::unsubscribe($post, $unsubscribedUser);
|
||||
|
||||
$parentComment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $unsubscribedUser->getKey(),
|
||||
'user_type' => $unsubscribedUser->getMorphClass(),
|
||||
'commenter_id' => $unsubscribedUser->getKey(),
|
||||
'commenter_type' => $unsubscribedUser->getMorphClass(),
|
||||
'body' => '<p>Original</p>',
|
||||
]);
|
||||
|
||||
$reply = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
'parent_id' => $parentComment->id,
|
||||
'body' => '<p>Reply</p>',
|
||||
]);
|
||||
@@ -178,20 +178,20 @@ it('auto-subscribes the comment author when creating a comment', function () {
|
||||
$author = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
expect(CommentSubscription::isSubscribed($post, $author))->toBeFalse();
|
||||
expect(Subscription::isSubscribed($post, $author))->toBeFalse();
|
||||
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
'body' => '<p>My comment</p>',
|
||||
]);
|
||||
|
||||
$listener = new SendCommentRepliedNotification;
|
||||
$listener->handle(new CommentCreated($comment));
|
||||
|
||||
expect(CommentSubscription::isSubscribed($post, $author))->toBeTrue();
|
||||
expect(Subscription::isSubscribed($post, $author))->toBeTrue();
|
||||
});
|
||||
|
||||
it('suppresses all notifications when notifications are disabled via config', function () {
|
||||
@@ -203,21 +203,21 @@ it('suppresses all notifications when notifications are disabled via config', fu
|
||||
$mentioned = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $subscriber);
|
||||
Subscription::subscribe($post, $subscriber);
|
||||
|
||||
$parentComment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $subscriber->getKey(),
|
||||
'user_type' => $subscriber->getMorphClass(),
|
||||
'commenter_id' => $subscriber->getKey(),
|
||||
'commenter_type' => $subscriber->getMorphClass(),
|
||||
'body' => '<p>Original</p>',
|
||||
]);
|
||||
|
||||
$reply = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
'parent_id' => $parentComment->id,
|
||||
'body' => '<p>Reply</p>',
|
||||
]);
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\CommentSubscription;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\Events\CommentCreated;
|
||||
use Relaticle\Comments\Events\UserMentioned;
|
||||
use Relaticle\Comments\Listeners\SendCommentRepliedNotification;
|
||||
use Relaticle\Comments\Listeners\SendUserMentionedNotification;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Models\Subscription;
|
||||
use Relaticle\Comments\Notifications\CommentRepliedNotification;
|
||||
use Relaticle\Comments\Notifications\UserMentionedNotification;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
@@ -22,8 +21,8 @@ it('returns correct via channels from config for CommentRepliedNotification', fu
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Hello</p>',
|
||||
]);
|
||||
|
||||
@@ -39,8 +38,8 @@ it('returns toDatabase array with comment data for CommentRepliedNotification',
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>This is a reply body</p>',
|
||||
]);
|
||||
|
||||
@@ -51,7 +50,7 @@ it('returns toDatabase array with comment data for CommentRepliedNotification',
|
||||
->and($data['comment_id'])->toBe($comment->id)
|
||||
->and($data['commentable_type'])->toBe($post->getMorphClass())
|
||||
->and($data['commentable_id'])->toBe($post->id)
|
||||
->and($data['commenter_name'])->toBe($user->getCommentName());
|
||||
->and($data['commenter_name'])->toBe($user->getCommentDisplayName());
|
||||
});
|
||||
|
||||
it('returns correct via channels from config for UserMentionedNotification', function () {
|
||||
@@ -64,8 +63,8 @@ it('returns correct via channels from config for UserMentionedNotification', fun
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $mentionedBy->getKey(),
|
||||
'user_type' => $mentionedBy->getMorphClass(),
|
||||
'commenter_id' => $mentionedBy->getKey(),
|
||||
'commenter_type' => $mentionedBy->getMorphClass(),
|
||||
'body' => '<p>Hey @someone</p>',
|
||||
]);
|
||||
|
||||
@@ -82,8 +81,8 @@ it('returns toDatabase array with mention data for UserMentionedNotification', f
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $mentioner->getKey(),
|
||||
'user_type' => $mentioner->getMorphClass(),
|
||||
'commenter_id' => $mentioner->getKey(),
|
||||
'commenter_type' => $mentioner->getMorphClass(),
|
||||
'body' => '<p>Hey @mentioned</p>',
|
||||
]);
|
||||
|
||||
@@ -92,7 +91,7 @@ it('returns toDatabase array with mention data for UserMentionedNotification', f
|
||||
|
||||
expect($data)->toHaveKeys(['comment_id', 'commentable_type', 'commentable_id', 'mentioner_name', 'body'])
|
||||
->and($data['comment_id'])->toBe($comment->id)
|
||||
->and($data['mentioner_name'])->toBe($mentioner->getCommentName());
|
||||
->and($data['mentioner_name'])->toBe($mentioner->getCommentDisplayName());
|
||||
});
|
||||
|
||||
it('sends notification to subscribers when reply comment is created', function () {
|
||||
@@ -102,21 +101,21 @@ it('sends notification to subscribers when reply comment is created', function (
|
||||
$subscriber = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $subscriber);
|
||||
Subscription::subscribe($post, $subscriber);
|
||||
|
||||
$parentComment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $subscriber->getKey(),
|
||||
'user_type' => $subscriber->getMorphClass(),
|
||||
'commenter_id' => $subscriber->getKey(),
|
||||
'commenter_type' => $subscriber->getMorphClass(),
|
||||
'body' => '<p>Original comment</p>',
|
||||
]);
|
||||
|
||||
$reply = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
'parent_id' => $parentComment->id,
|
||||
'body' => '<p>Reply to original</p>',
|
||||
]);
|
||||
@@ -134,13 +133,13 @@ it('does NOT send notification for top-level comments', function () {
|
||||
$subscriber = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $subscriber);
|
||||
Subscription::subscribe($post, $subscriber);
|
||||
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
'body' => '<p>Top-level comment</p>',
|
||||
]);
|
||||
|
||||
@@ -156,21 +155,21 @@ it('does NOT notify the reply author themselves', function () {
|
||||
$user = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $user);
|
||||
Subscription::subscribe($post, $user);
|
||||
|
||||
$parentComment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>My comment</p>',
|
||||
]);
|
||||
|
||||
$reply = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'parent_id' => $parentComment->id,
|
||||
'body' => '<p>My own reply</p>',
|
||||
]);
|
||||
@@ -187,20 +186,20 @@ it('auto-subscribes comment author to the thread', function () {
|
||||
$author = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
expect(CommentSubscription::isSubscribed($post, $author))->toBeFalse();
|
||||
expect(Subscription::isSubscribed($post, $author))->toBeFalse();
|
||||
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
'body' => '<p>Comment</p>',
|
||||
]);
|
||||
|
||||
$listener = new SendCommentRepliedNotification;
|
||||
$listener->handle(new CommentCreated($comment));
|
||||
|
||||
expect(CommentSubscription::isSubscribed($post, $author))->toBeTrue();
|
||||
expect(Subscription::isSubscribed($post, $author))->toBeTrue();
|
||||
});
|
||||
|
||||
it('only notifies subscribed users for reply notifications', function () {
|
||||
@@ -211,21 +210,21 @@ it('only notifies subscribed users for reply notifications', function () {
|
||||
$nonSubscriber = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $subscriber);
|
||||
Subscription::subscribe($post, $subscriber);
|
||||
|
||||
$parentComment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $subscriber->getKey(),
|
||||
'user_type' => $subscriber->getMorphClass(),
|
||||
'commenter_id' => $subscriber->getKey(),
|
||||
'commenter_type' => $subscriber->getMorphClass(),
|
||||
'body' => '<p>Original</p>',
|
||||
]);
|
||||
|
||||
$reply = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
'parent_id' => $parentComment->id,
|
||||
'body' => '<p>Reply</p>',
|
||||
]);
|
||||
@@ -247,8 +246,8 @@ it('sends mention notification to mentioned user', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
'body' => '<p>Hey @mentioned</p>',
|
||||
]);
|
||||
|
||||
@@ -268,8 +267,8 @@ it('does NOT send mention notification to the comment author', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
'body' => '<p>Hey @myself</p>',
|
||||
]);
|
||||
|
||||
@@ -287,13 +286,13 @@ it('auto-subscribes mentioned user to the thread', function () {
|
||||
$mentioned = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
expect(CommentSubscription::isSubscribed($post, $mentioned))->toBeFalse();
|
||||
expect(Subscription::isSubscribed($post, $mentioned))->toBeFalse();
|
||||
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
'body' => '<p>Hey @mentioned</p>',
|
||||
]);
|
||||
|
||||
@@ -301,7 +300,7 @@ it('auto-subscribes mentioned user to the thread', function () {
|
||||
$listener = new SendUserMentionedNotification;
|
||||
$listener->handle($event);
|
||||
|
||||
expect(CommentSubscription::isSubscribed($post, $mentioned))->toBeTrue();
|
||||
expect(Subscription::isSubscribed($post, $mentioned))->toBeTrue();
|
||||
});
|
||||
|
||||
it('does not send notifications when notifications are disabled', function () {
|
||||
@@ -313,21 +312,21 @@ it('does not send notifications when notifications are disabled', function () {
|
||||
$mentioned = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $subscriber);
|
||||
Subscription::subscribe($post, $subscriber);
|
||||
|
||||
$parentComment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $subscriber->getKey(),
|
||||
'user_type' => $subscriber->getMorphClass(),
|
||||
'commenter_id' => $subscriber->getKey(),
|
||||
'commenter_type' => $subscriber->getMorphClass(),
|
||||
'body' => '<p>Original</p>',
|
||||
]);
|
||||
|
||||
$reply = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $author->getKey(),
|
||||
'user_type' => $author->getMorphClass(),
|
||||
'commenter_id' => $author->getKey(),
|
||||
'commenter_type' => $author->getMorphClass(),
|
||||
'parent_id' => $parentComment->id,
|
||||
'body' => '<p>Reply</p>',
|
||||
]);
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
|
||||
it('returns false for isBroadcastingEnabled by default', function () {
|
||||
expect(Config::isBroadcastingEnabled())->toBeFalse();
|
||||
expect(CommentsConfig::isBroadcastingEnabled())->toBeFalse();
|
||||
});
|
||||
|
||||
it('returns true for isBroadcastingEnabled when config overridden', function () {
|
||||
config()->set('comments.broadcasting.enabled', true);
|
||||
|
||||
expect(Config::isBroadcastingEnabled())->toBeTrue();
|
||||
expect(CommentsConfig::isBroadcastingEnabled())->toBeTrue();
|
||||
});
|
||||
|
||||
it('returns comments as default broadcast channel prefix', function () {
|
||||
expect(Config::getBroadcastChannelPrefix())->toBe('comments');
|
||||
expect(CommentsConfig::getBroadcastChannelPrefix())->toBe('comments');
|
||||
});
|
||||
|
||||
it('returns custom broadcast channel prefix when overridden', function () {
|
||||
config()->set('comments.broadcasting.channel_prefix', 'my-app-comments');
|
||||
|
||||
expect(Config::getBroadcastChannelPrefix())->toBe('my-app-comments');
|
||||
expect(CommentsConfig::getBroadcastChannelPrefix())->toBe('my-app-comments');
|
||||
});
|
||||
|
||||
it('returns 10s as default polling interval', function () {
|
||||
expect(Config::getPollingInterval())->toBe('10s');
|
||||
expect(CommentsConfig::getPollingInterval())->toBe('10s');
|
||||
});
|
||||
|
||||
it('returns custom polling interval when overridden', function () {
|
||||
config()->set('comments.polling.interval', '30s');
|
||||
|
||||
expect(Config::getPollingInterval())->toBe('30s');
|
||||
expect(CommentsConfig::getPollingInterval())->toBe('30s');
|
||||
});
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Livewire\Livewire;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\CommentReaction;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Events\CommentReacted;
|
||||
use Relaticle\Comments\Livewire\Reactions;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Models\Reaction;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -17,8 +17,8 @@ it('adds a reaction when user clicks an emoji', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
@@ -26,10 +26,10 @@ it('adds a reaction when user clicks an emoji', function () {
|
||||
Livewire::test(Reactions::class, ['comment' => $comment])
|
||||
->call('toggleReaction', 'thumbs_up');
|
||||
|
||||
expect(CommentReaction::where([
|
||||
expect(Reaction::where([
|
||||
'comment_id' => $comment->id,
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'reaction' => 'thumbs_up',
|
||||
])->exists())->toBeTrue();
|
||||
});
|
||||
@@ -41,14 +41,14 @@ it('removes a reaction when toggling same emoji', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
CommentReaction::create([
|
||||
Reaction::create([
|
||||
'comment_id' => $comment->id,
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'reaction' => 'thumbs_up',
|
||||
]);
|
||||
|
||||
@@ -57,10 +57,10 @@ it('removes a reaction when toggling same emoji', function () {
|
||||
Livewire::test(Reactions::class, ['comment' => $comment])
|
||||
->call('toggleReaction', 'thumbs_up');
|
||||
|
||||
expect(CommentReaction::where([
|
||||
expect(Reaction::where([
|
||||
'comment_id' => $comment->id,
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'reaction' => 'thumbs_up',
|
||||
])->exists())->toBeFalse();
|
||||
});
|
||||
@@ -74,8 +74,8 @@ it('fires CommentReacted event with added action', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
@@ -98,14 +98,14 @@ it('fires CommentReacted event with removed action', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
CommentReaction::create([
|
||||
Reaction::create([
|
||||
'comment_id' => $comment->id,
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'reaction' => 'heart',
|
||||
]);
|
||||
|
||||
@@ -133,35 +133,35 @@ it('returns correct reaction summary with counts', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user1->getKey(),
|
||||
'user_type' => $user1->getMorphClass(),
|
||||
'commenter_id' => $user1->getKey(),
|
||||
'commenter_type' => $user1->getMorphClass(),
|
||||
]);
|
||||
|
||||
CommentReaction::create([
|
||||
Reaction::create([
|
||||
'comment_id' => $comment->id,
|
||||
'user_id' => $user1->getKey(),
|
||||
'user_type' => $user1->getMorphClass(),
|
||||
'commenter_id' => $user1->getKey(),
|
||||
'commenter_type' => $user1->getMorphClass(),
|
||||
'reaction' => 'thumbs_up',
|
||||
]);
|
||||
|
||||
CommentReaction::create([
|
||||
Reaction::create([
|
||||
'comment_id' => $comment->id,
|
||||
'user_id' => $user2->getKey(),
|
||||
'user_type' => $user2->getMorphClass(),
|
||||
'commenter_id' => $user2->getKey(),
|
||||
'commenter_type' => $user2->getMorphClass(),
|
||||
'reaction' => 'thumbs_up',
|
||||
]);
|
||||
|
||||
CommentReaction::create([
|
||||
Reaction::create([
|
||||
'comment_id' => $comment->id,
|
||||
'user_id' => $user3->getKey(),
|
||||
'user_type' => $user3->getMorphClass(),
|
||||
'commenter_id' => $user3->getKey(),
|
||||
'commenter_type' => $user3->getMorphClass(),
|
||||
'reaction' => 'thumbs_up',
|
||||
]);
|
||||
|
||||
CommentReaction::create([
|
||||
Reaction::create([
|
||||
'comment_id' => $comment->id,
|
||||
'user_id' => $user1->getKey(),
|
||||
'user_type' => $user1->getMorphClass(),
|
||||
'commenter_id' => $user1->getKey(),
|
||||
'commenter_type' => $user1->getMorphClass(),
|
||||
'reaction' => 'heart',
|
||||
]);
|
||||
|
||||
@@ -187,14 +187,14 @@ it('requires authentication to react', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
Livewire::test(Reactions::class, ['comment' => $comment])
|
||||
->call('toggleReaction', 'thumbs_up');
|
||||
|
||||
expect(CommentReaction::count())->toBe(0);
|
||||
expect(Reaction::count())->toBe(0);
|
||||
});
|
||||
|
||||
it('allows multiple reaction types from same user', function () {
|
||||
@@ -204,8 +204,8 @@ it('allows multiple reaction types from same user', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
@@ -215,17 +215,17 @@ it('allows multiple reaction types from same user', function () {
|
||||
$component->call('toggleReaction', 'thumbs_up');
|
||||
$component->call('toggleReaction', 'heart');
|
||||
|
||||
expect(CommentReaction::where([
|
||||
expect(Reaction::where([
|
||||
'comment_id' => $comment->id,
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'reaction' => 'thumbs_up',
|
||||
])->exists())->toBeTrue();
|
||||
|
||||
expect(CommentReaction::where([
|
||||
expect(Reaction::where([
|
||||
'comment_id' => $comment->id,
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'reaction' => 'heart',
|
||||
])->exists())->toBeTrue();
|
||||
});
|
||||
@@ -238,8 +238,8 @@ it('allows same reaction from multiple users', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user1->getKey(),
|
||||
'user_type' => $user1->getMorphClass(),
|
||||
'commenter_id' => $user1->getKey(),
|
||||
'commenter_type' => $user1->getMorphClass(),
|
||||
]);
|
||||
|
||||
$this->actingAs($user1);
|
||||
@@ -250,7 +250,7 @@ it('allows same reaction from multiple users', function () {
|
||||
Livewire::test(Reactions::class, ['comment' => $comment])
|
||||
->call('toggleReaction', 'thumbs_up');
|
||||
|
||||
expect(CommentReaction::where([
|
||||
expect(Reaction::where([
|
||||
'comment_id' => $comment->id,
|
||||
'reaction' => 'thumbs_up',
|
||||
])->count())->toBe(2);
|
||||
@@ -263,8 +263,8 @@ it('rejects invalid reaction keys', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
@@ -272,7 +272,7 @@ it('rejects invalid reaction keys', function () {
|
||||
Livewire::test(Reactions::class, ['comment' => $comment])
|
||||
->call('toggleReaction', 'invalid_emoji');
|
||||
|
||||
expect(CommentReaction::count())->toBe(0);
|
||||
expect(Reaction::count())->toBe(0);
|
||||
});
|
||||
|
||||
it('marks reacted_by_user correctly in summary', function () {
|
||||
@@ -283,14 +283,14 @@ it('marks reacted_by_user correctly in summary', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $userA->getKey(),
|
||||
'user_type' => $userA->getMorphClass(),
|
||||
'commenter_id' => $userA->getKey(),
|
||||
'commenter_type' => $userA->getMorphClass(),
|
||||
]);
|
||||
|
||||
CommentReaction::create([
|
||||
Reaction::create([
|
||||
'comment_id' => $comment->id,
|
||||
'user_id' => $userA->getKey(),
|
||||
'user_type' => $userA->getMorphClass(),
|
||||
'commenter_id' => $userA->getKey(),
|
||||
'commenter_type' => $userA->getMorphClass(),
|
||||
'reaction' => 'thumbs_up',
|
||||
]);
|
||||
|
||||
@@ -310,7 +310,7 @@ it('marks reacted_by_user correctly in summary', function () {
|
||||
});
|
||||
|
||||
it('returns configured emoji set from config', function () {
|
||||
$emojiSet = Config::getReactionEmojiSet();
|
||||
$emojiSet = CommentsConfig::getReactionEmojiSet();
|
||||
|
||||
expect($emojiSet)->toBeArray();
|
||||
expect($emojiSet)->toHaveKey('thumbs_up');
|
||||
@@ -322,7 +322,7 @@ it('returns configured emoji set from config', function () {
|
||||
});
|
||||
|
||||
it('returns allowed reaction keys from config', function () {
|
||||
$allowed = Config::getAllowedReactions();
|
||||
$allowed = CommentsConfig::getAllowedReactions();
|
||||
|
||||
expect($allowed)->toBeArray();
|
||||
expect($allowed)->toContain('thumbs_up');
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
|
||||
use Livewire\Livewire;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Livewire\CommentItem;
|
||||
use Relaticle\Comments\Livewire\Comments;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -35,8 +35,8 @@ it('pre-fills editBody with existing comment HTML when starting edit', function
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => $originalHtml,
|
||||
]);
|
||||
|
||||
@@ -54,8 +54,8 @@ it('saves edited HTML content through edit form', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Original</p>',
|
||||
]);
|
||||
|
||||
@@ -81,8 +81,8 @@ it('creates reply with rich HTML content', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
@@ -108,8 +108,8 @@ it('renders comment body with fi-prose class', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>Styled comment</p>',
|
||||
]);
|
||||
|
||||
@@ -120,7 +120,7 @@ it('renders comment body with fi-prose class', function () {
|
||||
});
|
||||
|
||||
it('returns editor toolbar configuration as nested array', function () {
|
||||
$toolbar = Config::getEditorToolbar();
|
||||
$toolbar = CommentsConfig::getEditorToolbar();
|
||||
|
||||
expect($toolbar)->toBeArray();
|
||||
expect($toolbar)->not->toBeEmpty();
|
||||
@@ -134,7 +134,7 @@ it('uses custom toolbar config when overridden', function () {
|
||||
['bold', 'italic'],
|
||||
]]);
|
||||
|
||||
$toolbar = Config::getEditorToolbar();
|
||||
$toolbar = CommentsConfig::getEditorToolbar();
|
||||
|
||||
expect($toolbar)->toHaveCount(1);
|
||||
expect($toolbar[0])->toBe(['bold', 'italic']);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Config;
|
||||
use Relaticle\Comments\CommentsConfig;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
|
||||
it('registers the config file', function () {
|
||||
expect(config('comments'))->toBeArray();
|
||||
@@ -11,15 +11,15 @@ it('registers the config file', function () {
|
||||
});
|
||||
|
||||
it('resolves the comment model from config', function () {
|
||||
expect(Config::getCommentModel())->toBe(Comment::class);
|
||||
expect(CommentsConfig::getCommentModel())->toBe(Comment::class);
|
||||
});
|
||||
|
||||
it('resolves the comment table from config', function () {
|
||||
expect(Config::getCommentTable())->toBe('comments');
|
||||
expect(CommentsConfig::getCommentTable())->toBe('comments');
|
||||
});
|
||||
|
||||
it('resolves max depth from config', function () {
|
||||
expect(Config::getMaxDepth())->toBe(2);
|
||||
expect(CommentsConfig::getMaxDepth())->toBe(2);
|
||||
});
|
||||
|
||||
it('registers the morph map for comment', function () {
|
||||
@@ -32,7 +32,7 @@ it('creates the comments table via migration', function () {
|
||||
expect(Schema::hasTable('comments'))->toBeTrue();
|
||||
expect(Schema::hasColumns('comments', [
|
||||
'id', 'commentable_type', 'commentable_id',
|
||||
'user_type', 'user_id', 'parent_id', 'body',
|
||||
'commenter_type', 'commenter_id', 'parent_id', 'body',
|
||||
'edited_at', 'deleted_at', 'created_at', 'updated_at',
|
||||
]))->toBeTrue();
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Livewire\Livewire;
|
||||
use Relaticle\Comments\CommentSubscription;
|
||||
use Relaticle\Comments\Livewire\Comments;
|
||||
use Relaticle\Comments\Models\Subscription;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -12,35 +12,35 @@ it('subscribes user when toggling from unsubscribed state', function () {
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
expect(CommentSubscription::isSubscribed($post, $user))->toBeFalse();
|
||||
expect(Subscription::isSubscribed($post, $user))->toBeFalse();
|
||||
|
||||
Livewire::test(Comments::class, ['model' => $post])
|
||||
->call('toggleSubscription');
|
||||
|
||||
expect(CommentSubscription::isSubscribed($post, $user))->toBeTrue();
|
||||
expect(Subscription::isSubscribed($post, $user))->toBeTrue();
|
||||
});
|
||||
|
||||
it('unsubscribes user when toggling from subscribed state', function () {
|
||||
$user = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $user);
|
||||
Subscription::subscribe($post, $user);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
expect(CommentSubscription::isSubscribed($post, $user))->toBeTrue();
|
||||
expect(Subscription::isSubscribed($post, $user))->toBeTrue();
|
||||
|
||||
Livewire::test(Comments::class, ['model' => $post])
|
||||
->call('toggleSubscription');
|
||||
|
||||
expect(CommentSubscription::isSubscribed($post, $user))->toBeFalse();
|
||||
expect(Subscription::isSubscribed($post, $user))->toBeFalse();
|
||||
});
|
||||
|
||||
it('returns true for isSubscribed computed when user is subscribed', function () {
|
||||
$user = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $user);
|
||||
Subscription::subscribe($post, $user);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
@@ -64,7 +64,7 @@ it('renders Subscribed text for subscribed user', function () {
|
||||
$user = User::factory()->create();
|
||||
$post = Post::factory()->create();
|
||||
|
||||
CommentSubscription::subscribe($post, $user);
|
||||
Subscription::subscribe($post, $user);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Relaticle\Comments\Comment;
|
||||
use Relaticle\Comments\Events\UserMentioned;
|
||||
use Relaticle\Comments\Models\Comment;
|
||||
use Relaticle\Comments\Tests\Models\Post;
|
||||
use Relaticle\Comments\Tests\Models\User;
|
||||
|
||||
@@ -13,8 +13,8 @@ it('carries correct comment and mentioned user in payload', function () {
|
||||
$comment = Comment::factory()->create([
|
||||
'commentable_id' => $post->id,
|
||||
'commentable_type' => $post->getMorphClass(),
|
||||
'user_id' => $user->getKey(),
|
||||
'user_type' => $user->getMorphClass(),
|
||||
'commenter_id' => $user->getKey(),
|
||||
'commenter_type' => $user->getMorphClass(),
|
||||
'body' => '<p>@john</p>',
|
||||
]);
|
||||
|
||||
|
||||
@@ -5,14 +5,14 @@ namespace Relaticle\Comments\Tests\Models;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Relaticle\Comments\Concerns\IsCommenter;
|
||||
use Relaticle\Comments\Contracts\Commenter;
|
||||
use Relaticle\Comments\Concerns\CanComment;
|
||||
use Relaticle\Comments\Contracts\Commentator;
|
||||
use Relaticle\Comments\Tests\Database\Factories\UserFactory;
|
||||
|
||||
class User extends Authenticatable implements Commenter
|
||||
class User extends Authenticatable implements Commentator
|
||||
{
|
||||
use CanComment;
|
||||
use HasFactory;
|
||||
use IsCommenter;
|
||||
use Notifiable;
|
||||
|
||||
protected $table = 'users';
|
||||
|
||||
@@ -48,13 +48,13 @@ abstract class TestCase extends Orchestra
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create(config('comments.tables.comments', 'comments'), function (Blueprint $table) {
|
||||
Schema::create(config('comments.table_names.comments', 'comments'), function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('commentable');
|
||||
$table->morphs('user');
|
||||
$table->morphs('commenter');
|
||||
$table->foreignId('parent_id')
|
||||
->nullable()
|
||||
->constrained(config('comments.tables.comments', 'comments'))
|
||||
->constrained(config('comments.table_names.comments', 'comments'))
|
||||
->cascadeOnDelete();
|
||||
$table->text('body');
|
||||
$table->timestamp('edited_at')->nullable();
|
||||
@@ -67,30 +67,30 @@ abstract class TestCase extends Orchestra
|
||||
Schema::create('comment_mentions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('comment_id')
|
||||
->constrained(config('comments.tables.comments', 'comments'))
|
||||
->constrained(config('comments.table_names.comments', 'comments'))
|
||||
->cascadeOnDelete();
|
||||
$table->morphs('user');
|
||||
$table->morphs('commenter');
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['comment_id', 'user_id', 'user_type']);
|
||||
$table->unique(['comment_id', 'commenter_id', 'commenter_type']);
|
||||
});
|
||||
|
||||
Schema::create('comment_reactions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('comment_id')
|
||||
->constrained(config('comments.tables.comments', 'comments'))
|
||||
->constrained(config('comments.table_names.comments', 'comments'))
|
||||
->cascadeOnDelete();
|
||||
$table->morphs('user');
|
||||
$table->morphs('commenter');
|
||||
$table->string('reaction');
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['comment_id', 'user_id', 'user_type', 'reaction']);
|
||||
$table->unique(['comment_id', 'commenter_id', 'commenter_type', 'reaction']);
|
||||
});
|
||||
|
||||
Schema::create('comment_attachments', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('comment_id')
|
||||
->constrained(config('comments.tables.comments', 'comments'))
|
||||
->constrained(config('comments.table_names.comments', 'comments'))
|
||||
->cascadeOnDelete();
|
||||
$table->string('file_path');
|
||||
$table->string('original_name');
|
||||
@@ -112,10 +112,10 @@ abstract class TestCase extends Orchestra
|
||||
Schema::create('comment_subscriptions', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('commentable');
|
||||
$table->morphs('user');
|
||||
$table->morphs('commenter');
|
||||
$table->timestamp('created_at')->nullable();
|
||||
|
||||
$table->unique(['commentable_type', 'commentable_id', 'user_type', 'user_id'], 'comment_subscriptions_unique');
|
||||
$table->unique(['commentable_type', 'commentable_id', 'commenter_type', 'commenter_id'], 'comment_subscriptions_unique');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user