mirror of
https://github.com/discourse/discourse.git
synced 2025-01-08 20:54:10 +08:00
0d6839e8a8
Our current algorithm for picking the number of notifications to display when expanding the notifications relies on magic numbers. Previously we only allowed for header and an estimate of maximum height of notification container, this is not ideal as there is padding at the bottom and top of the notification container This adds a special number for padding. The longer term fix though is to render the notification panel off screen then grab the correct count, finally adding it back into view with. This would allow for large fonts, small fonts, custom themes and much more.
149 lines
3.5 KiB
JavaScript
149 lines
3.5 KiB
JavaScript
import Session from "discourse/models/session";
|
|
import { createWidget } from "discourse/widgets/widget";
|
|
import { h } from "virtual-dom";
|
|
import { headerHeight } from "discourse/components/site-header";
|
|
import { Promise } from "rsvp";
|
|
|
|
// even a 2 liner notification should be under 50px in default view
|
|
const AVERAGE_ITEM_HEIGHT = 50;
|
|
|
|
// our UX usually carries about 100px of padding around the notification excluding header
|
|
const PADDING = 100;
|
|
|
|
/**
|
|
* This tries to enforce a consistent flow of fetching, caching, refreshing,
|
|
* and rendering for "quick access items".
|
|
*
|
|
* There are parts to introducing a new quick access panel:
|
|
* 1. A user menu link that sends a `quickAccess` action, with a unique `type`.
|
|
* 2. A `quick-access-${type}` widget, extended from `quick-access-panel`.
|
|
*/
|
|
export default createWidget("quick-access-panel", {
|
|
tagName: "div.quick-access-panel",
|
|
emptyStatePlaceholderItemKey: "",
|
|
|
|
buildKey: () => {
|
|
throw Error('Cannot attach abstract widget "quick-access-panel".');
|
|
},
|
|
|
|
markReadRequest() {
|
|
return Promise.resolve();
|
|
},
|
|
|
|
hasUnread() {
|
|
return false;
|
|
},
|
|
|
|
showAllHref() {
|
|
return "";
|
|
},
|
|
|
|
hasMore() {
|
|
return this.getItems().length >= this.estimateItemLimit();
|
|
},
|
|
|
|
findNewItems() {
|
|
return Promise.resolve([]);
|
|
},
|
|
|
|
newItemsLoaded() {},
|
|
|
|
itemHtml(item) {}, // eslint-disable-line no-unused-vars
|
|
|
|
emptyStatePlaceholderItem() {
|
|
if (this.emptyStatePlaceholderItemKey) {
|
|
return h("li.read", I18n.t(this.emptyStatePlaceholderItemKey));
|
|
} else {
|
|
return "";
|
|
}
|
|
},
|
|
|
|
defaultState() {
|
|
return { items: [], loading: false, loaded: false };
|
|
},
|
|
|
|
markRead() {
|
|
return this.markReadRequest().then(() => {
|
|
this.refreshNotifications(this.state);
|
|
});
|
|
},
|
|
|
|
estimateItemLimit() {
|
|
// Estimate (poorly) the amount of notifications to return.
|
|
let limit = Math.round(
|
|
($(window).height() - headerHeight() - PADDING) / AVERAGE_ITEM_HEIGHT
|
|
);
|
|
|
|
// 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;
|
|
} else if (limit > 40) {
|
|
limit = 40;
|
|
}
|
|
|
|
return limit;
|
|
},
|
|
|
|
refreshNotifications(state) {
|
|
if (this.loading) {
|
|
return;
|
|
}
|
|
|
|
if (this.getItems().length === 0) {
|
|
state.loading = true;
|
|
}
|
|
|
|
this.findNewItems()
|
|
.then(newItems => this.setItems(newItems))
|
|
.catch(() => this.setItems([]))
|
|
.finally(() => {
|
|
state.loading = false;
|
|
state.loaded = true;
|
|
this.newItemsLoaded();
|
|
this.sendWidgetAction("itemsLoaded", {
|
|
hasUnread: this.hasUnread(),
|
|
markRead: () => this.markRead()
|
|
});
|
|
this.scheduleRerender();
|
|
});
|
|
},
|
|
|
|
html(attrs, state) {
|
|
if (!state.loaded) {
|
|
this.refreshNotifications(state);
|
|
}
|
|
|
|
if (state.loading) {
|
|
return [h("div.spinner-container", h("div.spinner"))];
|
|
}
|
|
|
|
const items = this.getItems().length
|
|
? this.getItems().map(item => this.itemHtml(item))
|
|
: [this.emptyStatePlaceholderItem()];
|
|
|
|
if (this.hasMore()) {
|
|
items.push(
|
|
h(
|
|
"li.read.last.show-all",
|
|
this.attach("link", {
|
|
title: "view_all",
|
|
icon: "chevron-down",
|
|
href: this.showAllHref()
|
|
})
|
|
)
|
|
);
|
|
}
|
|
|
|
return [h("ul", items)];
|
|
},
|
|
|
|
getItems() {
|
|
return Session.currentProp(`${this.key}-items`) || [];
|
|
},
|
|
|
|
setItems(newItems) {
|
|
Session.currentProp(`${this.key}-items`, newItems);
|
|
}
|
|
});
|