Files
David TaylorandGitHub 666b5dddde DEV: Move discourse-common/(utils|lib) to discourse/lib (#30733)
`discourse-common` was created in the past to share logic between the
'wizard' app and the main 'discourse' app. Since then, the wizard has
been consolidated into the main app, so the separation of
`discourse-common` is no longer useful.

This commit moves `discourse-common/(lib|utils)/*` into
`discourse/lib/*`, adds shims for the imports, and updates existing
uses in core.
2025-01-13 13:02:49 +00:00

534 lines
16 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { tracked } from "@glimmer/tracking";
import { service } from "@ember/service";
import { dasherize } from "@ember/string";
import { htmlSafe } from "@ember/template";
import UserStatusMessage from "discourse/components/user-status-message";
import { decorateUsername } from "discourse/helpers/decorate-username-selector";
import { avatarUrl } from "discourse/lib/avatar-utils";
import { bind } from "discourse/lib/decorators";
import { withPluginApi } from "discourse/lib/plugin-api";
import { emojiUnescape } from "discourse/lib/text";
import { escapeExpression } from "discourse/lib/utilities";
import { i18n } from "discourse-i18n";
import ChatModalNewMessage from "discourse/plugins/chat/discourse/components/chat/modal/new-message";
import {
CHAT_PANEL,
initSidebarState,
} from "discourse/plugins/chat/discourse/lib/init-sidebar-state";
export default {
name: "chat-sidebar",
initialize(container) {
this.chatService = container.lookup("service:chat");
if (!this.chatService.userCanChat) {
return;
}
this.siteSettings = container.lookup("service:site-settings");
this.currentUser = container.lookup("service:current-user");
withPluginApi("1.8.0", (api) => {
const chatStateManager = container.lookup("service:chat-state-manager");
api.addSidebarPanel(
(BaseCustomSidebarPanel) =>
class ChatSidebarPanel extends BaseCustomSidebarPanel {
key = CHAT_PANEL;
switchButtonLabel = i18n("sidebar.panels.chat.label");
switchButtonIcon = "d-chat";
get switchButtonDefaultUrl() {
return chatStateManager.lastKnownChatURL || "/chat";
}
}
);
initSidebarState(api, api.getCurrentUser());
});
withPluginApi("1.3.0", (api) => {
const chatChannelsManager = container.lookup(
"service:chat-channels-manager"
);
const chatStateManager = container.lookup("service:chat-state-manager");
api.addSidebarSection(
(BaseCustomSidebarSection, BaseCustomSidebarSectionLink) => {
const SidebarChatMyThreadsSectionLink = class extends BaseCustomSidebarSectionLink {
route = "chat.threads";
text = i18n("chat.my_threads.title");
title = i18n("chat.my_threads.title");
name = "user-threads";
prefixType = "icon";
prefixValue = "discourse-threads";
suffixType = "icon";
constructor() {
super(...arguments);
if (container.isDestroyed) {
return;
}
}
get suffixValue() {
return chatChannelsManager.allChannels.some(
(channel) => channel.unreadThreadsCount > 0
)
? "circle"
: "";
}
get suffixCSSClass() {
return chatChannelsManager.allChannels.some(
(channel) => channel.tracking.watchedThreadsUnreadCount > 0
)
? "urgent"
: "unread";
}
};
const SidebarChatMyThreadsSection = class extends BaseCustomSidebarSection {
@service chatChannelsManager;
// we only show `My Threads` link
hideSectionHeader = true;
name = "user-threads";
// sidebar API doesn’t let you have undefined values
// even if you don't show the section’s header
title = "";
get links() {
return [new SidebarChatMyThreadsSectionLink()];
}
get text() {
return null;
}
get displaySection() {
return this.chatChannelsManager.hasThreadedChannels;
}
};
return SidebarChatMyThreadsSection;
},
CHAT_PANEL
);
if (this.siteSettings.enable_public_channels) {
api.addSidebarSection(
(BaseCustomSidebarSection, BaseCustomSidebarSectionLink) => {
const SidebarChatChannelsSectionLink = class extends BaseCustomSidebarSectionLink {
constructor({ channel, chatService }) {
super(...arguments);
this.channel = channel;
this.chatService = chatService;
this.chatStateManager = chatStateManager;
}
get name() {
return dasherize(this.channel.slugifiedTitle);
}
get classNames() {
const classes = [];
if (this.channel.currentUserMembership.muted) {
classes.push("sidebar-section-link--muted");
}
if (
this.channel.id === this.chatService.activeChannel?.id &&
(this.chatStateManager.isDrawerExpanded ||
this.chatStateManager.isFullPageActive)
) {
classes.push("sidebar-section-link--active");
}
classes.push(`channel-${this.channel.id}`);
return classes.join(" ");
}
get route() {
return "chat.channel";
}
get models() {
return this.channel.routeModels;
}
get text() {
return htmlSafe(emojiUnescape(this.channel.escapedTitle));
}
get prefixType() {
return "icon";
}
get prefixValue() {
return "d-chat";
}
get prefixColor() {
return this.channel.chatable.color;
}
get title() {
return this.channel.escapedDescription
? htmlSafe(this.channel.escapedDescription)
: `${this.channel.escapedTitle} ${i18n("chat.title")}`;
}
get prefixBadge() {
return this.channel.chatable.read_restricted ? "lock" : "";
}
get suffixType() {
return "icon";
}
get suffixValue() {
return this.channel.tracking.unreadCount > 0 ||
// We want to do this so we don't show a blue dot if the user is inside
// the channel and a new unread thread comes in.
(this.chatService.activeChannel?.id !== this.channel.id &&
this.channel.unreadThreadsCountSinceLastViewed > 0)
? "circle"
: "";
}
get suffixCSSClass() {
return this.channel.tracking.mentionCount > 0 ||
this.channel.tracking.watchedThreadsUnreadCount > 0
? "urgent"
: "unread";
}
};
const SidebarChatChannelsSection = class extends BaseCustomSidebarSection {
@service currentUser;
@service chatStateManager;
@tracked
currentUserCanJoinPublicChannels =
this.currentUser &&
(this.currentUser.staff ||
this.currentUser.has_joinable_public_channels);
constructor() {
super(...arguments);
if (container.isDestroyed) {
return;
}
this.chatService = container.lookup("service:chat");
this.chatChannelsManager = container.lookup(
"service:chat-channels-manager"
);
this.router = container.lookup("service:router");
}
get sectionLinks() {
return this.chatChannelsManager.publicMessageChannels.map(
(channel) =>
new SidebarChatChannelsSectionLink({
channel,
chatService: this.chatService,
})
);
}
get name() {
return "chat-channels";
}
get title() {
return i18n("chat.chat_channels");
}
get text() {
return i18n("chat.chat_channels");
}
get actions() {
return [
{
id: "browseChannels",
title: i18n("chat.channels_list_popup.browse"),
action: () => this.router.transitionTo("chat.browse.open"),
},
];
}
get actionsIcon() {
return "pencil";
}
get links() {
return this.sectionLinks;
}
get displaySection() {
return (
this.chatStateManager.hasPreloadedChannels &&
(this.sectionLinks.length > 0 ||
this.currentUserCanJoinPublicChannels)
);
}
};
return SidebarChatChannelsSection;
},
CHAT_PANEL
);
}
api.addSidebarSection(
(BaseCustomSidebarSection, BaseCustomSidebarSectionLink) => {
const SidebarChatDirectMessagesSectionLink = class extends BaseCustomSidebarSectionLink {
route = "chat.channel";
suffixType = "icon";
hoverType = "icon";
hoverValue = "xmark";
hoverTitle = i18n("chat.direct_messages.close");
constructor({ channel, chatService, currentUser }) {
super(...arguments);
this.channel = channel;
this.chatService = chatService;
this.currentUser = currentUser;
this.chatStateManager = chatStateManager;
if (this.oneOnOneMessage) {
const user = this.channel.chatable.users[0];
if (user.username !== i18n("chat.deleted_chat_username")) {
user.statusManager.trackStatus();
}
}
}
@bind
willDestroy() {
if (this.oneOnOneMessage) {
this.channel.chatable.users[0].statusManager.stopTrackingStatus();
}
}
get oneOnOneMessage() {
return this.channel.chatable.users.length === 1;
}
get contentComponentArgs() {
return this.channel.chatable.users[0].get("status");
}
get contentComponent() {
if (this.oneOnOneMessage) {
return UserStatusMessage;
}
}
get name() {
return this.channel.slugifiedTitle;
}
get classNames() {
const classes = [];
if (this.channel.currentUserMembership.muted) {
classes.push("sidebar-section-link--muted");
}
if (
this.channel.id === this.chatService.activeChannel?.id &&
(this.chatStateManager.isDrawerExpanded ||
this.chatStateManager.isFullPageActive)
) {
classes.push("sidebar-section-link--active");
}
classes.push(`channel-${this.channel.id}`);
return classes.join(" ");
}
get models() {
return this.channel.routeModels;
}
get title() {
if (this.channel.chatable.group) {
return i18n("chat.placeholder_channel", {
channelName: this.channel.escapedTitle,
});
} else {
return i18n("chat.placeholder_users", {
commaSeparatedNames: this.channel.escapedTitle,
});
}
}
get text() {
if (this.channel.chatable.group) {
return this.channel.title;
} else {
const username = this.channel.escapedTitle.replaceAll("@", "");
return htmlSafe(
`${escapeExpression(username)}${decorateUsername(
escapeExpression(username)
)}`
);
}
}
get prefixType() {
if (this.channel.iconUploadUrl) {
return "image";
} else if (this.channel.chatable.group) {
return "text";
} else {
return "image";
}
}
get prefixValue() {
if (this.channel.iconUploadUrl) {
return this.channel.iconUploadUrl;
} else if (this.channel.chatable.group) {
return this.channel.membershipsCount;
} else {
return avatarUrl(
this.channel.chatable.users[0].avatar_template,
"tiny"
);
}
}
get prefixCSSClass() {
const activeUsers = this.chatService.presenceChannel.users;
const user = this.channel.chatable.users[0];
if (
!!activeUsers?.findBy("id", user?.id) ||
!!activeUsers?.findBy("username", user?.username)
) {
return "active";
}
return "";
}
get suffixValue() {
return this.channel.tracking.unreadCount > 0 ||
this.channel.unreadThreadsCountSinceLastViewed > 0
? "circle"
: "";
}
get suffixCSSClass() {
return this.channel.tracking.unreadCount > 0 ||
this.channel.tracking.mentionCount > 0 ||
this.channel.tracking.watchedThreadsUnreadCount > 0
? "urgent"
: "unread";
}
get hoverAction() {
return (event) => {
event.stopPropagation();
event.preventDefault();
this.chatService.unfollowChannel(this.channel);
};
}
};
const SidebarChatDirectMessagesSection = class extends BaseCustomSidebarSection {
@service site;
@service modal;
@service router;
@service currentUser;
@service chatStateManager;
@tracked
userCanDirectMessage = this.chatService.userCanDirectMessage;
constructor() {
super(...arguments);
if (container.isDestroyed) {
return;
}
this.chatService = container.lookup("service:chat");
this.chatChannelsManager = container.lookup(
"service:chat-channels-manager"
);
}
get sectionLinks() {
return this.chatChannelsManager.truncatedDirectMessageChannels.map(
(channel) =>
new SidebarChatDirectMessagesSectionLink({
channel,
chatService: this.chatService,
currentUser: this.currentUser,
})
);
}
get name() {
return "chat-dms";
}
get title() {
return i18n("chat.direct_messages.title");
}
get text() {
return i18n("chat.direct_messages.title");
}
get actions() {
if (!this.userCanDirectMessage) {
return [];
}
return [
{
id: "startDm",
title: i18n("chat.direct_messages.new"),
action: () => {
this.modal.show(ChatModalNewMessage);
},
},
];
}
get actionsIcon() {
return "plus";
}
get links() {
return this.sectionLinks;
}
get displaySection() {
return (
this.chatStateManager.hasPreloadedChannels &&
(this.sectionLinks.length > 0 || this.userCanDirectMessage)
);
}
};
return SidebarChatDirectMessagesSection;
},
"chat"
);
});
},
};