@drodil/backstage-plugin-qeta-react 2.14.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/dist/api.esm.js +8 -0
  2. package/dist/api.esm.js.map +1 -0
  3. package/dist/components/AnswerCard/AnswerCard.esm.js +113 -0
  4. package/dist/components/AnswerCard/AnswerCard.esm.js.map +1 -0
  5. package/dist/components/AnswerForm/AnswerForm.esm.js +153 -0
  6. package/dist/components/AnswerForm/AnswerForm.esm.js.map +1 -0
  7. package/dist/components/AnswersContainer/AnswerList.esm.js +100 -0
  8. package/dist/components/AnswersContainer/AnswerList.esm.js.map +1 -0
  9. package/dist/components/AnswersContainer/AnswerListItem.esm.js +90 -0
  10. package/dist/components/AnswersContainer/AnswerListItem.esm.js.map +1 -0
  11. package/dist/components/AnswersContainer/AnswersContainer.esm.js +210 -0
  12. package/dist/components/AnswersContainer/AnswersContainer.esm.js.map +1 -0
  13. package/dist/components/ArticleContent/ArticleButtons.esm.js +90 -0
  14. package/dist/components/ArticleContent/ArticleButtons.esm.js.map +1 -0
  15. package/dist/components/ArticleContent/ArticleContent.esm.js +78 -0
  16. package/dist/components/ArticleContent/ArticleContent.esm.js.map +1 -0
  17. package/dist/components/AuthorBox/AuthorBox.esm.js +25 -0
  18. package/dist/components/AuthorBox/AuthorBox.esm.js.map +1 -0
  19. package/dist/components/Buttons/AddToCollectionButton.esm.js +68 -0
  20. package/dist/components/Buttons/AddToCollectionButton.esm.js.map +1 -0
  21. package/dist/components/Buttons/AskQuestionButton.esm.js +40 -0
  22. package/dist/components/Buttons/AskQuestionButton.esm.js.map +1 -0
  23. package/dist/components/Buttons/BackToArticlesButton.esm.js +40 -0
  24. package/dist/components/Buttons/BackToArticlesButton.esm.js.map +1 -0
  25. package/dist/components/Buttons/BackToCollectionsButton.esm.js +25 -0
  26. package/dist/components/Buttons/BackToCollectionsButton.esm.js.map +1 -0
  27. package/dist/components/Buttons/BackToQuestionsButton.esm.js +40 -0
  28. package/dist/components/Buttons/BackToQuestionsButton.esm.js.map +1 -0
  29. package/dist/components/Buttons/CreateCollectionButton.esm.js +29 -0
  30. package/dist/components/Buttons/CreateCollectionButton.esm.js.map +1 -0
  31. package/dist/components/Buttons/EntityFollowButton.esm.js +34 -0
  32. package/dist/components/Buttons/EntityFollowButton.esm.js.map +1 -0
  33. package/dist/components/Buttons/FavoriteButton.esm.js +45 -0
  34. package/dist/components/Buttons/FavoriteButton.esm.js.map +1 -0
  35. package/dist/components/Buttons/LinkButton.esm.js +28 -0
  36. package/dist/components/Buttons/LinkButton.esm.js.map +1 -0
  37. package/dist/components/Buttons/TagFollowButton.esm.js +34 -0
  38. package/dist/components/Buttons/TagFollowButton.esm.js.map +1 -0
  39. package/dist/components/Buttons/VoteButtons.esm.js +75 -0
  40. package/dist/components/Buttons/VoteButtons.esm.js.map +1 -0
  41. package/dist/components/Buttons/WriteArticleButton.esm.js +40 -0
  42. package/dist/components/Buttons/WriteArticleButton.esm.js.map +1 -0
  43. package/dist/components/CollectionCard/CollectionCard.esm.js +63 -0
  44. package/dist/components/CollectionCard/CollectionCard.esm.js.map +1 -0
  45. package/dist/components/CollectionForm/CollectionForm.esm.js +251 -0
  46. package/dist/components/CollectionForm/CollectionForm.esm.js.map +1 -0
  47. package/dist/components/CollectionsGrid/CollectionsGrid.esm.js +40 -0
  48. package/dist/components/CollectionsGrid/CollectionsGrid.esm.js.map +1 -0
  49. package/dist/components/CollectionsGrid/CollectionsGridContent.esm.js +67 -0
  50. package/dist/components/CollectionsGrid/CollectionsGridContent.esm.js.map +1 -0
  51. package/dist/components/CollectionsGrid/CollectionsGridItem.esm.js +48 -0
  52. package/dist/components/CollectionsGrid/CollectionsGridItem.esm.js.map +1 -0
  53. package/dist/components/CommentSection/CommentList.esm.js +47 -0
  54. package/dist/components/CommentSection/CommentList.esm.js.map +1 -0
  55. package/dist/components/CommentSection/CommentSection.esm.js +126 -0
  56. package/dist/components/CommentSection/CommentSection.esm.js.map +1 -0
  57. package/dist/components/DeleteModal/DeleteModal.esm.js +88 -0
  58. package/dist/components/DeleteModal/DeleteModal.esm.js.map +1 -0
  59. package/dist/components/EntitiesGrid/EntitiesGrid.esm.js +45 -0
  60. package/dist/components/EntitiesGrid/EntitiesGrid.esm.js.map +1 -0
  61. package/dist/components/EntitiesGrid/EntitiesGridItem.esm.js +61 -0
  62. package/dist/components/EntitiesGrid/EntitiesGridItem.esm.js.map +1 -0
  63. package/dist/components/FilterPanel/DateRangeFilter.esm.js +110 -0
  64. package/dist/components/FilterPanel/DateRangeFilter.esm.js.map +1 -0
  65. package/dist/components/FilterPanel/FilterPanel.esm.js +238 -0
  66. package/dist/components/FilterPanel/FilterPanel.esm.js.map +1 -0
  67. package/dist/components/FollowedLists/FollowedEntitiesList.esm.js +43 -0
  68. package/dist/components/FollowedLists/FollowedEntitiesList.esm.js.map +1 -0
  69. package/dist/components/FollowedLists/FollowedTagsList.esm.js +43 -0
  70. package/dist/components/FollowedLists/FollowedTagsList.esm.js.map +1 -0
  71. package/dist/components/HomePageCards/ImpactCard.esm.js +22 -0
  72. package/dist/components/HomePageCards/ImpactCard.esm.js.map +1 -0
  73. package/dist/components/HomePageCards/PostsCard.esm.js +42 -0
  74. package/dist/components/HomePageCards/PostsCard.esm.js.map +1 -0
  75. package/dist/components/Links/Links.esm.js +33 -0
  76. package/dist/components/Links/Links.esm.js.map +1 -0
  77. package/dist/components/MarkdownEditor/MarkdownEditor.esm.js +63 -0
  78. package/dist/components/MarkdownEditor/MarkdownEditor.esm.js.map +1 -0
  79. package/dist/components/MarkdownRenderer/MarkdownRenderer.esm.js +97 -0
  80. package/dist/components/MarkdownRenderer/MarkdownRenderer.esm.js.map +1 -0
  81. package/dist/components/PostAnonymouslyCheckbox/PostAnonymouslyCheckbox.esm.js +34 -0
  82. package/dist/components/PostAnonymouslyCheckbox/PostAnonymouslyCheckbox.esm.js.map +1 -0
  83. package/dist/components/PostForm/EntitiesInput.esm.js +100 -0
  84. package/dist/components/PostForm/EntitiesInput.esm.js.map +1 -0
  85. package/dist/components/PostForm/PostForm.esm.js +283 -0
  86. package/dist/components/PostForm/PostForm.esm.js.map +1 -0
  87. package/dist/components/PostForm/TagInput.esm.js +81 -0
  88. package/dist/components/PostForm/TagInput.esm.js.map +1 -0
  89. package/dist/components/PostHighlightList/PostHighlightList.esm.js +72 -0
  90. package/dist/components/PostHighlightList/PostHighlightList.esm.js.map +1 -0
  91. package/dist/components/PostsContainer/NoPostsCard.esm.js +50 -0
  92. package/dist/components/PostsContainer/NoPostsCard.esm.js.map +1 -0
  93. package/dist/components/PostsContainer/PostList.esm.js +113 -0
  94. package/dist/components/PostsContainer/PostList.esm.js.map +1 -0
  95. package/dist/components/PostsContainer/PostListItem.esm.js +138 -0
  96. package/dist/components/PostsContainer/PostListItem.esm.js.map +1 -0
  97. package/dist/components/PostsContainer/PostsContainer.esm.js +152 -0
  98. package/dist/components/PostsContainer/PostsContainer.esm.js.map +1 -0
  99. package/dist/components/PostsGrid/PostsGrid.esm.js +146 -0
  100. package/dist/components/PostsGrid/PostsGrid.esm.js.map +1 -0
  101. package/dist/components/PostsGrid/PostsGridContent.esm.js +90 -0
  102. package/dist/components/PostsGrid/PostsGridContent.esm.js.map +1 -0
  103. package/dist/components/PostsGrid/PostsGridItem.esm.js +57 -0
  104. package/dist/components/PostsGrid/PostsGridItem.esm.js.map +1 -0
  105. package/dist/components/QuestionCard/QuestionCard.esm.js +107 -0
  106. package/dist/components/QuestionCard/QuestionCard.esm.js.map +1 -0
  107. package/dist/components/QuestionsTable/QuestionTableRow.esm.js +21 -0
  108. package/dist/components/QuestionsTable/QuestionTableRow.esm.js.map +1 -0
  109. package/dist/components/QuestionsTable/QuestionsTable.esm.js +131 -0
  110. package/dist/components/QuestionsTable/QuestionsTable.esm.js.map +1 -0
  111. package/dist/components/RelativeTimeWithTooltip/RelativeTimeWithTooltip.esm.js +22 -0
  112. package/dist/components/RelativeTimeWithTooltip/RelativeTimeWithTooltip.esm.js.map +1 -0
  113. package/dist/components/StatsChart/StatsChart.esm.js +245 -0
  114. package/dist/components/StatsChart/StatsChart.esm.js.map +1 -0
  115. package/dist/components/SummaryStatsGrid/SummaryStatsGrid.esm.js +53 -0
  116. package/dist/components/SummaryStatsGrid/SummaryStatsGrid.esm.js.map +1 -0
  117. package/dist/components/TagsAndEntities/EntityChip.esm.js +80 -0
  118. package/dist/components/TagsAndEntities/EntityChip.esm.js.map +1 -0
  119. package/dist/components/TagsAndEntities/TagChip.esm.js +78 -0
  120. package/dist/components/TagsAndEntities/TagChip.esm.js.map +1 -0
  121. package/dist/components/TagsAndEntities/TagsAndEntities.esm.js +32 -0
  122. package/dist/components/TagsAndEntities/TagsAndEntities.esm.js.map +1 -0
  123. package/dist/components/TagsGrid/EditTagModal.esm.js +73 -0
  124. package/dist/components/TagsGrid/EditTagModal.esm.js.map +1 -0
  125. package/dist/components/TagsGrid/TagGridItem.esm.js +56 -0
  126. package/dist/components/TagsGrid/TagGridItem.esm.js.map +1 -0
  127. package/dist/components/TagsGrid/TagsGrid.esm.js +45 -0
  128. package/dist/components/TagsGrid/TagsGrid.esm.js.map +1 -0
  129. package/dist/components/TopRankingUsersCard/TopRankingUsersCard.esm.js +161 -0
  130. package/dist/components/TopRankingUsersCard/TopRankingUsersCard.esm.js.map +1 -0
  131. package/dist/components/TopRankingUsersCard/TrophyIcon.esm.js +19 -0
  132. package/dist/components/TopRankingUsersCard/TrophyIcon.esm.js.map +1 -0
  133. package/dist/components/TopRankingUsersCard/styles.esm.js +23 -0
  134. package/dist/components/TopRankingUsersCard/styles.esm.js.map +1 -0
  135. package/dist/components/UsersGrid/UsersGrid.esm.js +43 -0
  136. package/dist/components/UsersGrid/UsersGrid.esm.js.map +1 -0
  137. package/dist/components/UsersGrid/UsersGridItem.esm.js +66 -0
  138. package/dist/components/UsersGrid/UsersGridItem.esm.js.map +1 -0
  139. package/dist/index.d.ts +568 -1
  140. package/dist/index.esm.js +45 -1
  141. package/dist/index.esm.js.map +1 -1
  142. package/dist/routes.esm.js +56 -1
  143. package/dist/routes.esm.js.map +1 -1
  144. package/dist/translation.esm.js +470 -0
  145. package/dist/translation.esm.js.map +1 -0
  146. package/dist/utils/hooks.esm.js +734 -0
  147. package/dist/utils/hooks.esm.js.map +1 -0
  148. package/dist/utils/utils.esm.js +93 -0
  149. package/dist/utils/utils.esm.js.map +1 -0
  150. package/package.json +32 -5
