232 lines
6.7 KiB
JavaScript
232 lines
6.7 KiB
JavaScript
import Component from "@ember/component";
|
|
import { action } from "@ember/object";
|
|
import ChatChannel from "discourse/plugins/chat/discourse/models/chat-channel";
|
|
import { ajax } from "discourse/lib/ajax";
|
|
import { bind } from "discourse-common/utils/decorators";
|
|
import { schedule } from "@ember/runloop";
|
|
import { inject as service } from "@ember/service";
|
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
|
import discourseDebounce from "discourse-common/lib/debounce";
|
|
import { INPUT_DELAY } from "discourse-common/config/environment";
|
|
import { isPresent } from "@ember/utils";
|
|
|
|
export default Component.extend({
|
|
chat: service(),
|
|
tagName: "",
|
|
filter: "",
|
|
channels: null,
|
|
searchIndex: 0,
|
|
loading: false,
|
|
chatChannelsManager: service(),
|
|
|
|
didInsertElement() {
|
|
this._super(...arguments);
|
|
|
|
this.appEvents.on("chat-channel-selector-modal:close", this.close);
|
|
document.addEventListener("keyup", this.onKeyUp);
|
|
document
|
|
.getElementById("chat-channel-selector-modal-inner")
|
|
?.addEventListener("mouseover", this.mouseover);
|
|
document.getElementById("chat-channel-selector-input")?.focus();
|
|
|
|
this.getInitialChannels();
|
|
},
|
|
|
|
willDestroyElement() {
|
|
this._super(...arguments);
|
|
|
|
this.appEvents.off("chat-channel-selector-modal:close", this.close);
|
|
document.removeEventListener("keyup", this.onKeyUp);
|
|
document
|
|
.getElementById("chat-channel-selector-modal-inner")
|
|
?.removeEventListener("mouseover", this.mouseover);
|
|
},
|
|
|
|
@bind
|
|
mouseover(e) {
|
|
if (e.target.classList.contains("chat-channel-selection-row")) {
|
|
let channel;
|
|
const id = parseInt(e.target.dataset.id, 10);
|
|
if (e.target.classList.contains("channel-row")) {
|
|
channel = this.channels.findBy("id", id);
|
|
} else {
|
|
channel = this.channels.find((c) => c.user && c.id === id);
|
|
}
|
|
channel?.set("focused", true);
|
|
this.channels.forEach((c) => {
|
|
if (c !== channel) {
|
|
c.set("focused", false);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
@bind
|
|
onKeyUp(e) {
|
|
if (e.key === "Enter") {
|
|
let focusedChannel = this.channels.find((c) => c.focused);
|
|
this.switchChannel(focusedChannel);
|
|
e.preventDefault();
|
|
} else if (e.key === "ArrowDown") {
|
|
this.arrowNavigateChannels("down");
|
|
e.preventDefault();
|
|
} else if (e.key === "ArrowUp") {
|
|
this.arrowNavigateChannels("up");
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
|
|
arrowNavigateChannels(direction) {
|
|
const indexOfFocused = this.channels.findIndex((c) => c.focused);
|
|
if (indexOfFocused > -1) {
|
|
const nextIndex = direction === "down" ? 1 : -1;
|
|
const nextChannel = this.channels[indexOfFocused + nextIndex];
|
|
if (nextChannel) {
|
|
this.channels[indexOfFocused].set("focused", false);
|
|
nextChannel.set("focused", true);
|
|
}
|
|
} else {
|
|
this.channels[0].set("focused", true);
|
|
}
|
|
|
|
schedule("afterRender", () => {
|
|
let focusedChannel = document.querySelector(
|
|
"#chat-channel-selector-modal-inner .chat-channel-selection-row.focused"
|
|
);
|
|
focusedChannel?.scrollIntoView({ block: "nearest", inline: "start" });
|
|
});
|
|
},
|
|
|
|
@action
|
|
switchChannel(channel) {
|
|
if (channel.user) {
|
|
return this.fetchOrCreateChannelForUser(channel).then((response) => {
|
|
const newChannel = this.chatChannelsManager.store(response.channel);
|
|
return this.chatChannelsManager.follow(newChannel).then((c) => {
|
|
this.chat.openChannel(c);
|
|
this.close();
|
|
});
|
|
});
|
|
} else {
|
|
return this.chatChannelsManager.follow(channel).then((c) => {
|
|
this.chat.openChannel(c);
|
|
this.close();
|
|
});
|
|
}
|
|
},
|
|
|
|
@action
|
|
search(value) {
|
|
if (isPresent(value?.trim())) {
|
|
discourseDebounce(
|
|
this,
|
|
this.fetchChannelsFromServer,
|
|
value?.trim(),
|
|
INPUT_DELAY
|
|
);
|
|
} else {
|
|
discourseDebounce(this, this.getInitialChannels, INPUT_DELAY);
|
|
}
|
|
},
|
|
|
|
@action
|
|
fetchChannelsFromServer(filter) {
|
|
if (this.isDestroyed || this.isDestroying) {
|
|
return;
|
|
}
|
|
|
|
this.setProperties({
|
|
loading: true,
|
|
searchIndex: this.searchIndex + 1,
|
|
});
|
|
const thisSearchIndex = this.searchIndex;
|
|
ajax("/chat/api/chatables", { data: { filter } })
|
|
.then((searchModel) => {
|
|
if (this.searchIndex === thisSearchIndex) {
|
|
this.set("searchModel", searchModel);
|
|
const channels = searchModel.public_channels.concat(
|
|
searchModel.direct_message_channels,
|
|
searchModel.users
|
|
);
|
|
channels.forEach((c) => {
|
|
if (c.username) {
|
|
c.user = true; // This is used by the `chat-channel-selection-row` component
|
|
}
|
|
});
|
|
this.setProperties({
|
|
channels: channels.map((channel) => {
|
|
return channel.user
|
|
? ChatChannel.create(channel)
|
|
: this.chatChannelsManager.store(channel);
|
|
}),
|
|
loading: false,
|
|
});
|
|
this.focusFirstChannel(this.channels);
|
|
}
|
|
})
|
|
.catch(popupAjaxError);
|
|
},
|
|
|
|
@action
|
|
getInitialChannels() {
|
|
if (this.isDestroyed || this.isDestroying) {
|
|
return;
|
|
}
|
|
|
|
const channels = this.getChannelsWithFilter(this.filter);
|
|
this.set("channels", channels);
|
|
this.focusFirstChannel(channels);
|
|
},
|
|
|
|
@action
|
|
fetchOrCreateChannelForUser(user) {
|
|
return ajax("/chat/direct_messages/create.json", {
|
|
method: "POST",
|
|
data: { usernames: [user.username] },
|
|
}).catch(popupAjaxError);
|
|
},
|
|
|
|
focusFirstChannel(channels) {
|
|
channels.forEach((c) => c.set("focused", false));
|
|
channels[0]?.set("focused", true);
|
|
},
|
|
|
|
getChannelsWithFilter(filter, opts = { excludeActiveChannel: true }) {
|
|
let sortedChannels = this.chatChannelsManager.channels.sort((a, b) => {
|
|
return new Date(a.last_message_sent_at) > new Date(b.last_message_sent_at)
|
|
? -1
|
|
: 1;
|
|
});
|
|
|
|
const trimmedFilter = filter.trim();
|
|
const lowerCasedFilter = filter.toLowerCase();
|
|
|
|
return sortedChannels.filter((channel) => {
|
|
if (
|
|
opts.excludeActiveChannel &&
|
|
this.chat.activeChannel?.id === channel.id
|
|
) {
|
|
return false;
|
|
}
|
|
if (!trimmedFilter.length) {
|
|
return true;
|
|
}
|
|
|
|
if (channel.isDirectMessageChannel) {
|
|
let userFound = false;
|
|
channel.chatable.users.forEach((user) => {
|
|
if (
|
|
user.username.toLowerCase().includes(lowerCasedFilter) ||
|
|
user.name?.toLowerCase().includes(lowerCasedFilter)
|
|
) {
|
|
return (userFound = true);
|
|
}
|
|
});
|
|
return userFound;
|
|
} else {
|
|
return channel.title.toLowerCase().includes(lowerCasedFilter);
|
|
}
|
|
});
|
|
},
|
|
});
|