mirror of
https://github.com/discourse/discourse.git
synced 2025-03-25 06:55:37 +08:00
DEV: Convert choose-topic to glimmer (#27229)
This commit is contained in:
parent
eb8549e527
commit
b1b218aa99
@ -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>
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -141,8 +141,8 @@
|
||||
</p>
|
||||
<form>
|
||||
<ChooseTopic
|
||||
@topicChangedCallback={{this.newTopicSelected}}
|
||||
@currentTopicId={{@model.topic.id}}
|
||||
@selectedTopicId={{this.selectedTopicId}}
|
||||
/>
|
||||
|
||||
{{#if this.selectedTopicId}}
|
||||
|
@ -177,4 +177,9 @@ export default class MoveToTopic extends Component {
|
||||
updateTags(newTags) {
|
||||
this.tags = newTags;
|
||||
}
|
||||
|
||||
@action
|
||||
newTopicSelected(topic) {
|
||||
this.selectedTopicId = topic.id;
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@
|
||||
{{#if this.existingTopic}}
|
||||
<p>{{this.existingTopicInstruction}}</p>
|
||||
<form>
|
||||
<ChooseTopic @selectedTopicId={{this.selectedTopicId}} />
|
||||
<ChooseTopic @topicChangedCallback={{@topicChangedCallback}} />
|
||||
</form>
|
||||
{{/if}}
|
||||
|
||||
|
@ -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}}
|
||||
/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user