@@ -0,0 +1,734 @@
1
+ import useAsync from 'react-use/lib/useAsync';
2
+ import { useApi, identityApiRef, useAnalytics, appThemeApiRef, configApiRef } from '@backstage/core-plugin-api';
3
+ import { makeStyles } from '@material-ui/core';
4
+ import { catalogApiRef, useEntityPresentation } from '@backstage/plugin-catalog-react';
5
+ import { trimEnd } from 'lodash';
6
+ import { useSearchParams } from 'react-router-dom';
7
+ import React, { useEffect, useMemo, useCallback, useState } from 'react';
8
+ import { filterTags } from '@drodil/backstage-plugin-qeta-common';
9
+ import DataLoader from 'dataloader';
10
+ import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
11
+ import { qetaTranslationRef } from '../translation.esm.js';
12
+ import { qetaApiRef } from '../api.esm.js';
13
+ import { filterKeys } from '../components/FilterPanel/FilterPanel.esm.js';
14
+ import useDebounce from 'react-use/lib/useDebounce';
15
+ import { getFiltersWithDateRange } from './utils.esm.js';
16
+ import { useSignal } from '@backstage/plugin-signals-react';
17
+ import { useAsyncRetry } from 'react-use';
18
+
19
+ const useTranslation = () => {
20
+ return useTranslationRef(qetaTranslationRef);
21
+ };
22
+ function usePaginatedPosts(props) {
23
+ const { type, tags, author, entity, favorite, initialPageSize } = props;
24
+ const analytics = useAnalytics();
25
+ const [page, setPage] = React.useState(1);
26
+ const [pageCount, setPageCount] = React.useState(1);
27
+ const [postsPerPage, setPostsPerPage] = React.useState(initialPageSize ?? 10);
28
+ const [showFilterPanel, setShowFilterPanel] = React.useState(false);
29
+ const [searchParams, setSearchParams] = useSearchParams();
30
+ const [searchQuery, setSearchQuery] = React.useState("");
31
+ const [filters, setFilters] = React.useState({
32
+ order: "desc",
33
+ orderBy: "created",
34
+ noAnswers: "false",
35
+ noCorrectAnswer: "false",
36
+ noVotes: "false",
37
+ searchQuery: "",
38
+ entity: entity ?? "",
39
+ tags: tags ?? [],
40
+ dateRange: "",
41
+ collectionId: props.collectionId
42
+ });
43
+ const onPageChange = (value) => {
44
+ setPage(value);
45
+ setSearchParams((prev) => {
46
+ const newValue = prev;
47
+ newValue.set("page", String(value));
48
+ return newValue;
49
+ });
50
+ };
51
+ const loadNextPage = () => {
52
+ setPage((prev) => prev + 1);
53
+ };
54
+ const onFilterChange = (key, value) => {
55
+ if (filters[key] === value) {
56
+ return;
57
+ }
58
+ setPage(1);
59
+ setFilters({ ...filters, ...{ [key]: value } });
60
+ setSearchParams((prev) => {
61
+ const newValue = prev;
62
+ if (!value || value === "false") {
63
+ newValue.delete(key);
64
+ } else if (Array.isArray(value)) {
65
+ if (value.length === 0) {
66
+ newValue.delete(key);
67
+ } else {
68
+ newValue.set(key, value.join(","));
69
+ }
70
+ } else if (value.length > 0) {
71
+ newValue.set(key, value);
72
+ } else {
73
+ newValue.delete(key);
74
+ }
75
+ return newValue;
76
+ });
77
+ };
78
+ const onSearchQueryChange = (event) => {
79
+ onPageChange(1);
80
+ if (event.target.value) {
81
+ analytics.captureEvent("qeta_search", event.target.value);
82
+ }
83
+ setSearchQuery(event.target.value);
84
+ };
85
+ useDebounce(
86
+ () => {
87
+ if (filters.searchQuery !== searchQuery) {
88
+ setFilters({ ...filters, searchQuery });
89
+ }
90
+ },
91
+ 400,
92
+ [searchQuery]
93
+ );
94
+ useEffect(() => {
95
+ let filtersApplied = false;
96
+ searchParams.forEach((value, key) => {
97
+ try {
98
+ if (key === "page") {
99
+ const pv = Number.parseInt(value, 10);
100
+ if (pv > 0) {
101
+ setPage(pv);
102
+ } else {
103
+ setPage(1);
104
+ }
105
+ } else if (key === "postsPerPage") {
106
+ const qpp = Number.parseInt(value, 10);
107
+ if (qpp > 0) setPostsPerPage(qpp);
108
+ } else if (filterKeys.includes(key)) {
109
+ filtersApplied = true;
110
+ if (key === "tags") {
111
+ filters.tags = filterTags(value) ?? [];
112
+ } else {
113
+ filters[key] = value;
114
+ }
115
+ }
116
+ } catch (_e) {
117
+ }
118
+ });
119
+ setFilters(filters);
120
+ if (filtersApplied) {
121
+ setShowFilterPanel(true);
122
+ }
123
+ }, [searchParams, filters]);
124
+ const {
125
+ value: response,
126
+ loading,
127
+ error
128
+ } = useQetaApi(
129
+ (api) => {
130
+ return api.getPosts({
131
+ type,
132
+ limit: postsPerPage,
133
+ offset: (page - 1) * postsPerPage,
134
+ includeEntities: true,
135
+ author,
136
+ favorite,
137
+ ...getFiltersWithDateRange(filters)
138
+ });
139
+ },
140
+ [type, page, filters, postsPerPage]
141
+ );
142
+ useEffect(() => {
143
+ if (response) {
144
+ setPageCount(Math.ceil(response.total / postsPerPage));
145
+ }
146
+ }, [response, postsPerPage]);
147
+ const onPageSizeChange = (value) => {
148
+ if (response) {
149
+ let newPage = page;
150
+ while (newPage * value > response.total) {
151
+ newPage -= 1;
152
+ }
153
+ onPageChange(Math.max(1, newPage));
154
+ }
155
+ setPostsPerPage(value);
156
+ setSearchParams((prev) => {
157
+ const newValue = prev;
158
+ newValue.set("postsPerPage", String(value));
159
+ return newValue;
160
+ });
161
+ };
162
+ return {
163
+ page,
164
+ setPage,
165
+ postsPerPage,
166
+ setPostsPerPage,
167
+ showFilterPanel,
168
+ setShowFilterPanel,
169
+ searchParams,
170
+ setSearchParams,
171
+ searchQuery,
172
+ setSearchQuery,
173
+ filters,
174
+ setFilters,
175
+ onPageChange,
176
+ onPageSizeChange,
177
+ onFilterChange,
178
+ onSearchQueryChange,
179
+ response,
180
+ loading,
181
+ error,
182
+ loadNextPage,
183
+ pageCount
184
+ };
185
+ }
186
+ function useVoting(resp) {
187
+ const [entity, setEntity] = React.useState(
188
+ resp
189
+ );
190
+ const [ownVote, setOwnVote] = React.useState(entity.ownVote ?? 0);
191
+ const [correctAnswer, setCorrectAnswer] = useState(
192
+ "postId" in entity ? entity.correct : false
193
+ );
194
+ const [score, setScore] = useState(entity.score);
195
+ const analytics = useAnalytics();
196
+ const qetaApi = useApi(qetaApiRef);
197
+ const { t } = useTranslation();
198
+ const isQuestion = "title" in entity;
199
+ const own = entity.own ?? false;
200
+ const { lastSignal } = useSignal(
201
+ isQuestion ? `qeta:question_${entity.id}` : `qeta:answer_${entity.id}`
202
+ );
203
+ useEffect(() => {
204
+ if (entity) {
205
+ setScore(entity.score);
206
+ }
207
+ }, [entity]);
208
+ useEffect(() => {
209
+ if (lastSignal?.type === "post_stats" || lastSignal?.type === "answer_stats") {
210
+ setCorrectAnswer(lastSignal.correctAnswer);
211
+ setScore(lastSignal.score);
212
+ }
213
+ }, [lastSignal]);
214
+ const voteUp = () => {
215
+ if (isQuestion) {
216
+ qetaApi.votePostUp(entity.id).then((response) => {
217
+ setOwnVote(1);
218
+ analytics.captureEvent("vote", "question", { value: 1 });
219
+ setEntity(response);
220
+ });
221
+ } else if ("questionId" in entity) {
222
+ qetaApi.voteAnswerUp(entity.postId, entity.id).then((response) => {
223
+ setOwnVote(1);
224
+ analytics.captureEvent("vote", "answer", { value: 1 });
225
+ setEntity(response);
226
+ });
227
+ }
228
+ };
229
+ const voteDown = () => {
230
+ if (isQuestion) {
231
+ qetaApi.votePostDown(entity.id).then((response) => {
232
+ setOwnVote(-1);
233
+ analytics.captureEvent("vote", "question", { value: -1 });
234
+ setEntity(response);
235
+ });
236
+ } else if ("questionId" in entity) {
237
+ qetaApi.voteAnswerDown(entity.postId, entity.id).then((response) => {
238
+ setOwnVote(-1);
239
+ analytics.captureEvent("vote", "answer", { value: -1 });
240
+ setEntity(response);
241
+ });
242
+ }
243
+ };
244
+ let correctTooltip = correctAnswer ? t("voteButtons.answer.markIncorrect") : t("voteButtons.answer.markCorrect");
245
+ if (!entity?.own) {
246
+ correctTooltip = correctAnswer ? t("voteButtons.answer.marked") : "";
247
+ }
248
+ let voteUpTooltip = isQuestion ? t("voteButtons.question.good") : t("voteButtons.answer.good");
249
+ if (own) {
250
+ voteUpTooltip = isQuestion ? t("voteButtons.question.own") : t("voteButtons.answer.own");
251
+ }
252
+ let voteDownTooltip = isQuestion ? t("voteButtons.question.bad") : t("voteButtons.answer.bad");
253
+ if (own) {
254
+ voteDownTooltip = voteUpTooltip;
255
+ }
256
+ const toggleCorrectAnswer = () => {
257
+ if (!("postId" in entity)) {
258
+ return;
259
+ }
260
+ if (correctAnswer) {
261
+ qetaApi.markAnswerIncorrect(entity.postId, entity.id).then((response) => {
262
+ if (response) {
263
+ setCorrectAnswer(false);
264
+ }
265
+ });
266
+ } else {
267
+ qetaApi.markAnswerCorrect(entity.postId, entity.id).then((response) => {
268
+ if (response) {
269
+ setCorrectAnswer(true);
270
+ }
271
+ });
272
+ }
273
+ };
274
+ return {
275
+ entity,
276
+ ownVote,
277
+ correctAnswer,
278
+ score,
279
+ voteUp,
280
+ voteDown,
281
+ toggleCorrectAnswer,
282
+ voteUpTooltip,
283
+ voteDownTooltip,
284
+ correctTooltip
285
+ };
286
+ }
287
+ function useQetaApi(f, deps = []) {
288
+ const qetaApi = useApi(qetaApiRef);
289
+ return useAsyncRetry(async () => {
290
+ return await f(qetaApi);
291
+ }, deps);
292
+ }
293
+ function useIdentityApi(f, deps = []) {
294
+ const identityApi = useApi(identityApiRef);
295
+ return useAsync(async () => {
296
+ return await f(identityApi);
297
+ }, deps);
298
+ }
299
+ function useEntityQueryParameter(entity) {
300
+ const [searchParams] = useSearchParams();
301
+ const [entityRef, setEntityRef] = React.useState(entity);
302
+ useEffect(() => {
303
+ setEntityRef(searchParams.get("entity") ?? void 0);
304
+ }, [searchParams, setEntityRef]);
305
+ return entityRef;
306
+ }
307
+ function useIsDarkTheme() {
308
+ const appThemeApi = useApi(appThemeApiRef);
309
+ const themes = appThemeApi.getInstalledThemes();
310
+ const theme = useMemo(() => appThemeApi.getActiveThemeId(), [appThemeApi]);
311
+ return Boolean(themes.find((t) => t.id === theme)?.variant === "dark");
312
+ }
313
+ const useStyles = makeStyles((theme) => {
314
+ return {
315
+ leftMenu: {
316
+ top: "0",
317
+ width: "165px"
318
+ },
319
+ selectedMenuItem: {
320
+ color: theme.palette.primary.contrastText,
321
+ backgroundColor: theme.palette.primary.light,
322
+ borderRadius: theme.shape.borderRadius,
323
+ "&:hover": {
324
+ backgroundColor: theme.palette.primary.dark
325
+ },
326
+ "& svg": {
327
+ color: theme.palette.primary.contrastText
328
+ }
329
+ },
330
+ nonSelectedMenuItem: {
331
+ backgroundColor: "initial"
332
+ },
333
+ headerImage: {
334
+ width: "100%",
335
+ height: "250px",
336
+ objectFit: "cover"
337
+ },
338
+ markdownEditor: {
339
+ backgroundColor: "initial",
340
+ color: theme.palette.text.primary,
341
+ border: `1px solid ${theme.palette.action.disabled}`,
342
+ borderRadius: theme.shape.borderRadius,
343
+ "&:hover": {
344
+ borderColor: theme.palette.action.active
345
+ },
346
+ "&:focus-within": {
347
+ borderColor: theme.palette.primary.main
348
+ },
349
+ "& .mde-header": {
350
+ backgroundColor: "initial",
351
+ color: theme.palette.text.primary,
352
+ borderBottom: `1px solid ${theme.palette.action.selected}`,
353
+ "& .mde-tabs button, .mde-header-item > button": {
354
+ color: `${theme.palette.text.primary} !important`
355
+ }
356
+ },
357
+ "& .mde-preview-content": {
358
+ padding: "10px"
359
+ },
360
+ "& .mde-text, .mde-preview": {
361
+ fontSize: theme.typography.body1.fontSize,
362
+ fontFamily: theme.typography.body1.fontFamily,
363
+ lineHeight: theme.typography.body1.lineHeight
364
+ },
365
+ "& .mde-text": {
366
+ backgroundColor: "initial",
367
+ color: theme.palette.text.primary,
368
+ outline: "none"
369
+ },
370
+ "& .image-tip": {
371
+ color: theme.palette.text.primary,
372
+ backgroundColor: "initial"
373
+ }
374
+ },
375
+ markdownEditorError: {
376
+ border: `1px solid ${theme.palette.error.main} !important`
377
+ },
378
+ markdownContent: {
379
+ "& *": {
380
+ wordBreak: "break-word"
381
+ },
382
+ "&.inline": {
383
+ display: "inline-block"
384
+ },
385
+ "& > :first-child": {
386
+ marginTop: "0px !important"
387
+ },
388
+ "& > :last-child": {
389
+ marginBottom: "0px !important"
390
+ }
391
+ },
392
+ successColor: {
393
+ color: theme.palette.success.main
394
+ },
395
+ questionDivider: {
396
+ marginTop: theme.spacing(2),
397
+ marginBottom: theme.spacing(2)
398
+ },
399
+ questionCard: {
400
+ marginBottom: theme.spacing(1),
401
+ position: "relative"
402
+ },
403
+ questionCardVote: {
404
+ textAlign: "center",
405
+ width: "32px",
406
+ marginRight: "20px",
407
+ marginLeft: "5px",
408
+ display: "inline-block",
409
+ verticalAlign: "top"
410
+ },
411
+ questionCardContent: {
412
+ minHeight: "160px"
413
+ },
414
+ questionListItem: {
415
+ padding: "0.7rem",
416
+ paddingBottom: "1.4rem"
417
+ },
418
+ questionListItemStats: {
419
+ width: "70px",
420
+ textAlign: "right",
421
+ marginRight: "5px",
422
+ display: "inline-block",
423
+ verticalAlign: "top"
424
+ },
425
+ questionListItemContent: {
426
+ display: "inline-block",
427
+ width: "calc(100% - 80px)"
428
+ },
429
+ questionListItemAuthor: {
430
+ display: "inline",
431
+ float: "right"
432
+ },
433
+ questionListItemAvatar: {
434
+ display: "inline-flex !important",
435
+ marginRight: "0.25rem",
436
+ fontSize: "1rem",
437
+ maxWidth: "1rem",
438
+ maxHeight: "1rem"
439
+ },
440
+ answerCardContent: {
441
+ display: "inline-block",
442
+ width: "calc(100% - 70px)"
443
+ },
444
+ questionCardAuthor: {
445
+ padding: theme.spacing(1),
446
+ float: "right",
447
+ maxWidth: "200px",
448
+ border: `1px solid ${theme.palette.action.selected}`,
449
+ "& .avatar": {
450
+ width: theme.spacing(3),
451
+ height: theme.spacing(3),
452
+ fontSize: "1rem"
453
+ }
454
+ },
455
+ questionListPagination: {
456
+ marginTop: theme.spacing(2)
457
+ },
458
+ postButton: {
459
+ marginTop: theme.spacing(1),
460
+ marginBottom: theme.spacing(1)
461
+ },
462
+ questionsPerPageInput: {
463
+ paddingTop: "10px"
464
+ },
465
+ questionsPerPage: {
466
+ marginRight: theme.spacing(3)
467
+ },
468
+ postHighlightListContainer: {
469
+ width: "100%",
470
+ backgroundColor: theme.palette.background.paper,
471
+ border: `1px solid ${theme.palette.action.selected}`,
472
+ borderRadius: theme.shape.borderRadius,
473
+ "&:not(:first-child)": {
474
+ marginTop: theme.spacing(2)
475
+ }
476
+ },
477
+ postHighlightList: {
478
+ paddingBottom: "0px",
479
+ "& p": {
480
+ marginTop: "0",
481
+ marginBottom: "0"
482
+ }
483
+ },
484
+ filterPanel: {
485
+ border: `1px solid ${theme.palette.action.selected}`,
486
+ borderRadius: theme.shape.borderRadius,
487
+ padding: theme.spacing(3)
488
+ },
489
+ questionCardMetadata: {
490
+ marginTop: theme.spacing(3)
491
+ },
492
+ marginRight: {
493
+ marginRight: theme.spacing(1)
494
+ },
495
+ marginLeft: {
496
+ marginLeft: theme.spacing(1)
497
+ },
498
+ questionCardActions: {
499
+ marginTop: theme.spacing(2),
500
+ "& a": {
501
+ marginRight: theme.spacing(1)
502
+ }
503
+ },
504
+ noPadding: {
505
+ padding: `0 !important`
506
+ },
507
+ menuIcon: {
508
+ minWidth: "26px"
509
+ },
510
+ deleteModal: {
511
+ position: "absolute",
512
+ top: "20%",
513
+ left: "50%",
514
+ transform: "translate(-50%, -50%)",
515
+ width: 400,
516
+ backgroundColor: theme.palette.background.default,
517
+ border: `1px solid ${theme.palette.action.selected}`,
518
+ borderRadius: theme.shape.borderRadius,
519
+ padding: theme.spacing(2),
520
+ "& button": {
521
+ marginTop: theme.spacing(2),
522
+ float: "right"
523
+ }
524
+ },
525
+ authorLink: {
526
+ textOverflow: "ellipsis",
527
+ overflow: "hidden",
528
+ whiteSpace: "nowrap"
529
+ },
530
+ highlight: {
531
+ animation: "$highlight 2s"
532
+ },
533
+ "@keyframes highlight": {
534
+ "0%": {
535
+ boxShadow: `0px 0px 0px 3px ${theme.palette.secondary.light}`
536
+ },
537
+ "100%": {
538
+ boxShadow: "none"
539
+ }
540
+ },
541
+ dateFilter: {
542
+ minWidth: "200px",
543
+ marginTop: theme.spacing(2),
544
+ marginBottom: theme.spacing(2)
545
+ }
546
+ };
547
+ });
548
+ const useBaseUrl = () => {
549
+ try {
550
+ const config = useApi(configApiRef);
551
+ return config.getOptionalString("app.baseUrl");
552
+ } catch {
553
+ return void 0;
554
+ }
555
+ };
556
+ const useBasePath = () => {
557
+ const base = "http://sample.dev";
558
+ const url = useBaseUrl() ?? "/";
559
+ const { pathname } = new URL(url, base);
560
+ return trimEnd(pathname, "/");
561
+ };
562
+ const userCache = /* @__PURE__ */ new Map();
563
+ const dataLoaderFactory = (catalogApi) => new DataLoader(
564
+ async (entityRefs) => {
565
+ const { items } = await catalogApi.getEntitiesByRefs({
566
+ fields: [
567
+ "kind",
568
+ "metadata.name",
569
+ "metadata.namespace",
570
+ "spec.profile.displayName",
571
+ "spec.profile.picture"
572
+ ],
573
+ entityRefs
574
+ });
575
+ entityRefs.forEach((entityRef, index) => {
576
+ userCache.set(entityRef, items[index]);
577
+ });
578
+ return items;
579
+ },
580
+ {
581
+ name: "EntityAuthorLoader",
582
+ cacheMap: /* @__PURE__ */ new Map(),
583
+ maxBatchSize: 100,
584
+ batchScheduleFn: (callback) => {
585
+ setTimeout(callback, 50);
586
+ }
587
+ }
588
+ );
589
+ const useEntityAuthor = (entity) => {
590
+ const catalogApi = useApi(catalogApiRef);
591
+ const identityApi = useApi(identityApiRef);
592
+ const [name, setName] = React.useState(void 0);
593
+ const [user, setUser] = React.useState(null);
594
+ const [initials, setInitials] = React.useState(null);
595
+ const [currentUser, setCurrentUser] = React.useState(null);
596
+ const anonymous = "anonymous" in entity ? entity.anonymous ?? false : false;
597
+ let author = (
598
+ // eslint-disable-next-line no-nested-ternary
599
+ "author" in entity ? entity.author : "userRef" in entity ? entity.userRef : entity.owner
600
+ );
601
+ if (!author.startsWith("user:")) {
602
+ author = `user:${author}`;
603
+ }
604
+ const { primaryTitle: userName } = useEntityPresentation(author);
605
+ useEffect(() => {
606
+ if (anonymous) {
607
+ return;
608
+ }
609
+ if (userCache.get(author)) {
610
+ setUser(userCache.get(author));
611
+ return;
612
+ }
613
+ dataLoaderFactory(catalogApi).load(author).then((data) => {
614
+ if (data) {
615
+ setUser(data);
616
+ } else {
617
+ setUser(null);
618
+ }
619
+ }).catch(() => {
620
+ setUser(null);
621
+ });
622
+ }, [catalogApi, author, anonymous]);
623
+ useEffect(() => {
624
+ identityApi.getBackstageIdentity().then((res) => {
625
+ setCurrentUser(res.userEntityRef ?? "user:default/guest");
626
+ });
627
+ }, [identityApi]);
628
+ useEffect(() => {
629
+ let displayName = userName;
630
+ if (author === currentUser) {
631
+ displayName = "You";
632
+ if (anonymous) {
633
+ displayName += " (anonymous)";
634
+ }
635
+ }
636
+ setName(displayName);
637
+ }, [author, anonymous, currentUser, userName]);
638
+ useEffect(() => {
639
+ const init = (name ?? "").split(" ").map((p) => p[0]).join("").substring(0, 2).toUpperCase();
640
+ setInitials(init);
641
+ }, [name]);
642
+ return { name, initials, user };
643
+ };
644
+ let followedTags = void 0;
645
+ const useTagsFollow = () => {
646
+ const [tags, setTags] = React.useState(followedTags ?? []);
647
+ const [loading, setLoading] = React.useState(followedTags === void 0);
648
+ const qetaApi = useApi(qetaApiRef);
649
+ useEffect(() => {
650
+ if (followedTags === void 0) {
651
+ qetaApi.getFollowedTags().then((res) => {
652
+ followedTags = res.tags;
653
+ setTags(res.tags);
654
+ setLoading(false);
655
+ });
656
+ } else {
657
+ setTags(followedTags);
658
+ }
659
+ }, [qetaApi]);
660
+ const followTag = useCallback(
661
+ (tag) => {
662
+ qetaApi.followTag(tag).then(() => {
663
+ setTags((prev) => [...prev, tag]);
664
+ followedTags?.push(tag);
665
+ });
666
+ },
667
+ [qetaApi]
668
+ );
669
+ const unfollowTag = useCallback(
670
+ (tag) => {
671
+ qetaApi.unfollowTag(tag).then(() => {
672
+ setTags((prev) => prev.filter((t) => t !== tag));
673
+ followedTags = followedTags?.filter((t) => t !== tag);
674
+ });
675
+ },
676
+ [qetaApi]
677
+ );
678
+ const isFollowingTag = useCallback(
679
+ (tag) => tags.includes(tag),
680
+ [tags]
681
+ );
682
+ return {
683
+ tags,
684
+ followTag,
685
+ unfollowTag,
686
+ isFollowingTag,
687
+ loading
688
+ };
689
+ };
690
+ let followedEntities = void 0;
691
+ const useEntityFollow = () => {
692
+ const [entities, setEntities] = React.useState(
693
+ followedEntities ?? []
694
+ );
695
+ const [loading, setLoading] = React.useState(followedEntities === void 0);
696
+ const qetaApi = useApi(qetaApiRef);
697
+ useEffect(() => {
698
+ if (followedEntities === void 0) {
699
+ qetaApi.getFollowedEntities().then((res) => {
700
+ followedEntities = res.entityRefs;
701
+ setEntities(res.entityRefs);
702
+ setLoading(false);
703
+ });
704
+ } else {
705
+ setEntities(followedEntities);
706
+ }
707
+ }, [qetaApi]);
708
+ const followEntity = useCallback(
709
+ (entityRef) => {
710
+ qetaApi.followEntity(entityRef).then(() => {
711
+ setEntities((prev) => [...prev, entityRef]);
712
+ followedEntities?.push(entityRef);
713
+ });
714
+ },
715
+ [qetaApi]
716
+ );
717
+ const unfollowEntity = useCallback(
718
+ (entityRef) => {
719
+ qetaApi.unfollowEntity(entityRef).then(() => {
720
+ setEntities((prev) => prev.filter((t) => t !== entityRef));
721
+ followedEntities = followedEntities?.filter((t) => t !== entityRef);
722
+ });
723
+ },
724
+ [qetaApi]
725
+ );
726
+ const isFollowingEntity = useCallback(
727
+ (entityRef) => entities.includes(entityRef),
728
+ [entities]
729
+ );
730
+ return { entities, followEntity, unfollowEntity, isFollowingEntity, loading };
731
+ };
732
+
733
+ export { useBasePath, useBaseUrl, useEntityAuthor, useEntityFollow, useEntityQueryParameter, useIdentityApi, useIsDarkTheme, usePaginatedPosts, useQetaApi, useStyles, useTagsFollow, useTranslation, useVoting };
734
+ //# sourceMappingURL=hooks.esm.js.map