DEV: Convert choose-topic to glimmer (#27229)

This commit is contained in:
Jarek Radosz 2024-05-29 17:19:52 +02:00 committed by GitHub
parent eb8549e527
commit b1b218aa99
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 135 additions and 161 deletions

View File

@ -1,21 +1,19 @@
<label for="choose-topic-title">
{{#if this.labelIcon}}
{{d-icon this.labelIcon}}
{{/if}}
<span>{{i18n this.labelText}}</span>
</label>
<div>
<label for="choose-topic-title">
<span>{{i18n (or @label "choose_topic.title.search")}}</span>
</label>
<TextField
@value={{this.topicTitle}}
@placeholderKey="choose_topic.title.placeholder"
@id="choose-topic-title"
{{did-insert this.focusInput}}
/>
<input
{{on "keydown" this.ignoreEnter}}
{{on "input" this.onTopicTitleChange}}
type="text"
placeholder={{i18n "choose_topic.title.placeholder"}}
id="choose-topic-title"
/>
{{#if this.loading}}
<p>{{i18n "loading"}}</p>
{{else}}
{{#if this.noResults}}
{{#if this.loading}}
<p>{{i18n "loading"}}</p>
{{else if (not this.topics.length)}}
<p>{{i18n "choose_topic.none_found"}}</p>
{{else}}
<div class="choose-topic-list" role="radiogroup">
@ -23,11 +21,11 @@
<div class="controls existing-topic">
<label class="radio">
<Input
id={{concat "choose-topic-" t.id}}
{{on "click" (fn this.chooseTopic t)}}
@checked={{eq t.id this.selectedTopicId}}
@type="radio"
name="choose_topic_id"
{{on "click" (action "chooseTopic" t)}}
id={{concat "choose-topic-" t.id}}
/>
<TopicStatus @topic={{t}} @disableActions={{true}} />
<span class="topic-title">
@ -46,4 +44,4 @@
{{/each}}
</div>
{{/if}}
{{/if}}
</div>

View File

@ -1,154 +1,121 @@
import Component from "@ember/component";
import Component from "@glimmer/component";
import { tracked } from "@glimmer/tracking";
import { action } from "@ember/object";
import { isEmpty } from "@ember/utils";
import { isEmpty, isPresent } from "@ember/utils";
import { popupAjaxError } from "discourse/lib/ajax-error";
import { searchForTerm } from "discourse/lib/search";
import { INPUT_DELAY } from "discourse-common/config/environment";
import discourseComputed, {
debounce,
observes,
} from "discourse-common/utils/decorators";
import { debounce } from "discourse-common/utils/decorators";
export default Component.extend({
loading: null,
noResults: null,
topics: null,
selectedTopicId: null,
currentTopicId: null,
additionalFilters: null,
topicTitle: null,
label: null,
loadOnInit: false,
topicChangedCallback: null,
// args:
// topicChangedCallback
//
// optional:
// currentTopicId
// additionalFilters
// label
// loadOnInit
export default class ChooseTopic extends Component {
@tracked loading = true;
@tracked topics;
topicTitle;
init() {
this._super(...arguments);
constructor() {
super(...arguments);
this.additionalFilters = this.additionalFilters || "";
this.topicTitle = this.topicTitle || "";
if (this.loadOnInit && !isEmpty(this.additionalFilters)) {
searchForTerm(this.additionalFilters, {}).then((results) => {
if (results?.posts?.length > 0) {
this.set(
"topics",
results.posts
.mapBy("topic")
.filter((t) => t.id !== this.currentTopicId)
);
} else {
this.setProperties({ topics: null, loading: false });
}
});
if (this.args.loadOnInit && isPresent(this.args.additionalFilters)) {
this.initialSearch();
}
},
}
didInsertElement() {
this._super(...arguments);
async initialSearch() {
try {
const results = await searchForTerm(this.args.additionalFilters);
if (!results?.posts?.length) {
return;
}
document
.getElementById("choose-topic-title")
.addEventListener("keydown", this._handleEnter);
},
willDestroyElement() {
this._super(...arguments);
document
.getElementById("choose-topic-title")
.removeEventListener("keydown", this._handleEnter);
},
@observes("topicTitle")
topicTitleChanged() {
if (this.oldTopicTitle === this.topicTitle) {
return;
this.topics = results.posts
.mapBy("topic")
.filter((t) => t.id !== this.args.currentTopicId);
} catch (e) {
popupAjaxError(e);
} finally {
this.loading = false;
}
this.setProperties({
loading: true,
noResults: true,
selectedTopicId: null,
oldTopicTitle: this.topicTitle,
});
this.search(this.topicTitle);
},
@discourseComputed("label")
labelText(label) {
return label || "choose_topic.title.search";
},
@observes("topics")
topicsChanged() {
if (this.topics) {
this.set("noResults", this.topics.length === 0);
}
this.set("loading", false);
},
}
@debounce(INPUT_DELAY)
search(title) {
if (!this.element || this.isDestroying || this.isDestroyed) {
async search(title) {
if (this.isDestroying || this.isDestroyed) {
return;
}
if (isEmpty(title) && isEmpty(this.additionalFilters)) {
this.setProperties({ topics: null, loading: false });
this.onSearchEmptied?.();
if (isEmpty(title) && isEmpty(this.args.additionalFilters)) {
this.topics = null;
this.loading = false;
return;
}
const currentTopicId = this.currentTopicId;
const titleWithFilters = `${title} ${this.additionalFilters}`;
const searchParams = {};
const titleWithFilters = [title, this.args.additionalFilters]
.filter(Boolean)
.join(" ");
let searchParams;
if (!isEmpty(title)) {
searchParams.typeFilter = "topic";
searchParams.restrictToArchetype = "regular";
searchParams.searchForId = true;
if (isPresent(title)) {
searchParams = {
typeFilter: "topic",
restrictToArchetype: "regular",
searchForId: true,
};
}
searchForTerm(titleWithFilters, searchParams).then((results) => {
try {
const results = await searchForTerm(titleWithFilters, searchParams);
// search term changed after the request was fired but before we
// got a response, ignore results.
if (title !== this.topicTitle) {
return;
}
if (results?.posts?.length > 0) {
this.set(
"topics",
results.posts.mapBy("topic").filter((t) => t.id !== currentTopicId)
);
if (this.topics.length === 1) {
this.send("chooseTopic", this.topics[0]);
}
} else {
this.setProperties({ topics: null, loading: false });
if (!results?.posts?.length) {
this.topics = null;
return;
}
});
},
this.topics = results.posts
.mapBy("topic")
.filter((t) => t.id !== this.args.currentTopicId);
if (this.topics.length === 1) {
this.chooseTopic(this.topics[0]);
}
} catch (e) {
popupAjaxError(e);
} finally {
this.loading = false;
}
}
@action
async onTopicTitleChange(event) {
this.topicTitle = event.target.value;
this.loading = true;
await this.search(this.topicTitle);
}
@action
ignoreEnter(event) {
if (event.key === "Enter") {
event.preventDefault();
event.stopPropagation();
}
}
@action
chooseTopic(topic) {
this.set("selectedTopicId", topic.id);
if (this.topicChangedCallback) {
this.topicChangedCallback(topic);
}
},
@action
focusInput(element) {
if (this.autoFocus) {
element.focus();
}
},
_handleEnter(event) {
if (event.key === "Enter") {
event.preventDefault();
}
},
});
this.args.topicChangedCallback(topic);
}
}

View File

@ -6,12 +6,11 @@
>
<:body>
<ChooseTopic
@topicChangedCallback={{this.newTopicSelected}}
@currentTopicId={{@model.user.featured_topic.id}}
@selectedTopicId={{this.newFeaturedTopicId}}
@loadOnInit={{true}}
@additionalFilters="status:public"
@label="user.feature_topic_on_profile.search_label"
@topicChangedCallback={{this.newTopicSelected}}
@loadOnInit={{true}}
/>
</:body>
<:footer>

View File

@ -141,8 +141,8 @@
</p>
<form>
<ChooseTopic
@topicChangedCallback={{this.newTopicSelected}}
@currentTopicId={{@model.topic.id}}
@selectedTopicId={{this.selectedTopicId}}
/>
{{#if this.selectedTopicId}}

View File

@ -177,4 +177,9 @@ export default class MoveToTopic extends Component {
updateTags(newTags) {
this.tags = newTags;
}
@action
newTopicSelected(topic) {
this.selectedTopicId = topic.id;
}
}

View File

@ -70,7 +70,7 @@
{{#if this.existingTopic}}
<p>{{this.existingTopicInstruction}}</p>
<form>
<ChooseTopic @selectedTopicId={{this.selectedTopicId}} />
<ChooseTopic @topicChangedCallback={{@topicChangedCallback}} />
</form>
{{/if}}

View File

@ -82,12 +82,25 @@ export default class ChatModalArchiveChannel extends Component {
);
}
get data() {
const data = { type: this.selection };
if (this.newTopic) {
data.title = this.topicTitle;
data.category_id = this.categoryId;
data.tags = this.tags;
}
if (this.existingTopic) {
data.topic_id = this.selectedTopicId;
}
return data;
}
@action
archiveChannel() {
this.saving = true;
return this.chatApi
.createChannelArchive(this.channel.id, this.#data())
.createChannelArchive(this.channel.id, this.data)
.then(() => {
this.flash = I18n.t("chat.channel_archive.process_started");
this.flashType = "success";
@ -101,17 +114,9 @@ export default class ChatModalArchiveChannel extends Component {
.finally(() => (this.saving = false));
}
#data() {
const data = { type: this.selection };
if (this.newTopic) {
data.title = this.topicTitle;
data.category_id = this.categoryId;
data.tags = this.tags;
}
if (this.existingTopic) {
data.topic_id = this.selectedTopicId;
}
return data;
@action
newTopicSelected(topic) {
this.selectedTopicId = topic.id;
}
<template>
@ -132,7 +137,7 @@ export default class ChatModalArchiveChannel extends Component {
@topicTitle={{this.topicTitle}}
@categoryId={{this.categoryId}}
@tags={{this.tags}}
@selectedTopicId={{this.selectedTopicId}}
@topicChangedCallback={{this.newTopicSelected}}
@instructionLabels={{this.instructionLabels}}
@allowNewMessage={{false}}
/>