- Note this is also tweaking the UI a little bit as we are now using links/buttons in the header as needed - It disables the find ideal channel in drawer mode, if loading `/chat` in drawer mode it will either reopen at the last position or just stay on index
317 lines
7.6 KiB
JavaScript
317 lines
7.6 KiB
JavaScript
import { caretPosition } from "discourse/lib/utilities";
|
|
import { isEmpty } from "@ember/utils";
|
|
import Component from "@ember/component";
|
|
import { action } from "@ember/object";
|
|
import discourseDebounce from "discourse-common/lib/debounce";
|
|
import discourseComputed, { bind } from "discourse-common/utils/decorators";
|
|
import { INPUT_DELAY } from "discourse-common/config/environment";
|
|
import { inject as service } from "@ember/service";
|
|
import { schedule } from "@ember/runloop";
|
|
import { gt, not } from "@ember/object/computed";
|
|
import { createDirectMessageChannelDraft } from "discourse/plugins/chat/discourse/models/chat-channel";
|
|
|
|
export default Component.extend({
|
|
tagName: "",
|
|
users: null,
|
|
selectedUsers: null,
|
|
term: null,
|
|
isFiltering: false,
|
|
isFilterFocused: false,
|
|
highlightedSelectedUser: null,
|
|
focusedUser: null,
|
|
chat: service(),
|
|
router: service(),
|
|
chatStateManager: service(),
|
|
isLoading: false,
|
|
onSwitchChannel: null,
|
|
|
|
init() {
|
|
this._super(...arguments);
|
|
|
|
this.set("users", []);
|
|
this.set("selectedUsers", []);
|
|
this.set("channel", createDirectMessageChannelDraft());
|
|
},
|
|
|
|
didInsertElement() {
|
|
this._super(...arguments);
|
|
|
|
this.filterUsernames();
|
|
},
|
|
|
|
didReceiveAttrs() {
|
|
this._super(...arguments);
|
|
|
|
this.set("term", null);
|
|
|
|
this.focusFilter();
|
|
|
|
if (!this.hasSelection) {
|
|
this.filterUsernames();
|
|
}
|
|
},
|
|
|
|
hasSelection: gt("channel.chatable.users.length", 0),
|
|
|
|
@discourseComputed
|
|
chatProgressBarContainer() {
|
|
return document.querySelector("#chat-progress-bar-container");
|
|
},
|
|
|
|
@bind
|
|
filterUsernames(term = null) {
|
|
this.set("isFiltering", true);
|
|
|
|
this.chat
|
|
.searchPossibleDirectMessageUsers({
|
|
term,
|
|
limit: 6,
|
|
exclude: this.channel.chatable?.users?.mapBy("username") || [],
|
|
lastSeenUsers: isEmpty(term) ? true : false,
|
|
})
|
|
.then((r) => {
|
|
if (this.isDestroying || this.isDestroyed) {
|
|
return;
|
|
}
|
|
|
|
if (r !== "__CANCELLED") {
|
|
this.set("users", r.users || []);
|
|
this.set("focusedUser", this.users.firstObject);
|
|
}
|
|
})
|
|
.finally(() => {
|
|
if (this.isDestroying || this.isDestroyed) {
|
|
return;
|
|
}
|
|
|
|
this.set("isFiltering", false);
|
|
});
|
|
},
|
|
|
|
shouldRenderResults: not("isFiltering"),
|
|
|
|
@action
|
|
selectUser(user) {
|
|
this.selectedUsers.pushObject(user);
|
|
this.users.removeObject(user);
|
|
this.set("users", []);
|
|
this.set("focusedUser", null);
|
|
this.set("highlightedSelectedUser", null);
|
|
this.set("term", null);
|
|
this.focusFilter();
|
|
this.onChangeSelectedUsers?.(this.selectedUsers);
|
|
},
|
|
|
|
@action
|
|
deselectUser(user) {
|
|
this.users.removeObject(user);
|
|
this.selectedUsers.removeObject(user);
|
|
this.set("focusedUser", this.users.firstObject);
|
|
this.set("highlightedSelectedUser", null);
|
|
this.set("term", null);
|
|
|
|
if (isEmpty(this.selectedUsers)) {
|
|
this.filterUsernames();
|
|
}
|
|
|
|
this.focusFilter();
|
|
this.onChangeSelectedUsers?.(this.selectedUsers);
|
|
},
|
|
|
|
@action
|
|
focusFilter() {
|
|
this.set("isFilterFocused", true);
|
|
|
|
schedule("afterRender", () => {
|
|
document.querySelector(".filter-usernames")?.focus();
|
|
});
|
|
},
|
|
|
|
@action
|
|
onFilterInput(term) {
|
|
this.set("term", term);
|
|
this.set("users", []);
|
|
|
|
if (!term?.length) {
|
|
return;
|
|
}
|
|
|
|
this.set("isFiltering", true);
|
|
|
|
discourseDebounce(this, this.filterUsernames, term, INPUT_DELAY);
|
|
},
|
|
|
|
@action
|
|
handleUserKeyUp(user, event) {
|
|
if (event.key === "Enter") {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
this.selectUser(user);
|
|
}
|
|
},
|
|
|
|
@action
|
|
onFilterInputFocusOut() {
|
|
this.set("isFilterFocused", false);
|
|
this.set("highlightedSelectedUser", null);
|
|
},
|
|
|
|
@action
|
|
leaveChannel() {
|
|
this.router.transitionTo("chat.index");
|
|
},
|
|
|
|
@action
|
|
handleFilterKeyUp(event) {
|
|
if (event.key === "Tab") {
|
|
const enabledComposer = document.querySelector(".chat-composer-input");
|
|
if (enabledComposer && !enabledComposer.disabled) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
enabledComposer.focus();
|
|
}
|
|
}
|
|
|
|
if (
|
|
(event.key === "Enter" || event.key === "Backspace") &&
|
|
this.highlightedSelectedUser
|
|
) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
this.deselectUser(this.highlightedSelectedUser);
|
|
return;
|
|
}
|
|
|
|
if (event.key === "Backspace" && isEmpty(this.term) && this.hasSelection) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
this.deselectUser(this.channel.chatable.users.lastObject);
|
|
}
|
|
|
|
if (event.key === "Enter" && this.focusedUser) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
this.selectUser(this.focusedUser);
|
|
}
|
|
|
|
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
this._handleVerticalArrowKeys(event);
|
|
}
|
|
|
|
if (event.key === "Escape" && this.highlightedSelectedUser) {
|
|
this.set("highlightedSelectedUser", null);
|
|
}
|
|
|
|
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
|
|
this._handleHorizontalArrowKeys(event);
|
|
}
|
|
},
|
|
|
|
_firstSelectWithArrows(event) {
|
|
if (event.key === "ArrowRight") {
|
|
return;
|
|
}
|
|
|
|
if (event.key === "ArrowLeft") {
|
|
const position = caretPosition(
|
|
document.querySelector(".filter-usernames")
|
|
);
|
|
if (position > 0) {
|
|
return;
|
|
} else {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
this.set(
|
|
"highlightedSelectedUser",
|
|
this.channel.chatable.users.lastObject
|
|
);
|
|
}
|
|
}
|
|
},
|
|
|
|
_changeSelectionWithArrows(event) {
|
|
if (event.key === "ArrowRight") {
|
|
if (
|
|
this.highlightedSelectedUser === this.channel.chatable.users.lastObject
|
|
) {
|
|
this.set("highlightedSelectedUser", null);
|
|
return;
|
|
}
|
|
|
|
if (this.channel.chatable.users.length === 1) {
|
|
return;
|
|
}
|
|
|
|
this._highlightNextSelectedUser(event.key === "ArrowLeft" ? -1 : 1);
|
|
}
|
|
|
|
if (event.key === "ArrowLeft") {
|
|
if (this.channel.chatable.users.length === 1) {
|
|
return;
|
|
}
|
|
|
|
this._highlightNextSelectedUser(event.key === "ArrowLeft" ? -1 : 1);
|
|
}
|
|
},
|
|
|
|
_highlightNextSelectedUser(modifier) {
|
|
const newIndex =
|
|
this.channel.chatable.users.indexOf(this.highlightedSelectedUser) +
|
|
modifier;
|
|
|
|
if (this.channel.chatable.users.objectAt(newIndex)) {
|
|
this.set(
|
|
"highlightedSelectedUser",
|
|
this.channel.chatable.users.objectAt(newIndex)
|
|
);
|
|
} else {
|
|
this.set(
|
|
"highlightedSelectedUser",
|
|
event.key === "ArrowLeft"
|
|
? this.channel.chatable.users.lastObject
|
|
: this.channel.chatable.users.firstObject
|
|
);
|
|
}
|
|
},
|
|
|
|
_handleHorizontalArrowKeys(event) {
|
|
const position = caretPosition(document.querySelector(".filter-usernames"));
|
|
if (position > 0) {
|
|
return;
|
|
}
|
|
|
|
if (!this.highlightedSelectedUser) {
|
|
this._firstSelectWithArrows(event);
|
|
} else {
|
|
this._changeSelectionWithArrows(event);
|
|
}
|
|
},
|
|
|
|
_handleVerticalArrowKeys(event) {
|
|
if (isEmpty(this.users)) {
|
|
return;
|
|
}
|
|
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
if (!this.focusedUser) {
|
|
this.set("focusedUser", this.users.firstObject);
|
|
return;
|
|
}
|
|
|
|
const modifier = event.key === "ArrowUp" ? -1 : 1;
|
|
const newIndex = this.users.indexOf(this.focusedUser) + modifier;
|
|
|
|
if (this.users.objectAt(newIndex)) {
|
|
this.set("focusedUser", this.users.objectAt(newIndex));
|
|
} else {
|
|
this.set(
|
|
"focusedUser",
|
|
event.key === "ArrowUp" ? this.users.lastObject : this.users.firstObject
|
|
);
|
|
}
|
|
},
|
|
});
|