diff --git a/config/nested-comments.php b/config/nested-comments.php index b62bcce..72c7862 100644 --- a/config/nested-comments.php +++ b/config/nested-comments.php @@ -1,5 +1,8 @@ [ 'comments' => 'comments', @@ -10,7 +13,7 @@ 'models' => [ 'comment' => \Coolsam\NestedComments\Models\Comment::class, 'reaction' => \Coolsam\NestedComments\Models\Reaction::class, - 'user' => env( 'AUTH_MODEL', 'App\Models\User'), // The model that will be used to get the authenticated user + 'user' => env('AUTH_MODEL', 'App\Models\User'), // The model that will be used to get the authenticated user ], 'policies' => [ @@ -32,4 +35,7 @@ 'allow-multiple-reactions' => env('ALLOW_MULTIPLE_REACTIONS', false), // Allow multiple reactions from the same user 'allow-guest-reactions' => env('ALLOW_GUEST_REACTIONS', false), // Allow guest users to react 'allow-guest-comments' => env('ALLOW_GUEST_COMMENTS', false), // Allow guest users to comment + 'closures' => [ + 'getUserNameUsing' => fn (Authenticatable | Model $user) => $user->getAttribute('name') + ] ]; diff --git a/database/migrations/create_nested_comments_table.php.stub b/database/migrations/create_nested_comments_table.php.stub index 2340b08..9ac51f6 100644 --- a/database/migrations/create_nested_comments_table.php.stub +++ b/database/migrations/create_nested_comments_table.php.stub @@ -16,7 +16,10 @@ return new class extends Migration $table->foreignId('user_id')->nullable()->constrained($users)->cascadeOnDelete(); $table->text('body'); $table->morphs('commentable'); + $table->ulid('guest_id')->nullable()->index(); + $table->string('guest_name')->nullable(); $table->ipAddress()->nullable(); + $table->boolean('is_published')->default(false); $table->timestamps(); }); @@ -25,7 +28,10 @@ return new class extends Migration $table->foreignId('user_id')->nullable()->constrained($users)->cascadeOnDelete(); $table->morphs('reactable'); $table->string('emoji'); + $table->ulid('guest_id')->nullable()->index(); + $table->string('guest_name')->nullable(); $table->ipAddress()->nullable(); + $table->boolean('is_published')->default(false); $table->timestamps(); }); } diff --git a/resources/dist/nested-comments.css b/resources/dist/nested-comments.css index 94d422c..da973a5 100644 --- a/resources/dist/nested-comments.css +++ b/resources/dist/nested-comments.css @@ -1 +1 @@ -*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border-width:0;border-style:solid;border-color:rgba(var(--gray-200),1)}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-family),ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:rgba(var(--gray-400),1)}input::placeholder,textarea::placeholder{opacity:1;color:rgba(var(--gray-400),1)}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],input:where(:not([type])),select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:rgba(var(--gray-500),var(--tw-border-opacity,1));border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow:0 0 #0000}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,input:where(:not([type])):focus,select:focus,textarea:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:rgba(var(--gray-500),var(--tw-text-opacity,1));opacity:1}input::placeholder,textarea::placeholder{color:rgba(var(--gray-500),var(--tw-text-opacity,1));opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-year-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple],[size]:where(select:not([size="1"])){background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:rgba(var(--gray-500),var(--tw-border-opacity,1));border-width:1px;--tw-shadow:0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0'/%3E%3C/svg%3E")}@media (forced-colors:active){[type=checkbox]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}@media (forced-colors:active){[type=radio]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=radio]:checked:focus,[type=radio]:checked:hover{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}@media (forced-colors:active){[type=checkbox]:indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}:root.dark{color-scheme:dark}[data-field-wrapper]{scroll-margin-top:8rem}:checked+*>.\[\:checked\+\*\>\&\]\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}input:checked+.\[input\:checked\+\&\]\:bg-custom-600{--tw-bg-opacity:1;background-color:rgba(var(--c-600),var(--tw-bg-opacity,1))}input:checked+.\[input\:checked\+\&\]\:bg-gray-400{--tw-bg-opacity:1;background-color:rgba(var(--gray-400),var(--tw-bg-opacity,1))}input:checked+.\[input\:checked\+\&\]\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}input:checked+.\[input\:checked\+\&\]\:ring-0{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}input:checked+.\[input\:checked\+\&\]\:hover\:bg-custom-500:hover{--tw-bg-opacity:1;background-color:rgba(var(--c-500),var(--tw-bg-opacity,1))}input:checked+.\[input\:checked\+\&\]\:hover\:bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgba(var(--gray-300),var(--tw-bg-opacity,1))}input:checked+.dark\:\[input\:checked\+\&\]\:bg-custom-500:is(.dark *){--tw-bg-opacity:1;background-color:rgba(var(--c-500),var(--tw-bg-opacity,1))}input:checked+.dark\:\[input\:checked\+\&\]\:bg-gray-600:is(.dark *){--tw-bg-opacity:1;background-color:rgba(var(--gray-600),var(--tw-bg-opacity,1))}input:checked+.dark\:\[input\:checked\+\&\]\:hover\:bg-custom-400:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgba(var(--c-400),var(--tw-bg-opacity,1))}input:checked+.dark\:\[input\:checked\+\&\]\:hover\:bg-gray-500:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgba(var(--gray-500),var(--tw-bg-opacity,1))}input:checked:focus-visible+.\[input\:checked\:focus-visible\+\&\]\:ring-custom-500\/50{--tw-ring-color:rgba(var(--c-500),0.5)}input:checked:focus-visible+.dark\:\[input\:checked\:focus-visible\+\&\]\:ring-custom-400\/50:is(.dark *){--tw-ring-color:rgba(var(--c-400),0.5)}input:focus-visible+.\[input\:focus-visible\+\&\]\:z-10{z-index:10}input:focus-visible+.\[input\:focus-visible\+\&\]\:ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}input:focus-visible+.\[input\:focus-visible\+\&\]\:ring-gray-950\/10{--tw-ring-color:rgba(var(--gray-950),0.1)}input:focus-visible+.dark\:\[input\:focus-visible\+\&\]\:ring-white\/20:is(.dark *){--tw-ring-color:hsla(0,0%,100%,.2)} \ No newline at end of file +*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border-width:0;border-style:solid;border-color:rgba(var(--gray-200),1)}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-family),ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:rgba(var(--gray-400),1)}input::placeholder,textarea::placeholder{opacity:1;color:rgba(var(--gray-400),1)}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}[multiple],[type=date],[type=datetime-local],[type=email],[type=month],[type=number],[type=password],[type=search],[type=tel],[type=text],[type=time],[type=url],[type=week],input:where(:not([type])),select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:rgba(var(--gray-500),var(--tw-border-opacity,1));border-width:1px;border-radius:0;padding:.5rem .75rem;font-size:1rem;line-height:1.5rem;--tw-shadow:0 0 #0000}[multiple]:focus,[type=date]:focus,[type=datetime-local]:focus,[type=email]:focus,[type=month]:focus,[type=number]:focus,[type=password]:focus,[type=search]:focus,[type=tel]:focus,[type=text]:focus,[type=time]:focus,[type=url]:focus,[type=week]:focus,input:where(:not([type])):focus,select:focus,textarea:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow);border-color:#2563eb}input::-moz-placeholder,textarea::-moz-placeholder{color:rgba(var(--gray-500),var(--tw-text-opacity,1));opacity:1}input::placeholder,textarea::placeholder{color:rgba(var(--gray-500),var(--tw-text-opacity,1));opacity:1}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-date-and-time-value{min-height:1.5em;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-meridiem-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-year-field{padding-top:0;padding-bottom:0}select{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3E%3C/svg%3E");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;-webkit-print-color-adjust:exact;print-color-adjust:exact}[multiple],[size]:where(select:not([size="1"])){background-image:none;background-position:0 0;background-repeat:unset;background-size:initial;padding-right:.75rem;-webkit-print-color-adjust:unset;print-color-adjust:unset}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:0;-webkit-print-color-adjust:exact;print-color-adjust:exact;display:inline-block;vertical-align:middle;background-origin:border-box;-webkit-user-select:none;-moz-user-select:none;user-select:none;flex-shrink:0;height:1rem;width:1rem;color:#2563eb;background-color:#fff;border-color:rgba(var(--gray-500),var(--tw-border-opacity,1));border-width:1px;--tw-shadow:0 0 #0000}[type=checkbox]{border-radius:0}[type=radio]{border-radius:100%}[type=checkbox]:focus,[type=radio]:focus{outline:2px solid transparent;outline-offset:2px;--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:2px;--tw-ring-offset-color:#fff;--tw-ring-color:#2563eb;--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}[type=checkbox]:checked,[type=radio]:checked{border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}[type=checkbox]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0'/%3E%3C/svg%3E")}@media (forced-colors:active){[type=checkbox]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=radio]:checked{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='3'/%3E%3C/svg%3E")}@media (forced-colors:active){[type=radio]:checked{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:checked:focus,[type=checkbox]:checked:hover,[type=radio]:checked:focus,[type=radio]:checked:hover{border-color:transparent;background-color:currentColor}[type=checkbox]:indeterminate{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3E%3Cpath stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3E%3C/svg%3E");border-color:transparent;background-color:currentColor;background-size:100% 100%;background-position:50%;background-repeat:no-repeat}@media (forced-colors:active){[type=checkbox]:indeterminate{-webkit-appearance:auto;-moz-appearance:auto;appearance:auto}}[type=checkbox]:indeterminate:focus,[type=checkbox]:indeterminate:hover{border-color:transparent;background-color:currentColor}[type=file]{background:unset;border-color:inherit;border-width:0;border-radius:0;padding:0;font-size:unset;line-height:inherit}[type=file]:focus{outline:1px solid ButtonText;outline:1px auto -webkit-focus-ring-color}:root.dark{color-scheme:dark}[data-field-wrapper]{scroll-margin-top:8rem}.bg-primary-50{--tw-bg-opacity:1;background-color:rgba(var(--primary-50),var(--tw-bg-opacity,1))}.\!ring-0{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color)!important;--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color)!important;box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)!important}.ring-gray-100{--tw-ring-opacity:1;--tw-ring-color:rgba(var(--gray-100),var(--tw-ring-opacity,1))}.dark\:bg-primary-950:is(.dark *){--tw-bg-opacity:1;background-color:rgba(var(--primary-950),var(--tw-bg-opacity,1))}:checked+*>.\[\:checked\+\*\>\&\]\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}input:checked+.\[input\:checked\+\&\]\:bg-custom-600{--tw-bg-opacity:1;background-color:rgba(var(--c-600),var(--tw-bg-opacity,1))}input:checked+.\[input\:checked\+\&\]\:bg-gray-400{--tw-bg-opacity:1;background-color:rgba(var(--gray-400),var(--tw-bg-opacity,1))}input:checked+.\[input\:checked\+\&\]\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}input:checked+.\[input\:checked\+\&\]\:ring-0{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}input:checked+.\[input\:checked\+\&\]\:hover\:bg-custom-500:hover{--tw-bg-opacity:1;background-color:rgba(var(--c-500),var(--tw-bg-opacity,1))}input:checked+.\[input\:checked\+\&\]\:hover\:bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgba(var(--gray-300),var(--tw-bg-opacity,1))}input:checked+.dark\:\[input\:checked\+\&\]\:bg-custom-500:is(.dark *){--tw-bg-opacity:1;background-color:rgba(var(--c-500),var(--tw-bg-opacity,1))}input:checked+.dark\:\[input\:checked\+\&\]\:bg-gray-600:is(.dark *){--tw-bg-opacity:1;background-color:rgba(var(--gray-600),var(--tw-bg-opacity,1))}input:checked+.dark\:\[input\:checked\+\&\]\:hover\:bg-custom-400:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgba(var(--c-400),var(--tw-bg-opacity,1))}input:checked+.dark\:\[input\:checked\+\&\]\:hover\:bg-gray-500:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgba(var(--gray-500),var(--tw-bg-opacity,1))}input:checked:focus-visible+.\[input\:checked\:focus-visible\+\&\]\:ring-custom-500\/50{--tw-ring-color:rgba(var(--c-500),0.5)}input:checked:focus-visible+.dark\:\[input\:checked\:focus-visible\+\&\]\:ring-custom-400\/50:is(.dark *){--tw-ring-color:rgba(var(--c-400),0.5)}input:focus-visible+.\[input\:focus-visible\+\&\]\:z-10{z-index:10}input:focus-visible+.\[input\:focus-visible\+\&\]\:ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}input:focus-visible+.\[input\:focus-visible\+\&\]\:ring-gray-950\/10{--tw-ring-color:rgba(var(--gray-950),0.1)}input:focus-visible+.dark\:\[input\:focus-visible\+\&\]\:ring-white\/20:is(.dark *){--tw-ring-color:hsla(0,0%,100%,.2)} \ No newline at end of file diff --git a/resources/views/components/emoji-selector.blade.php b/resources/views/components/emoji-selector.blade.php new file mode 100644 index 0000000..8ba18d7 --- /dev/null +++ b/resources/views/components/emoji-selector.blade.php @@ -0,0 +1 @@ +ALL EMOJIS HERE \ No newline at end of file diff --git a/resources/views/livewire/add-comment.blade.php b/resources/views/livewire/add-comment.blade.php new file mode 100644 index 0000000..c1e100e --- /dev/null +++ b/resources/views/livewire/add-comment.blade.php @@ -0,0 +1,26 @@ +
+ @if($this->addingComment) +
+ {{ $this->form }} + + + Submit + + + Cancel + +
+ @else + + + + @endif + +
diff --git a/resources/views/livewire/comment-card.blade.php b/resources/views/livewire/comment-card.blade.php new file mode 100644 index 0000000..b2cf898 --- /dev/null +++ b/resources/views/livewire/comment-card.blade.php @@ -0,0 +1,6 @@ +
+ {{ $comment->commentator }} +
+ {!! e(new \Illuminate\Support\HtmlString($this->comment?->body)) !!} +
+
diff --git a/resources/views/livewire/comments.blade.php b/resources/views/livewire/comments.blade.php new file mode 100644 index 0000000..e13d723 --- /dev/null +++ b/resources/views/livewire/comments.blade.php @@ -0,0 +1,14 @@ + + + {{ __('Comments') }} + + + Refresh + + + @foreach($this->comments as $comment) + + @endforeach + diff --git a/src/Concerns/HasComments.php b/src/Concerns/HasComments.php index 6efb8e9..465cc9a 100644 --- a/src/Concerns/HasComments.php +++ b/src/Concerns/HasComments.php @@ -3,8 +3,11 @@ namespace Coolsam\NestedComments\Concerns; use Coolsam\NestedComments\Models\Comment; +use Coolsam\NestedComments\NestedComments; +use Exception; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphMany; +use Illuminate\Support\Collection; /** * @mixin Model @@ -21,7 +24,7 @@ public function getCommentsCountAttribute(): int return $this->comments()->count(); } - public function getCommentsTree($offset = null, $limit = null, $columns = ['*']) + public function getCommentsTree($offset = null, $limit = null, $columns = ['*']): Collection { $query = $this->comments() ->getQuery() @@ -33,24 +36,32 @@ public function getCommentsTree($offset = null, $limit = null, $columns = ['*']) $query->limit($limit); } - $columns = ['id', 'parent_id', '_lft', '_rgt', ...$columns]; + if (filled($columns) && $columns[0] !== '*') { + $columns = ['id', 'parent_id', '_lft', '_rgt', ...$columns]; + } - return collect($query->get($columns)->map(function (Comment $comment) use ($columns) { + return $query->get($columns)->map(function (Comment $comment) use ($columns) { $descendants = $comment->getDescendants($columns); - return collect($comment->toArray())->put('descendants', $descendants->toArray()); - })->toArray()); + $comment->setAttribute('replies', $descendants); + + return $comment; + }); } /** - * @throws \Exception + * @throws Exception */ - public function addComment(string $comment, mixed $parentId = null) + public function comment(string $comment, mixed $parentId = null, ?string $name = null) { $allowGuest = config('nested-comments.allow-guest-comments', false); if (! $allowGuest && ! auth()->check()) { - throw new \Exception('You must be logged in to comment.'); + throw new Exception('You must be logged in to comment.'); } - + if ($name) { + app(NestedComments::class)->setGuestName($name); + } + $guestId = app(NestedComments::class)->getGuestId(); + $guestName = app(NestedComments::class)->getGuestName(); if ($allowGuest && ! auth()->check()) { $userId = null; } else { @@ -63,21 +74,62 @@ public function addComment(string $comment, mixed $parentId = null) 'commentable_id' => $this->getKey(), 'commentable_type' => $this->getMorphClass(), 'parent_id' => $parentId, + 'guest_id' => $guestId, + 'guest_name' => $guestName, 'ip_address' => request()->ip(), ]); } /** - * @throws \Exception + * @throws Exception + */ + public function editComment(Comment $comment, string $body, ?string $name = null): ?bool + { + $allowGuest = config('nested-comments.allow-guest-comments', false); + + if (! auth()->check() && ! $allowGuest) { + throw new Exception('You must be logged in to edit your comment.'); + } + + if ($name) { + app(NestedComments::class)->setGuestName($name); + } + + if (\auth()->check() && $comment->getAttribute('user_id') !== auth()->id()) { + throw new Exception('You are not authorized to edit this comment.'); + } + + if ($allowGuest && ! auth()->check()) { + $guestId = app(NestedComments::class)->getGuestId(); + if ($comment->getAttribute('guest_id') !== $guestId) { + throw new Exception('You are not authorized to edit this comment.'); + } + } + $guestName = app(NestedComments::class)->getGuestName(); + + return $comment->update(['body' => $body, 'guest_name' => $guestName, 'ip_address' => request()->ip()]); + } + + /** + * @throws Exception */ public function deleteComment(Comment $comment): ?bool { - if (! auth()->check()) { - throw new \Exception('You must be logged in to delete your comment.'); + $allowGuest = config('nested-comments.allow-guest-comments', false); + + if (! auth()->check() && ! $allowGuest) { + throw new Exception('You must be logged in to edit your comment.'); } - if ($comment->getAttribute('user_id') !== auth()->id()) { - throw new \Exception('You are not authorized to delete this comment.'); + if (\auth()->check() && $comment->getAttribute('user_id') !== auth()->id()) { + throw new Exception('You are not authorized to edit this comment.'); + } + + if ($allowGuest && ! auth()->check()) { + $guestId = app(NestedComments::class)->getGuestId(); + if ($comment->getAttribute('guest_id') !== $guestId) { + throw new Exception('You are not authorized to edit this comment.'); + } } return $comment->delete(); diff --git a/src/Concerns/HasReactions.php b/src/Concerns/HasReactions.php index e37f429..18e8462 100644 --- a/src/Concerns/HasReactions.php +++ b/src/Concerns/HasReactions.php @@ -3,6 +3,7 @@ namespace Coolsam\NestedComments\Concerns; use Coolsam\NestedComments\Models\Reaction; +use Coolsam\NestedComments\NestedComments; use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Support\Facades\Auth; @@ -21,7 +22,7 @@ public function getReactionsCountAttribute(): int /** * @throws \Throwable */ - public function toggleReaction(string $emoji): Reaction | int + public function react(string $emoji): Reaction | int { $existing = $this->getExistingReaction($emoji); if ($existing) { @@ -33,9 +34,12 @@ public function toggleReaction(string $emoji): Reaction | int if (! $this->isAllowed($emoji)) { throw new \Exception('This reaction is not allowed.'); } + return $this->reactions()->create([ 'user_id' => Auth::check() ? Auth::id() : null, 'emoji' => $emoji, + 'guest_id' => app(NestedComments::class)->getGuestId(), + 'guest_name' => app(NestedComments::class)->getGuestName(), 'ip_address' => request()->ip(), ]); } @@ -53,14 +57,18 @@ protected function getExistingReaction(string $emoji): ?Reaction } if ($allowGuest && ! Auth::check()) { + $guestId = app(NestedComments::class)->getGuestId(); + if (! $guestId) { + throw new \Exception('Sorry, your guest session has not bee setup.'); + } $existingQuery = $this->reactions() - ->where('ip_address', '=', request()->ip()); + ->where('guest_id', '=', $guestId); } else { $existingQuery = $this->reactions() ->where('user_id', '=', Auth::id()); - } + if ($allowMultiple) { $existingQuery->where('emoji', '=', $emoji); } @@ -74,6 +82,7 @@ public function isAllowed(string $emoji): bool if (empty($allowed)) { return true; } + return in_array($emoji, $allowed); } } diff --git a/src/Http/Middleware/GuestCommentatorMiddleware.php b/src/Http/Middleware/GuestCommentatorMiddleware.php new file mode 100644 index 0000000..2028e9a --- /dev/null +++ b/src/Http/Middleware/GuestCommentatorMiddleware.php @@ -0,0 +1,24 @@ +setOrGetGuestId(); + app(NestedComments::class)->setOrGetGuestName(); + + return $next($request); + } +} diff --git a/src/Livewire/AddComment.php b/src/Livewire/AddComment.php new file mode 100644 index 0000000..908ab2b --- /dev/null +++ b/src/Livewire/AddComment.php @@ -0,0 +1,91 @@ +commentable = $commentable; + $this->replyTo = $replyTo; + $this->form->fill(); + } + + /** + * @return Model&HasComments + */ + public function getCommentable(): Model + { + if (! $this->commentable) { + throw new \Error('The $commentable property is required.'); + } + + return $this->commentable; + } + + public function form(Form $form): Form + { + return $form + ->schema([ + Forms\Components\RichEditor::make('body')->required()->autofocus(true), + ]) + ->statePath('data') + ->model(config('nested-comments.models.comment', Comment::class)); + } + + /** + * @throws \Exception + */ + public function create(): void + { + $data = $this->form->getState(); + + $commentable = $this->getCommentable(); + + /** + * @phpstan-ignore-next-line + */ + $record = $commentable->comment(comment: $data['body'], parentId: $this->replyTo?->getKey()); + $this->dispatch('refresh'); + $this->showForm(false); + } + + public function render(): View + { + $namespace = NestedCommentsServiceProvider::$viewNamespace; + return view("$namespace::livewire.add-comment"); + } + + public function showForm(bool $adding): void + { + $this->form->fill(); + $this->addingComment = $adding; + } +} diff --git a/src/Livewire/CommentCard.php b/src/Livewire/CommentCard.php new file mode 100644 index 0000000..e913444 --- /dev/null +++ b/src/Livewire/CommentCard.php @@ -0,0 +1,27 @@ +comment = $comment; + } + + public function render() + { + $namespace = NestedCommentsServiceProvider::$viewNamespace; + return view("$namespace::livewire.comment-card"); + } +} diff --git a/src/Livewire/Comments.php b/src/Livewire/Comments.php new file mode 100644 index 0000000..ec764f6 --- /dev/null +++ b/src/Livewire/Comments.php @@ -0,0 +1,55 @@ + 'refreshComments', + ]; + + /** + * @var Collection + */ + public Collection $comments; + + public function mount(): void + { + $this->comments = collect(); + if (! $this->record) { + throw new \Error('Record model (Commentable) is required'); + } + + if (! app(NestedComments::class)->classHasTrait($this->record, HasComments::class)) { + throw new \Error('Record model must use the HasComments trait'); + } + + $this->refreshComments(); + } + + public function refreshComments(): void + { + $this->record = $this->record?->newQuery()->find($this->record->getKey()); + $this->comments = $this->record?->getCommentsTree(); + } + + public function render() + { + $namespace = NestedCommentsServiceProvider::$viewNamespace; + + return view($namespace . '::livewire.comments'); + } +} diff --git a/src/Models/Comment.php b/src/Models/Comment.php index fb2ac74..9004ed9 100644 --- a/src/Models/Comment.php +++ b/src/Models/Comment.php @@ -4,6 +4,7 @@ use Coolsam\NestedComments\Concerns\HasReactions; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphTo; use Kalnoy\Nestedset\NodeTrait; @@ -18,4 +19,17 @@ public function commentable(): MorphTo { return $this->morphTo('commentable'); } + + public function user(): BelongsTo + { + return $this->belongsTo(config('nested-comments.models.user', config('auth.providers.users.model', 'App\\Models\\User')), 'user_id'); + } + + public function getCommentatorAttribute() + { + if ($this->user) { + return call_user_func(config('nested-comments.closures.getUserNameUsing'), $this->user); + } + return $this->getAttribute('guest_name'); + } } diff --git a/src/NestedComments.php b/src/NestedComments.php index db38518..f0d7a47 100644 --- a/src/NestedComments.php +++ b/src/NestedComments.php @@ -2,4 +2,57 @@ namespace Coolsam\NestedComments; -class NestedComments {} +use Illuminate\Contracts\Auth\Authenticatable; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Str; + +class NestedComments +{ + const GUEST_ID_FIELD = 'guest_commentator_id'; + + const GUEST_NAME_FIELD = 'guest_commentator_name'; + + public function getGuestId(): ?string + { + return session(self::GUEST_ID_FIELD); + } + + public function getGuestName(): string + { + if (Auth::check()) { + return call_user_func(config('nested-comments.closures.getUserNameUsing', fn (Authenticatable | Model $user) => $user->getAttribute('name')), Auth::user()); + } + + return session(self::GUEST_NAME_FIELD, 'Guest'); + } + + public function setGuestName(string $name): void + { + session([self::GUEST_NAME_FIELD => $name]); + } + + public function setOrGetGuestId() + { + $id = Str::ulid()->toString(); + if (! session()->has(self::GUEST_ID_FIELD)) { + session([self::GUEST_ID_FIELD => $id]); + } + + return session(self::GUEST_ID_FIELD); + } + + public function setOrGetGuestName() + { + if (! session()->has(self::GUEST_NAME_FIELD)) { + session([self::GUEST_NAME_FIELD => 'Guest']); + } + + return session(self::GUEST_NAME_FIELD); + } + + public function classHasTrait(object | string $classInstance, string $trait): bool + { + return in_array($trait, class_uses_recursive($classInstance)); + } +} diff --git a/src/NestedCommentsServiceProvider.php b/src/NestedCommentsServiceProvider.php index 0cc263c..494cc50 100644 --- a/src/NestedCommentsServiceProvider.php +++ b/src/NestedCommentsServiceProvider.php @@ -3,6 +3,10 @@ namespace Coolsam\NestedComments; use Coolsam\NestedComments\Commands\NestedCommentsCommand; +use Coolsam\NestedComments\Http\Middleware\GuestCommentatorMiddleware; +use Coolsam\NestedComments\Livewire\AddComment; +use Coolsam\NestedComments\Livewire\CommentCard; +use Coolsam\NestedComments\Livewire\Comments; use Coolsam\NestedComments\Testing\TestsNestedComments; use Filament\Support\Assets\AlpineComponent; use Filament\Support\Assets\Asset; @@ -12,6 +16,7 @@ use Filament\Support\Facades\FilamentIcon; use Illuminate\Filesystem\Filesystem; use Livewire\Features\SupportTesting\Testable; +use Livewire\Livewire; use Spatie\LaravelPackageTools\Commands\InstallCommand; use Spatie\LaravelPackageTools\Package; use Spatie\LaravelPackageTools\PackageServiceProvider; @@ -56,13 +61,25 @@ public function configurePackage(Package $package): void if (file_exists($package->basePath('/../resources/views'))) { $package->hasViews(static::$viewNamespace); } + if (file_exists($package->basePath('/../resources/views/components'))) { + $package->hasViewComponents(static::$viewNamespace); + } } public function packageRegistered(): void {} + public function bootingPackage(): void + { + $this->app['router']->pushMiddlewareToGroup('web', GuestCommentatorMiddleware::class); + } + public function packageBooted(): void { $this->registerPolicies(); + + // Livewire components + $this->registerLivewireComponents(); + // Asset Registration FilamentAsset::register( $this->getAssets(), @@ -167,4 +184,25 @@ protected function registerPolicies(): void \Gate::policy($modelClass, $policy); } } + + protected function registerLivewireComponents() + { + $namespace = static::$viewNamespace; + $components = $this->getLivewireComponents(); + if (empty($components)) { + return; + } + foreach ($components as $name => $component) { + Livewire::component("$namespace::$name", $component); + } + } + + protected function getLivewireComponents(): array + { + return [ + 'comments' => Comments::class, + 'comment-card' => CommentCard::class, + 'add-comment' => AddComment::class, + ]; + } } diff --git a/tailwind.config.js b/tailwind.config.js index 7ae2ac4..c6c8850 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -3,8 +3,8 @@ const preset = require('./vendor/filament/filament/tailwind.config.preset') module.exports = { presets: [preset], content: [ - './app/Filament/**/*.php', - './resources/views/filament/**/*.blade.php', + './src/**/*.php', + './resources/views/**/*.blade.php', './vendor/filament/**/*.blade.php', ], }