Browse Source

Add modlog notifications

Daniel Supernault 5 years ago
parent
commit
51642fc40d

+ 66 - 0
app/Observers/ModLogObserver.php

@@ -0,0 +1,66 @@
+<?php
+
+namespace App\Observers;
+
+use App\Notification;
+use App\ModLog;
+use App\Services\ModLogService;
+use Log;
+
+class ModLogObserver
+{
+    /**
+     * Handle the mod log "created" event.
+     *
+     * @param  \App\ModLog  $modLog
+     * @return void
+     */
+    public function created(ModLog $modLog)
+    {
+        ModLogService::boot()->load($modLog)->fanout();
+    }
+
+    /**
+     * Handle the mod log "updated" event.
+     *
+     * @param  \App\ModLog  $modLog
+     * @return void
+     */
+    public function updated(ModLog $modLog)
+    {
+        ModLogService::boot()->load($modLog)->fanout();
+    }
+
+    /**
+     * Handle the mod log "deleted" event.
+     *
+     * @param  \App\ModLog  $modLog
+     * @return void
+     */
+    public function deleted(ModLog $modLog)
+    {
+        ModLogService::boot()->load($modLog)->unfanout();
+    }
+
+    /**
+     * Handle the mod log "restored" event.
+     *
+     * @param  \App\ModLog  $modLog
+     * @return void
+     */
+    public function restored(ModLog $modLog)
+    {
+        ModLogService::boot()->load($modLog)->fanout();
+    }
+
+    /**
+     * Handle the mod log "force deleted" event.
+     *
+     * @param  \App\ModLog  $modLog
+     * @return void
+     */
+    public function forceDeleted(ModLog $modLog)
+    {
+        ModLogService::boot()->load($modLog)->unfanout();
+    }
+}

+ 3 - 0
app/Providers/AppServiceProvider.php

@@ -5,6 +5,7 @@ namespace App\Providers;
 use App\Observers\{
 use App\Observers\{
     AvatarObserver,
     AvatarObserver,
     NotificationObserver,
     NotificationObserver,
+    ModLogObserver,
     StatusHashtagObserver,
     StatusHashtagObserver,
     UserObserver,
     UserObserver,
     UserFilterObserver,
     UserFilterObserver,
@@ -12,6 +13,7 @@ use App\Observers\{
 use App\{
 use App\{
     Avatar,
     Avatar,
     Notification,
     Notification,
+    ModLog,
     StatusHashtag,
     StatusHashtag,
     User,
     User,
     UserFilter
     UserFilter
@@ -35,6 +37,7 @@ class AppServiceProvider extends ServiceProvider
 
 
         Avatar::observe(AvatarObserver::class);
         Avatar::observe(AvatarObserver::class);
         Notification::observe(NotificationObserver::class);
         Notification::observe(NotificationObserver::class);
+        ModLog::observe(ModLogObserver::class);
         StatusHashtag::observe(StatusHashtagObserver::class);
         StatusHashtag::observe(StatusHashtagObserver::class);
         User::observe(UserObserver::class);
         User::observe(UserObserver::class);
         UserFilter::observe(UserFilterObserver::class);
         UserFilter::observe(UserFilterObserver::class);

+ 44 - 0
app/Services/ModLogService.php

@@ -2,7 +2,9 @@
 
 
 namespace App\Services;
 namespace App\Services;
 
 
+use Auth;
 use App\ModLog;
 use App\ModLog;
+use App\Notification;
 use App\User;
 use App\User;
 
 
 class ModLogService {
 class ModLogService {
@@ -95,4 +97,46 @@ class ModLogService {
 			return;
 			return;
 		}
 		}
 	}
 	}
+
+	public function load($modLog)
+	{
+		$this->log = $modLog;
+		return $this;
+	}
+
+	public function fanout()
+	{
+		$log = $this->log;
+
+		$msg = "{$log->user_username} commented on a modlog";
+		$rendered = "<span class='font-weight-bold'>{$log->user_username}</span> commented on a <a href='/i/admin/users/modlogs/{$log->user_id}}' class='font-weight-bold text-decoration-none'>modlog</a>";
+		$item_id = $log->id;
+		$item_type = 'App\ModLog';
+		$action = 'admin.user.modlog.comment';
+
+		$admins = User::whereNull('status')
+			->whereNotIn('id', [$log->user_id])
+			->whereIsAdmin(true)
+			->pluck('profile_id')
+			->toArray();
+
+		foreach($admins as $user) {
+			$n = new Notification;
+			$n->profile_id = $user;
+			$n->actor_id = $log->admin->profile_id;
+			$n->item_id = $item_id;
+			$n->item_type = $item_type;
+			$n->action = $action;
+			$n->message = $msg;
+			$n->rendered = $rendered;
+			$n->save();
+		}
+	}
+
+	public function unfanout()
+	{
+		Notification::whereItemType('App\ModLog')
+			->whereItemId($this->log->id)
+			->delete();
+	}
 }
 }

+ 24 - 1
app/Transformer/Api/NotificationTransformer.php

@@ -13,7 +13,8 @@ class NotificationTransformer extends Fractal\TransformerAbstract
 	protected $defaultIncludes = [
 	protected $defaultIncludes = [
 		'account',
 		'account',
 		'status',
 		'status',
-		'relationship'
+		'relationship',
+		'modlog'
 	];
 	];
 
 
 	public function transform(Notification $notification)
 	public function transform(Notification $notification)
@@ -54,6 +55,7 @@ class NotificationTransformer extends Fractal\TransformerAbstract
 			'share' => 'share',
 			'share' => 'share',
 			'like' => 'favourite',
 			'like' => 'favourite',
 			'comment' => 'comment',
 			'comment' => 'comment',
+			'admin.user.modlog.comment' => 'modlog'
 		];
 		];
 		return $verbs[$verb];
 		return $verbs[$verb];
 	}
 	}
