FEATURE: New/unread messages count in experimental sidebar (#17117)

* FEATURE: Expand messages filter links when viewing private messages.

* FEATURE: New/unread messages count in experimental sidebar
This commit is contained in:
Alan Guo Xiang Tan 2022-06-22 11:01:37 +08:00 committed by GitHub
parent b09cce6897
commit 0b8e6adabe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 278 additions and 15 deletions

View File

@ -2,13 +2,14 @@ import { cached } from "@glimmer/tracking";
import { getOwner } from "discourse-common/lib/get-owner"; import { getOwner } from "discourse-common/lib/get-owner";
import GlimmerComponent from "discourse/components/glimmer"; import GlimmerComponent from "discourse/components/glimmer";
import { bind } from "discourse-common/utils/decorators";
import GroupMessageSectionLink from "discourse/lib/sidebar/messages-section/group-message-section-link"; import GroupMessageSectionLink from "discourse/lib/sidebar/messages-section/group-message-section-link";
import PersonalMessageSectionLink from "discourse/lib/sidebar/messages-section/personal-message-section-link"; import PersonalMessageSectionLink from "discourse/lib/sidebar/messages-section/personal-message-section-link";
export const INBOX = "inbox"; export const INBOX = "inbox";
const UNREAD = "unread"; export const UNREAD = "unread";
const SENT = "sent"; const SENT = "sent";
const NEW = "new"; export const NEW = "new";
const ARCHIVE = "archive"; const ARCHIVE = "archive";
export const PERSONAL_MESSAGES_INBOX_FILTERS = [ export const PERSONAL_MESSAGES_INBOX_FILTERS = [
@ -30,6 +31,24 @@ export default class SidebarMessagesSection extends GlimmerComponent {
this, this,
this._refreshSectionLinksDisplayState this._refreshSectionLinksDisplayState
); );
this.pmTopicTrackingState
.startTracking()
.then(this._refreshSectionLinkCounts);
this._pmTopicTrackingStateKey = "messages-section";
this.pmTopicTrackingState.onStateChange(
this._pmTopicTrackingStateKey,
this._refreshSectionLinkCounts
);
}
@bind
_refreshSectionLinkCounts() {
for (const sectionLink of this.allSectionLinks) {
sectionLink.refreshCount();
}
} }
willDestroy() { willDestroy() {
@ -38,6 +57,11 @@ export default class SidebarMessagesSection extends GlimmerComponent {
this, this,
this._refreshSectionLinksDisplayState this._refreshSectionLinksDisplayState
); );
this.pmTopicTrackingState.offStateChange(
this._pmTopicTrackingStateKey,
this._refreshSectionLinkCounts
);
} }
_refreshSectionLinksDisplayState({ _refreshSectionLinksDisplayState({
@ -81,6 +105,7 @@ export default class SidebarMessagesSection extends GlimmerComponent {
new PersonalMessageSectionLink({ new PersonalMessageSectionLink({
currentUser: this.currentUser, currentUser: this.currentUser,
type, type,
pmTopicTrackingState: this.pmTopicTrackingState,
}) })
); );
}); });
@ -99,6 +124,7 @@ export default class SidebarMessagesSection extends GlimmerComponent {
group, group,
type: groupMessageLink, type: groupMessageLink,
currentUser: this.currentUser, currentUser: this.currentUser,
pmTopicTrackingState: this.pmTopicTrackingState,
}) })
); );
}); });

View File

