diff --git a/app/Http/Controllers/NotificationsController.php b/app/Http/Controllers/NotificationsController.php new file mode 100644 index 0000000..0b8e3d9 --- /dev/null +++ b/app/Http/Controllers/NotificationsController.php @@ -0,0 +1,13 @@ +notifications()->paginate(20); + \Auth::user()->markAsRead(); + return view('notifications.index', compact('notifications')); + } +} diff --git a/app/Models/Topic.php b/app/Models/Topic.php index 8be0669..a126e1e 100644 --- a/app/Models/Topic.php +++ b/app/Models/Topic.php @@ -7,7 +7,7 @@ class Topic extends Model protected $fillable = ['title', 'category_id', 'body', 'excerpt', 'slug']; - public function link($params = []) + public function link(...$params) { return route('topics.show', array_merge([$this->id, $this->slug], $params)); } diff --git a/app/Models/User.php b/app/Models/User.php index 3eb3ca7..67cd6b7 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -4,10 +4,13 @@ namespace App\Models; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Illuminate\Support\Facades\Auth; class User extends Authenticatable { - use Notifiable; + use Notifiable { + notify as protected laravelNotify; + } /** * The attributes that are mass assignable. @@ -46,4 +49,20 @@ class User extends Authenticatable { return $this->hasMany(Topic::class); } + + public function notify($instance) + { + if ($this->id == Auth::id()) { + return; + } + $this->increment('notification_count'); + $this->laravelNotify($instance); + } + + public function markAsRead() + { + $this->notification_count = 0; + $this->save(); + $this->unreadNotifications->markAsRead(); + } } diff --git a/app/Notifications/TopicReplied.php b/app/Notifications/TopicReplied.php new file mode 100644 index 0000000..75701f7 --- /dev/null +++ b/app/Notifications/TopicReplied.php @@ -0,0 +1,81 @@ +replay = $reply; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable) + { + return ['database']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable) + { + return (new MailMessage) + ->line('The introduction to the notification.') + ->action('Notification Action', url('/')) + ->line('Thank you for using our application!'); + } + + public function toDataBase($notifiable) + { + $topic = $this->replay->topic; + + $link = $topic->link('#reply', $this->replay->id); + + return [ + 'reply_id' => $this->replay->id, + 'reply_content' => $this->replay->content, + 'user_id' => $this->replay->user->id, + 'user_name' => $this->replay->user->name, + 'user_avatar' => $this->replay->user->avatar, + 'topic_link' => $link, + 'topic_id' => $topic->id, + 'topic_title' => $topic->title + ]; + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable) + { + return [ + // + ]; + } +} diff --git a/app/Observers/ReplyObserver.php b/app/Observers/ReplyObserver.php index aee8d0b..61c35f1 100644 --- a/app/Observers/ReplyObserver.php +++ b/app/Observers/ReplyObserver.php @@ -3,6 +3,7 @@ namespace App\Observers; use App\Models\Reply; +use App\Notifications\TopicReplied; // creating, created, updating, updated, saving, // saved, deleting, deleted, restoring, restored @@ -21,6 +22,10 @@ class ReplyObserver public function created(Reply $reply) { + $topic = $reply->topic; $reply->topic->increment('reply_count', 1); + if (!$reply->user->isAuthorOf($topic)) { + $topic->user->notify(new TopicReplied($reply)); + } } } \ No newline at end of file diff --git a/database/migrations/2018_01_27_165522_create_notifications_table.php b/database/migrations/2018_01_27_165522_create_notifications_table.php new file mode 100644 index 0000000..9797596 --- /dev/null +++ b/database/migrations/2018_01_27_165522_create_notifications_table.php @@ -0,0 +1,35 @@ +uuid('id')->primary(); + $table->string('type'); + $table->morphs('notifiable'); + $table->text('data'); + $table->timestamp('read_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('notifications'); + } +} diff --git a/database/migrations/2018_01_27_165914_add_notification_cout_to_users_table.php b/database/migrations/2018_01_27_165914_add_notification_cout_to_users_table.php new file mode 100644 index 0000000..e010b66 --- /dev/null +++ b/database/migrations/2018_01_27_165914_add_notification_cout_to_users_table.php @@ -0,0 +1,32 @@ +integer('notification_count')->unsigned()->default(0); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('notification_count'); + }); + } +} diff --git a/public/css/app.css b/public/css/app.css index e50247b..ec8d177 100644 --- a/public/css/app.css +++ b/public/css/app.css @@ -8975,5 +8975,15 @@ body { color: #b3b3b3; } -/* Topic Index Page */ +.notifications-badge { + margin-top: -1; +} + +.notifications-badge .badge-fade { + background-color: #EBE8E8; +} + +.notifications-badge .badge-hint { + background-color: #d15b47 !important; +} diff --git a/resources/assets/sass/app.scss b/resources/assets/sass/app.scss index b2f949a..2f593c8 100644 --- a/resources/assets/sass/app.scss +++ b/resources/assets/sass/app.scss @@ -174,4 +174,14 @@ body { } } -/* Topic Index Page */ \ No newline at end of file +.notifications-badge { + margin-top: -1; + + .badge-fade { + background-color: #EBE8E8; + } + + .badge-hint { + background-color: #d15b47 !important;; + } +} \ No newline at end of file diff --git a/resources/views/layouts/_header.blade.php b/resources/views/layouts/_header.blade.php index ddc3366..59bddb1 100644 --- a/resources/views/layouts/_header.blade.php +++ b/resources/views/layouts/_header.blade.php @@ -45,6 +45,15 @@ +