Separate read & dimiss actions

This commit is contained in:
Jeremy Stretch 2024-07-04 12:12:07 -04:00
parent a21d4fecbc
commit 9c875716f7
7 changed files with 61 additions and 25 deletions

View File

@ -78,7 +78,13 @@ class Notification(models.Model):
return super().__str__() return super().__str__()
def get_absolute_url(self): def get_absolute_url(self):
return self.object.get_absolute_url() return reverse('extras:notifications')
def get_read_url(self):
return reverse('extras:notification_read', kwargs={'pk': self.pk})
def get_dismiss_url(self):
return reverse('extras:notification_dismiss', kwargs={'pk': self.pk})
def clean(self): def clean(self):
super().clean() super().clean()

View File

@ -266,14 +266,35 @@ class BookmarkTable(NetBoxTable):
default_columns = ('object', 'object_type', 'created') default_columns = ('object', 'object_type', 'created')
class NotificationTable(NetBoxTable): class SubscriptionTable(NetBoxTable):
object_type = columns.ContentTypeColumn( object_type = columns.ContentTypeColumn(
verbose_name=_('Object Types'), verbose_name=_('Object Type'),
) )
object = tables.Column( object = tables.Column(
verbose_name=_('Object'), verbose_name=_('Object'),
linkify=True linkify=True
) )
actions = columns.ActionsColumn(
actions=('delete',)
)
class Meta(NetBoxTable.Meta):
model = Subscription
fields = ('pk', 'object', 'object_type', 'created')
default_columns = ('object', 'object_type', 'created')
class NotificationTable(NetBoxTable):
object_type = columns.ContentTypeColumn(
verbose_name=_('Object Type'),
)
object = tables.Column(
verbose_name=_('Object'),
linkify={
'viewname': 'extras:notification_read',
'args': [tables.A('pk')],
}
)
created = columns.DateTimeColumn( created = columns.DateTimeColumn(
timespec='minutes', timespec='minutes',
verbose_name=_('Created'), verbose_name=_('Created'),
@ -290,6 +311,9 @@ class NotificationTable(NetBoxTable):
model = Notification model = Notification
fields = ('pk', 'object', 'object_type', 'created', 'read') fields = ('pk', 'object', 'object_type', 'created', 'read')
default_columns = ('object', 'object_type', 'created', 'read') default_columns = ('object', 'object_type', 'created', 'read')
row_attrs = {
'data-unread': lambda record: not record.read,
}
class NotificationGroupTable(NetBoxTable): class NotificationGroupTable(NetBoxTable):
@ -304,24 +328,6 @@ class NotificationGroupTable(NetBoxTable):
default_columns = ('name', 'description') default_columns = ('name', 'description')
class SubscriptionTable(NetBoxTable):
object_type = columns.ContentTypeColumn(
verbose_name=_('Object Types'),
)
object = tables.Column(
verbose_name=_('Object'),
linkify=True
)
actions = columns.ActionsColumn(
actions=('delete',)
)
class Meta(NetBoxTable.Meta):
model = Subscription
fields = ('pk', 'object', 'object_type', 'created')
default_columns = ('object', 'object_type', 'created')
class WebhookTable(NetBoxTable): class WebhookTable(NetBoxTable):
name = tables.Column( name = tables.Column(
verbose_name=_('Name'), verbose_name=_('Name'),

View File

@ -428,9 +428,24 @@ class NotificationsView(LoginRequiredMixin, View):
@register_model_view(Notification, 'read') @register_model_view(Notification, 'read')
class NotificationReadView(LoginRequiredMixin, View): class NotificationReadView(LoginRequiredMixin, View):
"""
Mark the Notification read and redirect the user to its attached object.
"""
def get(self, request, pk): def get(self, request, pk):
request.user.notifications.filter(pk=pk).update(read=timezone.now()) notification = get_object_or_404(Notification, pk=pk)
notification.read = timezone.now()
notification.save()
return redirect(notification.object.get_absolute_url())
@register_model_view(Notification, 'dismiss')
class NotificationDismissView(LoginRequiredMixin, View):
"""
A convenience view which allows deleting notifications with one click.
"""
def get(self, request, pk):
request.user.notifications.filter(pk=pk).delete()
notifications = request.user.notifications.unread()[:10] notifications = request.user.notifications.unread()[:10]
return render(request, 'htmx/notifications.html', { return render(request, 'htmx/notifications.html', {

Binary file not shown.

View File

@ -0,0 +1,8 @@
@use 'sass:map';
// Highlight unread notifications
tr[data-unread=True] {
td {
background-color: rgba(map.get($theme-colors, "green"), 0.15);
}
}

View File

@ -24,3 +24,4 @@
@import 'custom/interfaces'; @import 'custom/interfaces';
@import 'custom/markdown'; @import 'custom/markdown';
@import 'custom/misc'; @import 'custom/misc';
@import 'custom/notifications';

View File

@ -4,11 +4,11 @@
<div class="list-group-item p-2"> <div class="list-group-item p-2">
<div class="row align-items-center"> <div class="row align-items-center">
<div class="col text-truncate"> <div class="col text-truncate">
<a href="{{ notification.get_absolute_url }}" class="text-body d-block">{{ notification.object }}</a> <a href="{{ notification.get_read_url }}" class="text-body d-block">{{ notification.object }}</a>
<div class="d-block text-secondary fs-5">{{ notification.get_event_display }} {{ notification.created|timesince }} {% trans "ago" %}</div> <div class="d-block text-secondary fs-5">{{ notification.get_event_display }} {{ notification.created|timesince }} {% trans "ago" %}</div>
</div> </div>
<div class="col-auto"> <div class="col-auto">
<a href="#" hx-get="{% url 'extras:notification_read' pk=notification.pk %}" hx-target="closest .notifications" class="list-group-item-actions text-secondary" title="{% trans "Mark read" %}"> <a href="#" hx-get="{{ notification.get_dismiss_url }}" hx-target="closest .notifications" class="list-group-item-actions text-secondary" title="{% trans "Dismiss" %}">
<i class="mdi mdi-close"></i> <i class="mdi mdi-close"></i>
</a> </a>
</div> </div>