Look for the specialised version first, before falling back to the default. This allows the behaviour to be customised based on the type of notification.
133 lines
3.4 KiB
JavaScript
133 lines
3.4 KiB
JavaScript
import { createWidget } from "discourse/widgets/widget";
|
|
import { headerHeight } from "discourse/components/site-header";
|
|
import { h } from "virtual-dom";
|
|
import DiscourseURL from "discourse/lib/url";
|
|
import { ajax } from "discourse/lib/ajax";
|
|
|
|
export default createWidget("user-notifications", {
|
|
tagName: "div.notifications",
|
|
buildKey: () => "user-notifications",
|
|
|
|
defaultState() {
|
|
return { notifications: [], loading: false, loaded: false };
|
|
},
|
|
|
|
markRead() {
|
|
ajax("/notifications/mark-read", { method: "PUT" }).then(() => {
|
|
this.refreshNotifications(this.state);
|
|
});
|
|
},
|
|
|
|
refreshNotifications(state) {
|
|
if (this.loading) {
|
|
return;
|
|
}
|
|
|
|
// estimate (poorly) the amount of notifications to return
|
|
let limit = Math.round(($(window).height() - headerHeight()) / 55);
|
|
// we REALLY don't want to be asking for negative counts of notifications
|
|
// less than 5 is also not that useful
|
|
if (limit < 5) {
|
|
limit = 5;
|
|
}
|
|
if (limit > 40) {
|
|
limit = 40;
|
|
}
|
|
|
|
const silent = this.currentUser.get("enforcedSecondFactor");
|
|
const stale = this.store.findStale(
|
|
"notification",
|
|
{ recent: true, silent, limit },
|
|
{ cacheKey: "recent-notifications" }
|
|
);
|
|
|
|
if (stale.hasResults) {
|
|
const results = stale.results;
|
|
let content = results.get("content");
|
|
|
|
// we have to truncate to limit, otherwise we will render too much
|
|
if (content && content.length > limit) {
|
|
content = content.splice(0, limit);
|
|
results.set("content", content);
|
|
results.set("totalRows", limit);
|
|
}
|
|
|
|
state.notifications = results;
|
|
} else {
|
|
state.loading = true;
|
|
}
|
|
|
|
stale
|
|
.refresh()
|
|
.then(notifications => {
|
|
if (!silent) {
|
|
this.currentUser.set("unread_notifications", 0);
|
|
}
|
|
state.notifications = notifications;
|
|
})
|
|
.catch(() => {
|
|
state.notifications = [];
|
|
})
|
|
.finally(() => {
|
|
state.loading = false;
|
|
state.loaded = true;
|
|
this.sendWidgetAction("notificationsLoaded", {
|
|
notifications: state.notifications,
|
|
markRead: () => this.markRead()
|
|
});
|
|
this.scheduleRerender();
|
|
});
|
|
},
|
|
|
|
html(attrs, state) {
|
|
if (!state.loaded) {
|
|
this.refreshNotifications(state);
|
|
}
|
|
|
|
const result = [];
|
|
if (state.loading) {
|
|
result.push(h("div.spinner-container", h("div.spinner")));
|
|
} else if (state.notifications.length) {
|
|
const notificationItems =
|
|
state.notifications.map(attrs => {
|
|
|
|
const notificationName =
|
|
this.site.notificationLookup[attrs.notification_type];
|
|
|
|
const widgetNames = [
|
|
`${notificationName.replace(/_/g, '-')}-notification-item`,
|
|
"default-notification-item"
|
|
];
|
|
|
|
return this.attach(widgetNames, attrs);
|
|
});
|
|
|
|
result.push(h("hr"));
|
|
|
|
const items = [notificationItems];
|
|
|
|
if (notificationItems.length > 5) {
|
|
items.push(
|
|
h(
|
|
"li.read.last.heading.show-all",
|
|
this.attach("button", {
|
|
title: "notifications.more",
|
|
icon: "chevron-down",
|
|
action: "showAllNotifications",
|
|
className: "btn"
|
|
})
|
|
)
|
|
);
|
|
}
|
|
|
|
result.push(h("ul", items));
|
|
}
|
|
|
|
return result;
|
|
},
|
|
|
|
showAllNotifications() {
|
|
DiscourseURL.routeTo(`${this.attrs.path}/notifications`);
|
|
}
|
|
});
|