feat: add resolve_object_url helper and notification click-through with comment highlighting
Extract duplicate target-redirect logic from comments router into a new `resolve_object_url` database function, add `target_url` field to follow and message notifications, create `/notifications/open/{uid}` endpoint that marks notifications read and redirects to their target, implement client-side NotificationManager for card clicks and comment hash scrolling, and add comment-highlight CSS animation.
This commit is contained in:
@@ -31,6 +31,7 @@
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1.25rem;
|
||||
transition: all 0.2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.notification-card.unread {
|
||||
|
||||
@@ -115,6 +115,22 @@
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.comment-highlight {
|
||||
animation: comment-highlight-fade 2s ease-out;
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
|
||||
@keyframes comment-highlight-fade {
|
||||
from {
|
||||
background: var(--accent-light);
|
||||
box-shadow: 0 0 0 4px var(--accent-light);
|
||||
}
|
||||
to {
|
||||
background: transparent;
|
||||
box-shadow: 0 0 0 4px transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.comment-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { ModalManager } from "./ModalManager.js";
|
||||
import { FormManager } from "./FormManager.js";
|
||||
import { VoteManager } from "./VoteManager.js";
|
||||
import { NotificationManager } from "./NotificationManager.js";
|
||||
import { MessageSearch } from "./MessageSearch.js";
|
||||
import { ProfileEditor } from "./ProfileEditor.js";
|
||||
import { MobileNav } from "./MobileNav.js";
|
||||
@@ -15,6 +16,7 @@ class Application {
|
||||
this.modals = new ModalManager();
|
||||
this.forms = new FormManager();
|
||||
this.votes = new VoteManager();
|
||||
this.notifications = new NotificationManager();
|
||||
this.messageSearch = new MessageSearch();
|
||||
this.profile = new ProfileEditor();
|
||||
this.mobileNav = new MobileNav();
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Http } from "./Http.js";
|
||||
|
||||
export class NotificationManager {
|
||||
constructor() {
|
||||
this.initCardNavigation();
|
||||
this.initDismiss();
|
||||
this.initHashScroll();
|
||||
}
|
||||
|
||||
initCardNavigation() {
|
||||
document.querySelectorAll(".notification-card[data-href]").forEach((card) => {
|
||||
card.addEventListener("click", (e) => {
|
||||
if (e.target.closest("a, button")) {
|
||||
return;
|
||||
}
|
||||
window.location.href = card.dataset.href;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
initDismiss() {
|
||||
document.querySelectorAll(".notification-dismiss").forEach((btn) => {
|
||||
btn.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
const uid = btn.dataset.uid;
|
||||
if (!uid) {
|
||||
return;
|
||||
}
|
||||
Http.postForm(`/notifications/mark-read/${uid}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
initHashScroll() {
|
||||
const hash = window.location.hash;
|
||||
if (!hash.startsWith("#comment-")) {
|
||||
return;
|
||||
}
|
||||
window.addEventListener("load", () => {
|
||||
const target = document.getElementById(hash.slice(1));
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
target.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
target.classList.add("comment-highlight");
|
||||
setTimeout(() => target.classList.remove("comment-highlight"), 2000);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ import { Http } from "./Http.js";
|
||||
export class VoteManager {
|
||||
constructor() {
|
||||
this.initVoteButtons();
|
||||
this.initNotificationDismiss();
|
||||
}
|
||||
|
||||
initVoteButtons() {
|
||||
@@ -15,16 +14,4 @@ export class VoteManager {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
initNotificationDismiss() {
|
||||
document.querySelectorAll(".notification-dismiss").forEach((btn) => {
|
||||
btn.addEventListener("click", () => {
|
||||
const uid = btn.dataset.uid;
|
||||
if (!uid) {
|
||||
return;
|
||||
}
|
||||
Http.postForm(`/notifications/mark-read/${uid}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user