@ -40,6 +40,10 @@ export default class GroupMessageSectionLink extends MessageSectionLink {
get text() { get text() {
if (this._isInbox) { if (this._isInbox) {
return this.group.name; return this.group.name;
} else if (this.count > 0) {
return I18n.t(`sidebar.sections.messages.links.${this.type}_with_count`, {
count: this.count,
});
} else { } else {
return I18n.t(`sidebar.sections.messages.links.${this.type}`); return I18n.t(`sidebar.sections.messages.links.${this.type}`);
} }

View File

@ -1,18 +1,42 @@
import { tracked } from "@glimmer/tracking"; import { tracked } from "@glimmer/tracking";
import { INBOX } from "discourse/components/sidebar/messages-section"; import {
INBOX,
NEW,
UNREAD,
} from "discourse/components/sidebar/messages-section";
export default class MessageSectionLink { export default class MessageSectionLink {
@tracked shouldDisplay = this._isInbox; @tracked shouldDisplay = this._isInbox;
@tracked count = 0;
constructor({ group, currentUser, type }) { constructor({ group, currentUser, type, pmTopicTrackingState }) {
this.group = group; this.group = group;
this.currentUser = currentUser; this.currentUser = currentUser;
this.type = type; this.type = type;
this.pmTopicTrackingState = pmTopicTrackingState;
}
refreshCount() {
this._refreshCount();
}
_refreshCount() {
if (this.shouldDisplay && this._shouldTrack) {
this.count = this.pmTopicTrackingState.lookupCount(this.type, {
inboxFilter: this.group ? "group" : "user",
groupName: this.group?.name,
});
}
} }
set setDisplayState(value) { set setDisplayState(value) {
const changed = this.shouldDisplay !== value;
this.shouldDisplay = value; this.shouldDisplay = value;
if (changed) {
this._refreshCount();
}
} }
get inboxFilter() { get inboxFilter() {
@ -43,4 +67,8 @@ export default class MessageSectionLink {
get _isInbox() { get _isInbox() {
return this.type === INBOX; return this.type === INBOX;
} }
get _shouldTrack() {
return this.type === NEW || this.type === UNREAD;
}
} }

View File

@ -38,8 +38,14 @@ export default class PersonalMessageSectionLink extends MessageSectionLink {
} }
get text() { get text() {
if (this.count > 0) {
return I18n.t(`sidebar.sections.messages.links.${this.type}_with_count`, {
count: this.count,
});
} else {
return I18n.t(`sidebar.sections.messages.links.${this.type}`); return I18n.t(`sidebar.sections.messages.links.${this.type}`);
} }
}
pageChanged({ currentRouteName, privateMessageTopic }) { pageChanged({ currentRouteName, privateMessageTopic }) {
if (this._isInbox) { if (this._isInbox) {

View File

@ -1,3 +1,5 @@
import { Promise } from "rsvp";
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import { bind, on } from "discourse-common/utils/decorators"; import { bind, on } from "discourse-common/utils/decorators";
@ -25,16 +27,20 @@ const PrivateMessageTopicTrackingState = EmberObject.extend({
this.statesModificationCounter = 0; this.statesModificationCounter = 0;
this.isTracking = false; this.isTracking = false;
this.newIncoming = []; this.newIncoming = [];
this.stateChangeCallbacks = {}; this.stateChangeCallbacks = new Map();
}, },
onStateChange(name, callback) { onStateChange(key, callback) {
this.stateChangeCallbacks[name] = callback; this.stateChangeCallbacks.set(key, callback);
},
offStateChange(key) {
this.stateChangeCallbacks.delete(key);
}, },
startTracking() { startTracking() {
if (this.isTracking) { if (this.isTracking) {
return; return Promise.resolve();
} }
this._establishChannels(); this._establishChannels();
@ -46,13 +52,13 @@ const PrivateMessageTopicTrackingState = EmberObject.extend({
_establishChannels() { _establishChannels() {
this.messageBus.subscribe( this.messageBus.subscribe(
this._userChannel(), this.userChannel(),
this._processMessage.bind(this) this._processMessage.bind(this)
); );
this.currentUser.groupsWithMessages?.forEach((group) => { this.currentUser.groupsWithMessages?.forEach((group) => {
this.messageBus.subscribe( this.messageBus.subscribe(
this._groupChannel(group.id), this.groupChannel(group.id),
this._processMessage.bind(this) this._processMessage.bind(this)
); );
}); });
@ -111,11 +117,11 @@ const PrivateMessageTopicTrackingState = EmberObject.extend({
return this.states.get(topicId); return this.states.get(topicId);
}, },
_userChannel() { userChannel() {
return `${this.CHANNEL_PREFIX}/user/${this.currentUser.id}`; return `${this.CHANNEL_PREFIX}/user/${this.currentUser.id}`;
}, },
_groupChannel(groupId) { groupChannel(groupId) {
return `${this.CHANNEL_PREFIX}/group/${groupId}`; return `${this.CHANNEL_PREFIX}/group/${groupId}`;
}, },
@ -263,7 +269,7 @@ const PrivateMessageTopicTrackingState = EmberObject.extend({
_afterStateChange() { _afterStateChange() {
this.incrementProperty("statesModificationCounter"); this.incrementProperty("statesModificationCounter");
Object.values(this.stateChangeCallbacks).forEach((callback) => callback()); this.stateChangeCallbacks.forEach((callback) => callback());
}, },
}); });

View File

@ -1,13 +1,17 @@
import { test } from "qunit"; import { test } from "qunit";
import I18n from "I18n";
import { click, currentURL, visit } from "@ember/test-helpers"; import { click, currentURL, settled, visit } from "@ember/test-helpers";
import { import {
acceptance, acceptance,
exists, exists,
publishToMessageBus,
query,
queryAll, queryAll,
updateCurrentUser, updateCurrentUser,
} from "discourse/tests/helpers/qunit-helpers"; } from "discourse/tests/helpers/qunit-helpers";
import { NotificationLevels } from "discourse/lib/notification-levels";
acceptance( acceptance(
"Sidebar - Messages Section - enable_personal_messages disabled", "Sidebar - Messages Section - enable_personal_messages disabled",
@ -355,5 +359,192 @@ acceptance(
"foo_group messages inbox filter links are not shown" "foo_group messages inbox filter links are not shown"
); );
}); });
test("new and unread counts for group messages", async function (assert) {
updateCurrentUser({
groups: [
{
id: 1,
name: "group1",
has_messages: true,
},
],
});
await visit("/");
const pmTopicTrackingState = this.container.lookup(
"pm-topic-tracking-state:main"
);
publishToMessageBus(pmTopicTrackingState.groupChannel(1), {
topic_id: 1,
message_type: "unread",
payload: {
last_read_post_number: 1,
highest_post_number: 2,
notification_level: NotificationLevels.TRACKING,
group_ids: [1],
},
});
publishToMessageBus(pmTopicTrackingState.groupChannel(1), {
topic_id: 2,
message_type: "new_topic",
payload: {
last_read_post_number: null,
highest_post_number: 1,
notification_level: NotificationLevels.TRACKING,
group_ids: [1],
},
});
await click(
".sidebar-section-messages .sidebar-section-link-group-messages-inbox.group1"
);
assert.strictEqual(
query(
".sidebar-section-messages .sidebar-section-link-group-messages-unread.group1"
).textContent.trim(),
I18n.t("sidebar.sections.messages.links.unread_with_count", {
count: 1,
}),
"displays 1 count for group1 unread inbox filter link"
);
assert.strictEqual(
query(
".sidebar-section-messages .sidebar-section-link-group-messages-new.group1"
).textContent.trim(),
I18n.t("sidebar.sections.messages.links.new_with_count", {
count: 1,
}),
"displays 1 count for group1 new inbox filter link"
);
publishToMessageBus(pmTopicTrackingState.groupChannel(1), {
topic_id: 2,
message_type: "read",
payload: {
last_read_post_number: 1,
highest_post_number: 1,
notification_level: NotificationLevels.TRACKING,
group_ids: [1],
},
});
await settled();
assert.strictEqual(
query(
".sidebar-section-messages .sidebar-section-link-group-messages-new.group1"
).textContent.trim(),
I18n.t("sidebar.sections.messages.links.new"),
"removes count for group1 new inbox filter link"
);
});
test("new and unread counts for personal messages", async function (assert) {
await visit("/");
const pmTopicTrackingState = this.container.lookup(
"pm-topic-tracking-state:main"
);
publishToMessageBus(pmTopicTrackingState.userChannel(), {
topic_id: 1,
message_type: "unread",
payload: {
last_read_post_number: 1,
highest_post_number: 2,
notification_level: NotificationLevels.TRACKING,
group_ids: [],
},
});
await settled();
await click(
".sidebar-section-messages .sidebar-section-link-personal-messages-inbox"
);
assert.strictEqual(
query(
".sidebar-section-messages .sidebar-section-link-personal-messages-unread"
).textContent.trim(),
I18n.t("sidebar.sections.messages.links.unread_with_count", {
count: 1,
}),
"displays 1 count for the unread inbox filter link"
);
publishToMessageBus(pmTopicTrackingState.userChannel(), {
topic_id: 2,
message_type: "unread",
payload: {
last_read_post_number: 1,
highest_post_number: 2,
notification_level: NotificationLevels.TRACKING,
group_ids: [],
},
});
await settled();
assert.strictEqual(
query(
".sidebar-section-messages .sidebar-section-link-personal-messages-unread"
).textContent.trim(),
I18n.t("sidebar.sections.messages.links.unread_with_count", {
count: 2,
}),
"displays 2 count for the unread inbox filter link"
);
publishToMessageBus(pmTopicTrackingState.userChannel(), {
topic_id: 3,
message_type: "new_topic",
payload: {
last_read_post_number: null,
highest_post_number: 1,
notification_level: NotificationLevels.TRACKING,
group_ids: [],
},
});
await settled();
assert.strictEqual(
query(
".sidebar-section-messages .sidebar-section-link-personal-messages-new"
).textContent.trim(),
I18n.t("sidebar.sections.messages.links.new_with_count", {
count: 1,
}),
"displays 1 count for the new inbox filter link"
);
publishToMessageBus(pmTopicTrackingState.userChannel(), {
topic_id: 3,
message_type: "read",
payload: {
last_read_post_number: 1,
highest_post_number: 1,
notification_level: NotificationLevels.TRACKING,
group_ids: [],
},
});
await settled();
assert.strictEqual(
query(
".sidebar-section-messages .sidebar-section-link-personal-messages-new"
).textContent.trim(),
I18n.t("sidebar.sections.messages.links.new"),
"removes the count from the new inbox filter link"
);
});
} }
); );

View File

@ -4051,7 +4051,9 @@ en:
inbox: "Inbox" inbox: "Inbox"
sent: "Sent" sent: "Sent"
new: "New" new: "New"
new_with_count: "New (%{count})"
unread: "Unread" unread: "Unread"
unread_with_count: "Unread (%{count})"
archive: "Archive" archive: "Archive"
tags: tags:
no_tracked_tags: "You are not tracking any tags." no_tracked_tags: "You are not tracking any tags."