mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 15:52:11 +08:00
REFACTOR: Update the notification menu to remove scrolling (#10371)
This commit is contained in:
parent
d67f7a7984
commit
1972364d0f
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 }) => {
|
||||||
|
|
|
@ -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);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -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" }
|
||||||
);
|
);
|
||||||
|
|
|
@ -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",
|
title: "view_all",
|
||||||
this.attach("link", {
|
icon: "chevron-down",
|
||||||
title: "view_all",
|
className: "show-all",
|
||||||
icon: "chevron-down",
|
url: this.showAllHref()
|
||||||
href: 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() {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user