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 ); } }, });