add: mark all notifications as read/unread

fix: anonymous avatar/username
This commit is contained in:
trisua 2025-05-22 22:54:06 -04:00
parent 37e68079d8
commit 3f6f1eda9f
7 changed files with 118 additions and 10 deletions

View file

@ -134,6 +134,8 @@ version = "1.0.0"
"notifs:action.mark_as_unread" = "Mark as unread" "notifs:action.mark_as_unread" = "Mark as unread"
"notifs:action.clear" = "Clear" "notifs:action.clear" = "Clear"
"notifs:label.notifications" = "Notifications" "notifs:label.notifications" = "Notifications"
"notifs:label.mark_all_as_read" = "Mark all as read"
"notifs:label.mark_all_as_unread" = "Mark all as unread"
"settings:tab.general" = "General" "settings:tab.general" = "General"
"settings:tab.account" = "Account" "settings:tab.account" = "Account"

View file

@ -124,7 +124,7 @@ expect_repost=false) -%} {% if community and show_community and community.id !=
config.town_square or question %} config.town_square or question %}
<div class="card-nest"> <div class="card-nest">
{% if question -%} {{ self::question(question=question[0], {% if question -%} {{ self::question(question=question[0],
owner=question[1]) }} {% else %} owner=question[1], profile=owner) }} {% else %}
<div class="card small"> <div class="card small">
<a <a
href="/api/v1/communities/find/{{ post.community }}" href="/api/v1/communities/find/{{ post.community }}"
@ -642,7 +642,7 @@ user %} {% if user.settings.theme_hue -%}
} }
</style> </style>
{%- endif %} {%- endmacro %} {% macro question(question, owner, {%- endif %} {%- endmacro %} {% macro question(question, owner,
show_community=true, secondary=false) -%} show_community=true, secondary=false, profile=false) -%}
<div class="card{% if secondary -%} secondary{%- endif %} flex gap-2"> <div class="card{% if secondary -%} secondary{%- endif %} flex gap-2">
{% if owner.id == 0 -%} {% if owner.id == 0 -%}
<span> <span>

View file

@ -9,6 +9,7 @@
<span>{{ text "notifs:label.notifications" }}</span> <span>{{ text "notifs:label.notifications" }}</span>
</span> </span>
<div class="flex gap-2">
<button <button
onclick="trigger('me::clear_notifs')" onclick="trigger('me::clear_notifs')"
class="small red quaternary" class="small red quaternary"
@ -16,6 +17,35 @@
{{ icon "bomb" }} {{ icon "bomb" }}
<span>{{ text "notifs:action.clear" }}</span> <span>{{ text "notifs:action.clear" }}</span>
</button> </button>
<div class="dropdown">
<button
class="small quaternary"
onclick="trigger('atto::hooks::dropdown', [event])"
exclude="dropdown"
>
{{ icon "ellipsis" }}
</button>
<div class="inner">
<button onclick="mark_all_as_read(true)">
{{ icon "bookmark-check" }}
<span
>{{ text "notifs:label.mark_all_as_read"
}}</span
>
</button>
<button onclick="mark_all_as_read(false)">
{{ icon "bookmark-x" }}
<span
>{{ text "notifs:label.mark_all_as_unread"
}}</span
>
</button>
</div>
</div>
</div>
</div> </div>
<div class="card tertiary flex flex-col gap-4"> <div class="card tertiary flex flex-col gap-4">
@ -24,4 +54,33 @@
</div> </div>
</div> </div>
</main> </main>
<script>
async function mark_all_as_read(read) {
if (
!(await trigger("atto::confirm", [
"Are you sure you want to do this?",
]))
) {
return;
}
fetch("/api/v1/notifications/all/read_status", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
read,
}),
})
.then((res) => res.json())
.then((res) => {
trigger("atto::toast", [
res.ok ? "success" : "error",
res.message,
]);
});
}
</script>
{% endblock %} {% endblock %}

View file

@ -89,7 +89,7 @@
{%- endif %} {% endfor %} {% for question in questions %} {%- endif %} {% endfor %} {% for question in questions %}
<!-- prettier-ignore --> <!-- prettier-ignore -->
<div class="card-nest"> <div class="card-nest">
{{ components::question(question=question[0], owner=question[1]) }} {{ components::question(question=question[0], owner=question[1], profile=user) }}
<form <form
class="card flex flex-col gap-2" class="card flex flex-col gap-2"

View file

@ -237,6 +237,10 @@ pub fn routes() -> Router {
"/notifications/{id}/read_status", "/notifications/{id}/read_status",
post(notifications::update_read_status_request), post(notifications::update_read_status_request),
) )
.route(
"/notifications/all/read_status",
post(notifications::update_all_read_status_request),
)
// community memberships // community memberships
.route( .route(
"/communities/{id}/join", "/communities/{id}/join",

View file

@ -87,3 +87,24 @@ pub async fn update_read_status_request(
Err(e) => Json(e.into()), Err(e) => Json(e.into()),
} }
} }
pub async fn update_all_read_status_request(
jar: CookieJar,
Extension(data): Extension<State>,
Json(req): Json<UpdateNotificationRead>,
) -> impl IntoResponse {
let data = &(data.read().await).0;
let user = match get_user_from_token!(jar, data) {
Some(ua) => ua,
None => return Json(Error::NotAllowed.into()),
};
match data.update_all_notifications_read(&user, req.read).await {
Ok(_) => Json(ApiReturn {
ok: true,
message: "Notifications updated".to_string(),
payload: (),
}),
Err(e) => Json(e.into()),
}
}

View file

@ -246,4 +246,26 @@ impl DataManager {
Ok(()) Ok(())
} }
pub async fn update_all_notifications_read(&self, user: &User, read: bool) -> Result<()> {
let notifications = self.get_notifications_by_owner(user.id).await?;
if notifications.len() > 1000 {
return Err(Error::MiscError(
"Too many notifications to do this".to_string(),
));
}
for notification in notifications {
if notification.read == read {
// no need to update this
continue;
}
self.update_notification_read(notification.id, read, user)
.await?
}
Ok(())
}
} }