@@ -62,4 +64,25 @@ class NotificationTransformer extends Fractal\TransformerAbstract
 	{
 	{
 		return $this->item($notification->actor, new RelationshipTransformer());
 		return $this->item($notification->actor, new RelationshipTransformer());
 	}
 	}
+
+	public function includeModlog(Notification $notification)
+	{
+		$n = $notification;
+		if($n->item_id && $n->item_type == 'App\ModLog') {
+			$ml = $n->item;
+			if(!empty($ml)) {
+				$res = $this->item($ml, function($ml) {
+					return [
+						'id' => $ml->object_uid,
+						'url' => url('/i/admin/users/modlogs/' . $ml->object_uid)
+					];
+				});
+				return $res;
+			} else {
+				return null;
+			}
+		} else {
+			return null;
+		}
+	}
 }
 }

+ 32 - 1
resources/assets/js/components/NotificationCard.vue

@@ -4,7 +4,7 @@
 		<div class="card notification-card shadow-none border">
 		<div class="card notification-card shadow-none border">
 			<div class="card-header bg-white">
 			<div class="card-header bg-white">
 				<p class="mb-0 d-flex align-items-center justify-content-between">
 				<p class="mb-0 d-flex align-items-center justify-content-between">
-					<span><i class="far fa-bell fa-lg text-white"></i></span>
+					<span data-toggle="tooltip" data-placement="bottom"><i class="fas fa-redo fa-lg text-white"></i></span>
 					<span class="small text-dark text-uppercase font-weight-bold">Alerts</span>
 					<span class="small text-dark text-uppercase font-weight-bold">Alerts</span>
 					<a class="text-decoration-none text-muted" href="/account/activity"><i class="fas fa-inbox fa-lg"></i></a>
 					<a class="text-decoration-none text-muted" href="/account/activity"><i class="fas fa-inbox fa-lg"></i></a>
 				</p>
 				</p>
@@ -43,6 +43,11 @@
 								<a :href="n.account.url" class="font-weight-bold text-dark word-break" :title="n.account.username">{{truncate(n.account.username)}}</a> shared your <a class="font-weight-bold" v-bind:href="n.status.reblog.url">post</a>.
 								<a :href="n.account.url" class="font-weight-bold text-dark word-break" :title="n.account.username">{{truncate(n.account.username)}}</a> shared your <a class="font-weight-bold" v-bind:href="n.status.reblog.url">post</a>.
 							</p>
 							</p>
 						</div>
 						</div>
+						<div v-else-if="n.type == 'modlog'">
+							<p class="my-0">
+								<a :href="n.account.url" class="font-weight-bold text-dark word-break" :title="n.account.username">{{truncate(n.account.username)}}</a> updated a <a class="font-weight-bold" v-bind:href="n.modlog.url">modlog</a>.
+							</p>
+						</div>
 					</div>
 					</div>
 					<div class="small text-muted font-weight-bold" :title="n.created_at">{{timeAgo(n.created_at)}}</div>
 					<div class="small text-muted font-weight-bold" :title="n.created_at">{{timeAgo(n.created_at)}}</div>
 				</div>
 				</div>
@@ -193,6 +198,32 @@
 						}
 						}
 					});
 					});
 				}, interval);
 				}, interval);
+			},
+
+			refreshNotifications() {
+				let self = this;
+				axios.get('/api/pixelfed/v1/notifications')
+				.then(res => {
+					let data = res.data.filter(n => {
+						if(n.type == 'share' || self.notificationMaxId >= n.id) {
+							return false;
+						}
+						return true;
+					});
+					if(data.length > 0) {
+						let ids = data.map(n => n.id);
+						let max = Math.max(ids);
+						if(max <= self.notificationMaxId) {
+							return;
+						} else {
+							self.notificationMaxId = max;
+							self.notifications = data;
+							let beep = new Audio('/static/beep.mp3');
+							beep.volume = 0.7;
+							beep.play();
+						}
+					}
+				});
 			}
 			}
 		}
 		}
 	}
 	}

+ 2 - 10
resources/assets/js/components/PostComponent.vue

@@ -675,9 +675,9 @@ export default {
       },
       },
 
 
       fetchData() {
       fetchData() {
+          let self = this;
           axios.get('/api/v2/profile/'+this.statusUsername+'/status/'+this.statusId)
           axios.get('/api/v2/profile/'+this.statusUsername+'/status/'+this.statusId)
             .then(response => {
             .then(response => {
-                let self = this;
                 self.status = response.data.status;
                 self.status = response.data.status;
                 self.user = response.data.user;
                 self.user = response.data.user;
                 window._sharedData.curUser = self.user;
                 window._sharedData.curUser = self.user;
@@ -696,15 +696,7 @@ export default {
                 this.loaded = true;
                 this.loaded = true;
                 $('head title').text(this.status.account.username + ' posted a photo: ' + this.status.favourites_count + ' likes');
                 $('head title').text(this.status.account.username + ' posted a photo: ' + this.status.favourites_count + ' likes');
             }).catch(error => {
             }).catch(error => {
-              if(!error.response) {
-              } else {
-                switch(error.response.status) {
-                  case 401:
-                  break;
-                  default:
-                  break;
-                }
-              }
+              swal('Oops!', 'An error occured, please try refreshing the page.', 'error');
             });
             });
       },
       },