From 9d149e4d36f0f15c18053dda29b2974a5994f660 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Wed, 2 Aug 2023 13:14:00 +0100 Subject: [PATCH] Notifications: Linked watch functionality to UI Got watch system working to an initial base state. Moved some existing logic where it makes sense. --- app/Activity/Controllers/WatchController.php | 12 +-- app/Activity/Models/Watch.php | 21 +--- app/Activity/Tools/UserWatchOptions.php | 98 +++++++++++++++++++ app/Entities/Controllers/BookController.php | 2 + lang/en/activities.php | 3 + lang/en/entities.php | 4 + resources/icons/watch-ignore.svg | 1 + resources/icons/watch.svg | 1 - resources/views/books/show.blade.php | 4 +- resources/views/entities/meta.blade.php | 21 ++-- .../views/entities/watch-action.blade.php | 8 +- .../views/entities/watch-controls.blade.php | 39 ++++---- routes/web.php | 3 + 13 files changed, 161 insertions(+), 56 deletions(-) create mode 100644 app/Activity/Tools/UserWatchOptions.php create mode 100644 resources/icons/watch-ignore.svg diff --git a/app/Activity/Controllers/WatchController.php b/app/Activity/Controllers/WatchController.php index f9e8a4e3d..a297aaafc 100644 --- a/app/Activity/Controllers/WatchController.php +++ b/app/Activity/Controllers/WatchController.php @@ -3,6 +3,7 @@ namespace BookStack\Activity\Controllers; use BookStack\Activity\Models\Watch; +use BookStack\Activity\Tools\UserWatchOptions; use BookStack\App\Model; use BookStack\Entities\Models\Entity; use BookStack\Http\Controller; @@ -19,13 +20,12 @@ class WatchController extends Controller ]); $watchable = $this->getValidatedModelFromRequest($request); - $newLevel = Watch::optionNameToLevel($requestData['level']); + $watchOptions = new UserWatchOptions(user()); + $watchOptions->updateEntityWatchLevel($watchable, $requestData['level']); - if ($newLevel < 0) { - // TODO - Delete - } else { - // TODO - Upsert - } + $this->showSuccessNotification(trans('activities.watch_update_level_notification')); + + return redirect()->back(); } /** diff --git a/app/Activity/Models/Watch.php b/app/Activity/Models/Watch.php index 6e0e1f787..6637c9655 100644 --- a/app/Activity/Models/Watch.php +++ b/app/Activity/Models/Watch.php @@ -18,13 +18,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; */ class Watch extends Model { - protected static array $levelByOption = [ - 'default' => -1, - 'ignore' => 0, - 'new' => 1, - 'updates' => 2, - 'comments' => 3, - ]; + protected $guarded = []; public function watchable() { @@ -36,17 +30,4 @@ class Watch extends Model return $this->hasMany(JointPermission::class, 'entity_id', 'watchable_id') ->whereColumn('favourites.watchable_type', '=', 'joint_permissions.entity_type'); } - - /** - * @return string[] - */ - public static function getAvailableOptionNames(): array - { - return array_keys(static::$levelByOption); - } - - public static function optionNameToLevel(string $option): int - { - return static::$levelByOption[$option] ?? -1; - } } diff --git a/app/Activity/Tools/UserWatchOptions.php b/app/Activity/Tools/UserWatchOptions.php new file mode 100644 index 000000000..0607d60a3 --- /dev/null +++ b/app/Activity/Tools/UserWatchOptions.php @@ -0,0 +1,98 @@ + -1, + 'ignore' => 0, + 'new' => 1, + 'updates' => 2, + 'comments' => 3, + ]; + + public function __construct( + protected User $user, + ) { + } + + public function canWatch(): bool + { + return $this->user->can('receive-notifications') && !$this->user->isDefault(); + } + + public function getEntityWatchLevel(Entity $entity): string + { + $levelValue = $this->entityQuery($entity)->first(['level'])->level ?? -1; + return $this->levelValueToName($levelValue); + } + + public function isWatching(Entity $entity): bool + { + return $this->entityQuery($entity)->exists(); + } + + public function updateEntityWatchLevel(Entity $entity, string $level): void + { + $levelValue = $this->levelNameToValue($level); + if ($levelValue < 0) { + $this->removeForEntity($entity); + return; + } + + $this->updateForEntity($entity, $levelValue); + } + + protected function updateForEntity(Entity $entity, int $levelValue): void + { + Watch::query()->updateOrCreate([ + 'watchable_id' => $entity->id, + 'watchable_type' => $entity->getMorphClass(), + 'user_id' => $this->user->id, + ], [ + 'level' => $levelValue, + ]); + } + + protected function removeForEntity(Entity $entity): void + { + $this->entityQuery($entity)->delete(); + } + + protected function entityQuery(Entity $entity): Builder + { + return Watch::query()->where('watchable_id', '=', $entity->id) + ->where('watchable_type', '=', $entity->getMorphClass()) + ->where('user_id', '=', $this->user->id); + } + + /** + * @return string[] + */ + public static function getAvailableLevelNames(): array + { + return array_keys(static::$levelByName); + } + + protected static function levelNameToValue(string $level): int + { + return static::$levelByName[$level] ?? -1; + } + + protected static function levelValueToName(int $level): string + { + foreach (static::$levelByName as $name => $value) { + if ($level === $value) { + return $name; + } + } + + return 'default'; + } +} diff --git a/app/Entities/Controllers/BookController.php b/app/Entities/Controllers/BookController.php index dcd1af5a1..3ce71c38a 100644 --- a/app/Entities/Controllers/BookController.php +++ b/app/Entities/Controllers/BookController.php @@ -5,6 +5,7 @@ namespace BookStack\Entities\Controllers; use BookStack\Activity\ActivityQueries; use BookStack\Activity\ActivityType; use BookStack\Activity\Models\View; +use BookStack\Activity\Tools\UserWatchOptions; use BookStack\Entities\Models\Bookshelf; use BookStack\Entities\Repos\BookRepo; use BookStack\Entities\Tools\BookContents; @@ -138,6 +139,7 @@ class BookController extends Controller 'current' => $book, 'bookChildren' => $bookChildren, 'bookParentShelves' => $bookParentShelves, + 'watchOptions' => new UserWatchOptions(user()), 'activity' => $activities->entityActivity($book, 20, 1), 'referenceCount' => $this->referenceFetcher->getPageReferenceCountToEntity($book), ]); diff --git a/lang/en/activities.php b/lang/en/activities.php index a96299ea7..d5b55c03d 100644 --- a/lang/en/activities.php +++ b/lang/en/activities.php @@ -58,6 +58,9 @@ return [ 'favourite_add_notification' => '":name" has been added to your favourites', 'favourite_remove_notification' => '":name" has been removed from your favourites', + // Watching + 'watch_update_level_notification' => 'Watch preferences successfully updated', + // Auth 'auth_login' => 'logged in', 'auth_register' => 'registered as new user', diff --git a/lang/en/entities.php b/lang/en/entities.php index 80b9142f5..87c09634b 100644 --- a/lang/en/entities.php +++ b/lang/en/entities.php @@ -417,4 +417,8 @@ return [ 'watch_title_comments' => 'All Page Updates & Comments', 'watch_desc_comments' => 'Notify upon all new pages, page changes and new comments.', 'watch_change_default' => 'Change default notification preferences', + 'watch_detail_ignore' => 'Ignoring notifications', + 'watch_detail_new' => 'Watching for new pages', + 'watch_detail_updates' => 'Watching new pages and updates', + 'watch_detail_comments' => 'Watching new pages, updates & comments', ]; diff --git a/resources/icons/watch-ignore.svg b/resources/icons/watch-ignore.svg new file mode 100644 index 000000000..2c6ffc24a --- /dev/null +++ b/resources/icons/watch-ignore.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/icons/watch.svg b/resources/icons/watch.svg index c95c8875c..0be661912 100644 --- a/resources/icons/watch.svg +++ b/resources/icons/watch.svg @@ -1,4 +1,3 @@ - \ No newline at end of file diff --git a/resources/views/books/show.blade.php b/resources/views/books/show.blade.php index 305a65132..5c8b0a772 100644 --- a/resources/views/books/show.blade.php +++ b/resources/views/books/show.blade.php @@ -142,7 +142,9 @@ @if(signedInUser()) @include('entities.favourite-action', ['entity' => $book]) @endif - @include('entities.watch-action', ['entity' => $book]) + @if($watchOptions->canWatch() && !$watchOptions->isWatching($book)) + @include('entities.watch-action', ['entity' => $book]) + @endif @if(userCan('content-export')) @include('entities.export-menu', ['entity' => $book]) @endif diff --git a/resources/views/entities/meta.blade.php b/resources/views/entities/meta.blade.php index 0bda12938..6783902a1 100644 --- a/resources/views/entities/meta.blade.php +++ b/resources/views/entities/meta.blade.php @@ -69,12 +69,17 @@ @endif - + @if($watchOptions?->canWatch() && $watchOptions->isWatching($entity)) + @php + $watchLevel = $watchOptions->getEntityWatchLevel($entity); + @endphp + + @endif \ No newline at end of file diff --git a/resources/views/entities/watch-action.blade.php b/resources/views/entities/watch-action.blade.php index dd626a0ef..34e287804 100644 --- a/resources/views/entities/watch-action.blade.php +++ b/resources/views/entities/watch-action.blade.php @@ -1,8 +1,12 @@ -
+ {{ csrf_field() }} + {{ method_field('PUT') }} - diff --git a/resources/views/entities/watch-controls.blade.php b/resources/views/entities/watch-controls.blade.php index 8d6bfed00..e02db5800 100644 --- a/resources/views/entities/watch-controls.blade.php +++ b/resources/views/entities/watch-controls.blade.php @@ -1,27 +1,30 @@ - -{{-- {{ method_field('PUT') }}--}} + + {{ method_field('PUT') }} {{ csrf_field() }}