REFACTOR: Update the notification menu to remove scrolling (#10371)

This commit is contained in:
Kris 2020-08-10 16:17:15 -04:00 committed by GitHub
parent d67f7a7984
commit 1972364d0f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 98 additions and 122 deletions

View File

@ -9,8 +9,6 @@ import PanEvents, {
} from "discourse/mixins/pan-events"; } from "discourse/mixins/pan-events";
import { topicTitleDecorators } from "discourse/components/topic-title"; import { topicTitleDecorators } from "discourse/components/topic-title";
const PANEL_BODY_MARGIN = 30;
const SiteHeaderComponent = MountWidget.extend(Docking, PanEvents, { const SiteHeaderComponent = MountWidget.extend(Docking, PanEvents, {
widget: "header", widget: "header",
docAt: null, docAt: null,
@ -311,8 +309,6 @@ const SiteHeaderComponent = MountWidget.extend(Docking, PanEvents, {
} }
const $panelBody = $(".panel-body", $panel); const $panelBody = $(".panel-body", $panel);
// 2 pixel fudge allows for firefox subpixel sizing stuff causing scrollbar
let contentHeight = $(".panel-body-contents", $panel).height() + 2;
// We use a mutationObserver to check for style changes, so it's important // We use a mutationObserver to check for style changes, so it's important
// we don't set it if it doesn't change. Same goes for the $panelBody! // we don't set it if it doesn't change. Same goes for the $panelBody!
@ -330,22 +326,6 @@ const SiteHeaderComponent = MountWidget.extend(Docking, PanEvents, {
$panel.css({ top: "100%", height: "auto" }); $panel.css({ top: "100%", height: "auto" });
} }
// adjust panel height
const fullHeight = $window.height();
const offsetTop = $panel.offset().top;
const scrollTop = $window.scrollTop();
if (
contentHeight + (offsetTop - scrollTop) + PANEL_BODY_MARGIN >
fullHeight ||
this.site.mobileView
) {
contentHeight =
fullHeight - (offsetTop - scrollTop) - PANEL_BODY_MARGIN;
}
if ($panelBody.height() !== contentHeight) {
$panelBody.height(contentHeight);
}
$("body").addClass("drop-down-mode"); $("body").addClass("drop-down-mode");
} else { } else {
if (this.site.mobileView) { if (this.site.mobileView) {
@ -354,15 +334,14 @@ const SiteHeaderComponent = MountWidget.extend(Docking, PanEvents, {
const menuTop = this.site.mobileView ? headerTop() : headerHeight(); const menuTop = this.site.mobileView ? headerTop() : headerHeight();
let height;
const winHeightOffset = 16; const winHeightOffset = 16;
let initialWinHeight = window.innerHeight let initialWinHeight = window.innerHeight
? window.innerHeight ? window.innerHeight
: $(window).height(); : $(window).height();
const winHeight = initialWinHeight - winHeightOffset; const winHeight = initialWinHeight - winHeightOffset;
if (menuTop + contentHeight < winHeight && !this.site.mobileView) {
height = contentHeight + "px"; let height;
} else { if (this.site.mobileView) {
height = winHeight - menuTop; height = winHeight - menuTop;
} }

View File

@ -37,7 +37,7 @@ createWidget("menu-panel", {
tagName: "div.menu-panel", tagName: "div.menu-panel",
template: hbs` template: hbs`
<div class='panel-body'> <div class='panel-body'>
<div class='panel-body-contents clearfix'> <div class='panel-body-contents'>
{{yield}} {{yield}}
</div> </div>
</div> </div>

View File

@ -10,11 +10,6 @@ const ICON = "bookmark";
createWidgetFrom(QuickAccessPanel, "quick-access-bookmarks", { createWidgetFrom(QuickAccessPanel, "quick-access-bookmarks", {
buildKey: () => "quick-access-bookmarks", buildKey: () => "quick-access-bookmarks",
hasMore() {
// Always show the button to the bookmarks page.
return true;
},
showAllHref() { showAllHref() {
return `${this.attrs.path}/activity/bookmarks`; return `${this.attrs.path}/activity/bookmarks`;
}, },
@ -49,10 +44,7 @@ createWidgetFrom(QuickAccessPanel, "quick-access-bookmarks", {
loadBookmarksWithReminders() { loadBookmarksWithReminders() {
return ajax(`/u/${this.currentUser.username}/bookmarks.json`, { return ajax(`/u/${this.currentUser.username}/bookmarks.json`, {
cache: "false", cache: "false"
data: {
limit: this.estimateItemLimit()
}
}).then(result => { }).then(result => {
result = result.user_bookmark_list; result = result.user_bookmark_list;
@ -71,7 +63,6 @@ createWidgetFrom(QuickAccessPanel, "quick-access-bookmarks", {
data: { data: {
username: this.currentUser.username, username: this.currentUser.username,
filter: UserAction.TYPES.bookmarks, filter: UserAction.TYPES.bookmarks,
limit: this.estimateItemLimit(),
no_results_help_key: "user_activity.no_bookmarks" no_results_help_key: "user_activity.no_bookmarks"
} }
}).then(({ user_actions, no_results_help }) => { }).then(({ user_actions, no_results_help }) => {

View File

@ -24,12 +24,6 @@ createWidgetFrom(QuickAccessPanel, "quick-access-messages", {
buildKey: () => "quick-access-messages", buildKey: () => "quick-access-messages",
emptyStatePlaceholderItemKey: "choose_topic.none_found", emptyStatePlaceholderItemKey: "choose_topic.none_found",
hasMore() {
// Always show the button to the messages page for composing, archiving,
// etc.
return true;
},
showAllHref() { showAllHref() {
return `${this.attrs.path}/messages`; return `${this.attrs.path}/messages`;
}, },
@ -40,7 +34,7 @@ createWidgetFrom(QuickAccessPanel, "quick-access-messages", {
filter: `topics/private-messages/${this.currentUser.username_lower}` filter: `topics/private-messages/${this.currentUser.username_lower}`
}) })
.then(({ topic_list }) => { .then(({ topic_list }) => {
return topic_list.topics.map(toItem).slice(0, this.estimateItemLimit()); return topic_list.topics.map(toItem);
}); });
}, },

View File

@ -46,8 +46,7 @@ createWidgetFrom(QuickAccessPanel, "quick-access-notifications", {
"notification", "notification",
{ {
recent: true, recent: true,
silent: this.currentUser.enforcedSecondFactor, silent: this.currentUser.enforcedSecondFactor
limit: this.estimateItemLimit()
}, },
{ cacheKey: "recent-notifications" } { cacheKey: "recent-notifications" }
); );

View File

@ -2,15 +2,8 @@ import I18n from "I18n";
import Session from "discourse/models/session"; import Session from "discourse/models/session";
import { createWidget } from "discourse/widgets/widget"; import { createWidget } from "discourse/widgets/widget";
import { h } from "virtual-dom"; import { h } from "virtual-dom";
import { headerHeight } from "discourse/components/site-header";
import { Promise } from "rsvp"; 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, * This tries to enforce a consistent flow of fetching, caching, refreshing,
* and rendering for "quick access items". * and rendering for "quick access items".
@ -31,6 +24,10 @@ export default createWidget("quick-access-panel", {
return Promise.resolve(); return Promise.resolve();
}, },
hideBottomItems() {
return false;
},
hasUnread() { hasUnread() {
return false; return false;
}, },
@ -39,10 +36,6 @@ export default createWidget("quick-access-panel", {
return ""; return "";
}, },
hasMore() {
return this.getItems().length >= this.estimateItemLimit();
},
findNewItems() { findNewItems() {
return Promise.resolve([]); return Promise.resolve([]);
}, },
@ -69,23 +62,6 @@ export default createWidget("quick-access-panel", {
}); });
}, },
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) { refreshNotifications(state) {
if (this.loading) { if (this.loading) {
return; return;
@ -119,24 +95,35 @@ export default createWidget("quick-access-panel", {
return [h("div.spinner-container", h("div.spinner"))]; return [h("div.spinner-container", h("div.spinner"))];
} }
let bottomItems = [];
const items = this.getItems().length const items = this.getItems().length
? this.getItems().map(item => this.itemHtml(item)) ? this.getItems().map(item => this.itemHtml(item))
: [this.emptyStatePlaceholderItem()]; : [this.emptyStatePlaceholderItem()];
if (this.hasMore()) { if (!this.hideBottomItems()) {
items.push( bottomItems.push(
h( this.attach("button", {
"li.read.last.show-all",
this.attach("link", {
title: "view_all", title: "view_all",
icon: "chevron-down", icon: "chevron-down",
href: this.showAllHref() className: "show-all",
url: this.showAllHref()
}) })
)
); );
} }
return [h("ul", items)]; if (this.hasUnread()) {
bottomItems.push(
this.attach("button", {
title: "user.dismiss_notifications_tooltip",
icon: "check",
label: "user.dismiss",
className: "notifications-dismiss",
action: "dismissNotifications"
})
);
}
return [h("ul", items), h("div.panel-body-bottom", bottomItems)];
}, },
getItems() { getItems() {

View File

@ -14,9 +14,9 @@ createWidgetFrom(QuickAccessPanel, "quick-access-profile", {
buildKey: () => "quick-access-profile", buildKey: () => "quick-access-profile",
hasMore() { hideBottomItems() {
// Never show the button to the full profile page. // Never show the button to the full profile page.
return false; return true;
}, },
findNewItems() { findNewItems() {

View File

@ -2,7 +2,6 @@ import { later } from "@ember/runloop";
import { createWidget } from "discourse/widgets/widget"; import { createWidget } from "discourse/widgets/widget";
import { h } from "virtual-dom"; import { h } from "virtual-dom";
import { formatUsername } from "discourse/lib/utilities"; import { formatUsername } from "discourse/lib/utilities";
import hbs from "discourse/widgets/hbs-compiler";
const UserMenuAction = { const UserMenuAction = {
QUICK_ACCESS: "quickAccess" QUICK_ACCESS: "quickAccess"
@ -145,23 +144,6 @@ createWidget("user-menu-links", {
} }
}); });
createWidget("user-menu-dismiss-link", {
tagName: "div.dismiss-link",
template: hbs`
<ul class='menu-links'>
<li>
{{link action="dismissNotifications"
className="dismiss"
tabindex="0"
icon="check"
label="user.dismiss"
title="user.dismiss_notifications_tooltip"}}
</li>
</ul>
`
});
export default createWidget("user-menu", { export default createWidget("user-menu", {
tagName: "div.user-menu", tagName: "div.user-menu",
buildKey: () => "user-menu", buildKey: () => "user-menu",
@ -191,11 +173,6 @@ export default createWidget("user-menu", {
this.quickAccessPanel(path) this.quickAccessPanel(path)
]; ];
if (this.state.hasUnread) {
result.push(h("hr.bottom-area"));
result.push(this.attach("user-menu-dismiss-link"));
}
return result; return result;
}, },

View File

@ -19,6 +19,7 @@
// positions are relative to the .d-header .panel div // positions are relative to the .d-header .panel div
top: 100%; // directly underneath .panel top: 100%; // directly underneath .panel
right: -10px; // 10px to the right of .panel - adjust as needed right: -10px; // 10px to the right of .panel - adjust as needed
max-height: 80vh;
} }
.menu-panel { .menu-panel {
@ -28,7 +29,9 @@
z-index: z("header"); z-index: z("header");
padding: 0.5em; padding: 0.5em;
width: 300px; width: 300px;
overflow: hidden;
display: flex;
flex-direction: column;
hr { hr {
margin: 3px 0; margin: 3px 0;
} }
@ -45,10 +48,41 @@
} }
.panel-body { .panel-body {
display: flex;
touch-action: pan-y pinch-zoom; touch-action: pan-y pinch-zoom;
overflow-y: auto; overflow: hidden;
overflow-x: hidden; height: 100%;
max-height: 100vh; }
.panel-body-contents {
max-height: 100%;
width: 100%;
display: flex;
flex-direction: column;
}
.panel-body-bottom {
display: flex;
flex: 1 0 0%; // safari height fix
margin-top: 0.5em;
.show-all {
flex: 1 1 auto;
button {
width: 100%;
}
}
.notifications-dismiss {
margin-left: 0.5em;
}
button {
background-color: var(--primary-very-low);
color: var(--primary-high);
&:hover {
background: var(--primary-low);
color: var(--primary);
}
}
} }
.badge-notification { .badge-notification {
@ -153,11 +187,13 @@
.user-menu { .user-menu {
.quick-access-panel { .quick-access-panel {
width: 100%; width: 100%;
display: table; display: flex;
margin-top: -1px; flex-direction: column;
min-height: 0;
max-height: 100%;
border-top: 1px solid var(--primary-low); border-top: 1px solid var(--primary-low);
padding-top: 0.5em; padding-top: 0.5em;
margin-top: -1px;
h3 { h3 {
padding: 0 0.4em; padding: 0 0.4em;
font-weight: bold; font-weight: bold;
@ -172,8 +208,17 @@
color: var(--primary-high); color: var(--primary-high);
} }
ul {
display: flex;
flex-flow: column wrap;
overflow: hidden;
max-height: 100%;
}
li { li {
background-color: var(--tertiary-low); background-color: var(--tertiary-low);
box-sizing: border-box;
list-style-type: none;
// This is until other languages remove the HTML from within // This is until other languages remove the HTML from within
// notifications. It can then be removed // notifications. It can then be removed
@ -192,7 +237,8 @@
} }
a { a {
padding: 0; display: flex;
padding: 0.25em 0.5em;
> div { > div {
overflow: hidden; // clears the text from wrapping below icons overflow: hidden; // clears the text from wrapping below icons
@ -215,11 +261,8 @@
} }
li:not(.show-all) { li:not(.show-all) {
padding: 0; padding: 0;
align-self: flex-start;
a { width: 100%;
display: flex;
padding: 0.25em 0.5em;
}
.d-icon { .d-icon {
padding-top: 0.2em; padding-top: 0.2em;
margin-right: 0.5em; margin-right: 0.5em;
@ -264,6 +307,8 @@
&.quick-access-profile { &.quick-access-profile {
li:not(.show-all) a { li:not(.show-all) a {
color: var(--primary); color: var(--primary);
display: flex;
padding: 0.25em 0.5em;
.d-icon { .d-icon {
color: var(--primary-medium); color: var(--primary-medium);
} }

View File

@ -1,4 +1,8 @@
.search-menu { .search-menu {
.menu-panel .panel-body-contents {
overflow-y: auto;
}
.search-input { .search-input {
position: relative; position: relative;
padding: 0.5em 3px; padding: 0.5em 3px;

View File

@ -13,7 +13,7 @@ widgetTest("basics", {
assert.ok(find(".user-notifications-link").length); assert.ok(find(".user-notifications-link").length);
assert.ok(find(".user-bookmarks-link").length); assert.ok(find(".user-bookmarks-link").length);
assert.ok(find(".quick-access-panel").length); assert.ok(find(".quick-access-panel").length);
assert.ok(find(".dismiss-link").length); assert.ok(find(".notifications-dismiss").length);
} }
}); });