@@ -2,43 +2,74 @@ import Component from "@glimmer/component";
22import { tracked } from " @glimmer/tracking" ;
33import { on } from " @ember/modifier" ;
44import { action } from " @ember/object" ;
5- import didInsert from " @ember/render-modifiers/modifiers/did-insert " ;
5+ import didUpdate from " @ember/render-modifiers/modifiers/did-update " ;
66import { service } from " @ember/service" ;
7+ import { not } from " truth-helpers" ;
78import DToggleSwitch from " discourse/components/d-toggle-switch" ;
89import { SEARCH_TYPE_DEFAULT } from " discourse/controllers/full-page-search" ;
910import { ajax } from " discourse/lib/ajax" ;
10- import { withPluginApi } from " discourse/lib/plugin-api" ;
1111import { isValidSearchTerm , translateResults } from " discourse/lib/search" ;
1212import icon from " discourse-common/helpers/d-icon" ;
13- import I18n from " I18n" ;
13+ import I18n , { i18n } from " discourse-i18n" ;
14+ import DTooltip from " float-kit/components/d-tooltip" ;
1415import AiIndicatorWave from " ../../components/ai-indicator-wave" ;
1516
1617export default class SemanticSearch extends Component {
1718 static shouldRender (_args , { siteSettings }) {
1819 return siteSettings .ai_embeddings_semantic_search_enabled ;
1920 }
2021
21- @service router;
2222 @service appEvents;
23+ @service router;
2324 @service siteSettings;
2425 @service searchPreferencesManager;
2526
26- @tracked searching = false ;
27+ @tracked searching;
2728 @tracked AiResults = [];
2829 @tracked showingAiResults = false ;
30+ @tracked sortOrder = this .args .outletArgs .sortOrder ;
2931 initialSearchTerm = this .args .outletArgs .search ;
3032
33+ constructor () {
34+ super (... arguments );
35+ this .appEvents .on (" full-page-search:trigger-search" , this , this .onSearch );
36+ this .handleSearch ();
37+ }
38+
39+ willDestroy () {
40+ super .willDestroy (... arguments );
41+ this .appEvents .off (" full-page-search:trigger-search" , this , this .onSearch );
42+ }
43+
44+ @action
45+ onSearch () {
46+ if (! this .searching ) {
47+ this .resetAiResults ();
48+ return this .performHyDESearch ();
49+ }
50+ }
51+
3152 get disableToggleSwitch () {
3253 if (
3354 this .searching ||
3455 this .AiResults .length === 0 ||
35- this .args . outletArgs . sortOrder !== 0
56+ ! this .validSearchOrder
3657 ) {
3758 return true ;
3859 }
3960 }
4061
62+ get validSearchOrder () {
63+ return this .sortOrder === 0 ;
64+ }
65+
4166 get searchStateText () {
67+ if (! this .validSearchOrder ) {
68+ return I18n .t (
69+ " discourse_ai.embeddings.semantic_search_results.unavailable"
70+ );
71+ }
72+
4273 // Search results:
4374 if (this .AiResults .length > 0 ) {
4475 if (this .showingAiResults ) {
@@ -89,7 +120,7 @@ export default class SemanticSearch extends Component {
89120 return (
90121 this .args .outletArgs .type === SEARCH_TYPE_DEFAULT &&
91122 isValidSearchTerm (this .searchTerm , this .siteSettings ) &&
92- this .args . outletArgs . sortOrder === 0
123+ this .validSearchOrder
93124 );
94125 }
95126
@@ -116,15 +147,13 @@ export default class SemanticSearch extends Component {
116147 return ;
117148 }
118149
119- if (this .initialSearchTerm && ! this .searching ) {
150+ if (this .initialSearchTerm ) {
151+ this .searching = true ;
120152 return this .performHyDESearch ();
121153 }
122-
123- this .#resetAndSearchOnEvent ();
124154 }
125155
126156 performHyDESearch () {
127- this .searching = true ;
128157 this .resetAiResults ();
129158
130159 ajax (" /discourse-ai/embeddings/semantic-search" , {
@@ -134,7 +163,6 @@ export default class SemanticSearch extends Component {
134163 const model = (await translateResults (results)) || {};
135164
136165 if (model .posts ? .length === 0 ) {
137- this .searching = false ;
138166 return ;
139167 }
140168
@@ -144,56 +172,65 @@ export default class SemanticSearch extends Component {
144172
145173 this .AiResults = model .posts ;
146174 })
147- .finally (() => (this .searching = false ));
148- }
149-
150- #resetAndSearchOnEvent () {
151- return withPluginApi (" 1.15.0" , (api ) => {
152- api .onAppEvent (" full-page-search:trigger-search" , () => {
153- if (! this .searching ) {
154- this .resetAiResults ();
155- return this .performHyDESearch ();
156- }
175+ .finally (() => {
176+ this .searching = false ;
157177 });
158- });
159178 }
160179
161180 @action
162- checkQueryParamsAndSearch () {
163- // This check is necessary because handleSearch() isn't called
164- // if query params are present and a new search has appended text.
165- // It ensures AiResults are reset and searched for properly
166- const searchQueryParam = this .router .currentRoute ? .queryParams ? .q ;
167- if (searchQueryParam) {
168- this .#resetAndSearchOnEvent ();
181+ sortChanged () {
182+ if (this .sortOrder !== this .args .outletArgs .sortOrder ) {
183+ this .sortOrder = this .args .outletArgs .sortOrder ;
184+
185+ if (this .validSearchOrder ) {
186+ this .handleSearch ();
187+ } else {
188+ this .showingAiResults = false ;
189+ this .resetAiResults ();
190+ }
169191 }
170192 }
171193
172194 <template >
173- <span {{didInsert this . checkQueryParamsAndSearch}} ></span >
174- {{#if this . searchEnabled }}
175- <div class =" semantic-search__container search-results" role =" region" >
176- <div class =" semantic-search__results" {{didInsert this . handleSearch}} >
177- <div
178- class =" semantic-search__searching
179- {{if this . searching ' in-progress' }} "
180- >
181- <DToggleSwitch
182- disabled ={{this .disableToggleSwitch }}
183- @ state ={{this .showingAiResults }}
184- class =" semantic-search__results-toggle"
185- {{on " click" this . toggleAiResults}}
186- />
187-
188- <div class =" semantic-search__searching-text" >
189- {{icon " discourse-sparkles" }}
190- {{this .searchStateText }}
191- </div >
192-
193- <AiIndicatorWave @ loading ={{this .searching }} />
195+ <span {{didUpdate this . sortChanged @ outletArgs.sortOrder}} ></span >
196+ <div class =" semantic-search__container search-results" role =" region" >
197+ <div class =" semantic-search__results" >
198+ <div
199+ class =" semantic-search__searching {{if this . searching ' in-progress' }} "
200+ >
201+ <DToggleSwitch
202+ disabled ={{this .disableToggleSwitch }}
203+ @ state ={{this .showingAiResults }}
204+ class =" semantic-search__results-toggle"
205+ {{on " click" this . toggleAiResults}}
206+ />
207+
208+ <div class =" semantic-search__searching-text" >
209+ {{icon " discourse-sparkles" }}
210+ {{this .searchStateText }}
194211 </div >
212+
213+ <AiIndicatorWave @ loading ={{this .searching }} />
214+
215+ {{#if ( not this . validSearchOrder) }}
216+
217+ <DTooltip
218+ @ identifier =" semantic-search-unavailable-tooltip"
219+ class =" semantic-search__unavailable-tooltip"
220+ ...attributes
221+ >
222+ <: trigger >
223+ {{icon " far-circle-question" }}
224+ </: trigger >
225+ <: content >
226+ {{i18n
227+ " discourse_ai.embeddings.semantic_search_unavailable_tooltip"
228+ }}
229+ </: content >
230+ </DTooltip >
231+ {{/if }}
195232 </div >
196233 </div >
197- {{/ if }}
234+ </ div >
198235 </template >
199236}
0 commit comments