@drodil/backstage-plugin-qeta-react 2.15.0 → 3.0.1

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 +91 -0
  14. package/dist/components/ArticleContent/ArticleButtons.esm.js.map +1 -0
  15. package/dist/components/ArticleContent/ArticleContent.esm.js +82 -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 +29 -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 +231 -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 +67 -0
  126. package/dist/components/TagsGrid/TagGridItem.esm.js.map +1 -0
  127. package/dist/components/TagsGrid/TagsGrid.esm.js +49 -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 +735 -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,735 @@
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
+ ...theme.overrides?.BackstageMarkdownContent
392
+ },
393
+ successColor: {
394
+ color: theme.palette.success.main
395
+ },
396
+ questionDivider: {
397
+ marginTop: theme.spacing(2),
398
+ marginBottom: theme.spacing(2)
399
+ },
400
+ questionCard: {
401
+ marginBottom: theme.spacing(1),
402
+ position: "relative"
403
+ },
404
+ questionCardVote: {
405
+ textAlign: "center",
406
+ width: "32px",
407
+ marginRight: "20px",
408
+ marginLeft: "5px",
409
+ display: "inline-block",
410
+ verticalAlign: "top"
411
+ },
412
+ questionCardContent: {
413
+ minHeight: "160px"
414
+ },
415
+ questionListItem: {
416
+ padding: "0.7rem",
417
+ paddingBottom: "1.4rem"
418
+ },
419
+ questionListItemStats: {
420
+ width: "70px",
421
+ textAlign: "right",
422
+ marginRight: "5px",
423
+ display: "inline-block",
424
+ verticalAlign: "top"
425
+ },
426
+ questionListItemContent: {
427
+ display: "inline-block",
428
+ width: "calc(100% - 80px)"
429
+ },
430
+ questionListItemAuthor: {
431
+ display: "inline",
432
+ float: "right"
433
+ },
434
+ questionListItemAvatar: {
435
+ display: "inline-flex !important",
436
+ marginRight: "0.25rem",
437
+ fontSize: "1rem",
438
+ maxWidth: "1rem",
439
+ maxHeight: "1rem"
440
+ },
441
+ answerCardContent: {
442
+ display: "inline-block",
443
+ width: "calc(100% - 70px)"
444
+ },
445
+ questionCardAuthor: {
446
+ padding: theme.spacing(1),
447
+ float: "right",
448
+ maxWidth: "200px",
449
+ border: `1px solid ${theme.palette.action.selected}`,
450
+ "& .avatar": {
451
+ width: theme.spacing(3),
452
+ height: theme.spacing(3),
453
+ fontSize: "1rem"
454
+ }
455
+ },
456
+ questionListPagination: {
457
+ marginTop: theme.spacing(2)
458
+ },
459
+ postButton: {
460
+ marginTop: theme.spacing(1),
461
+ marginBottom: theme.spacing(1)
462
+ },
463
+ questionsPerPageInput: {
464
+ paddingTop: "10px"
465
+ },
466
+ questionsPerPage: {
467
+ marginRight: theme.spacing(3)
468
+ },
469
+ postHighlightListContainer: {
470
+ width: "100%",
471
+ backgroundColor: theme.palette.background.paper,
472
+ border: `1px solid ${theme.palette.action.selected}`,
473
+ borderRadius: theme.shape.borderRadius,
474
+ "&:not(:first-child)": {
475
+ marginTop: theme.spacing(2)
476
+ }
477
+ },
478
+ postHighlightList: {
479
+ paddingBottom: "0px",
480
+ "& p": {
481
+ marginTop: "0",
482
+ marginBottom: "0"
483
+ }
484
+ },
485
+ filterPanel: {
486
+ border: `1px solid ${theme.palette.action.selected}`,
487
+ borderRadius: theme.shape.borderRadius,
488
+ padding: theme.spacing(3)
489
+ },
490
+ questionCardMetadata: {
491
+ marginTop: theme.spacing(3)
492
+ },
493
+ marginRight: {
494
+ marginRight: theme.spacing(1)
495
+ },
496
+ marginLeft: {
497
+ marginLeft: theme.spacing(1)
498
+ },
499
+ questionCardActions: {
500
+ marginTop: theme.spacing(2),
501
+ "& a": {
502
+ marginRight: theme.spacing(1)
503
+ }
504
+ },
505
+ noPadding: {
506
+ padding: `0 !important`
507
+ },
508
+ menuIcon: {
509
+ minWidth: "26px"
510
+ },
511
+ deleteModal: {
512
+ position: "absolute",
513
+ top: "20%",
514
+ left: "50%",
515
+ transform: "translate(-50%, -50%)",
516
+ width: 400,
517
+ backgroundColor: theme.palette.background.default,
518
+ border: `1px solid ${theme.palette.action.selected}`,
519
+ borderRadius: theme.shape.borderRadius,
520
+ padding: theme.spacing(2),
521
+ "& button": {
522
+ marginTop: theme.spacing(2),
523
+ float: "right"
524
+ }
525
+ },
526
+ authorLink: {
527
+ textOverflow: "ellipsis",
528
+ overflow: "hidden",
529
+ whiteSpace: "nowrap"
530
+ },
531
+ highlight: {
532
+ animation: "$highlight 2s"
533
+ },
534
+ "@keyframes highlight": {
535
+ "0%": {
536
+ boxShadow: `0px 0px 0px 3px ${theme.palette.secondary.light}`
537
+ },
538
+ "100%": {
539
+ boxShadow: "none"
540
+ }
541
+ },
542
+ dateFilter: {
543
+ minWidth: "200px",
544
+ marginTop: theme.spacing(2),
545
+ marginBottom: theme.spacing(2)
546
+ }
547
+ };
548
+ });
549
+ const useBaseUrl = () => {
550
+ try {
551
+ const config = useApi(configApiRef);
552
+ return config.getOptionalString("app.baseUrl");
553
+ } catch {
554
+ return void 0;
555
+ }
556
+ };
557
+ const useBasePath = () => {
558
+ const base = "http://sample.dev";
559
+ const url = useBaseUrl() ?? "/";
560
+ const { pathname } = new URL(url, base);
561
+ return trimEnd(pathname, "/");
562
+ };
563
+ const userCache = /* @__PURE__ */ new Map();
564
+ const dataLoaderFactory = (catalogApi) => new DataLoader(
565
+ async (entityRefs) => {
566
+ const { items } = await catalogApi.getEntitiesByRefs({
567
+ fields: [
568
+ "kind",
569
+ "metadata.name",
570
+ "metadata.namespace",
571
+ "spec.profile.displayName",
572
+ "spec.profile.picture"
573
+ ],
574
+ entityRefs
575
+ });
576
+ entityRefs.forEach((entityRef, index) => {
577
+ userCache.set(entityRef, items[index]);
578
+ });
579
+ return items;
580
+ },
581
+ {
582
+ name: "EntityAuthorLoader",
583
+ cacheMap: /* @__PURE__ */ new Map(),
584
+ maxBatchSize: 100,
585
+ batchScheduleFn: (callback) => {
586
+ setTimeout(callback, 50);
587
+ }
588
+ }
589
+ );
590
+ const useEntityAuthor = (entity) => {
591
+ const catalogApi = useApi(catalogApiRef);
592
+ const identityApi = useApi(identityApiRef);
593
+ const [name, setName] = React.useState(void 0);
594
+ const [user, setUser] = React.useState(null);
595
+ const [initials, setInitials] = React.useState(null);
596
+ const [currentUser, setCurrentUser] = React.useState(null);
597
+ const anonymous = "anonymous" in entity ? entity.anonymous ?? false : false;
598
+ let author = (
599
+ // eslint-disable-next-line no-nested-ternary
600
+ "author" in entity ? entity.author : "userRef" in entity ? entity.userRef : entity.owner
601
+ );
602
+ if (!author.startsWith("user:")) {
603
+ author = `user:${author}`;
604
+ }
605
+ const { primaryTitle: userName } = useEntityPresentation(author);
606
+ useEffect(() => {
607
+ if (anonymous) {
608
+ return;
609
+ }
610
+ if (userCache.get(author)) {
611
+ setUser(userCache.get(author));
612
+ return;
613
+ }
614
+ dataLoaderFactory(catalogApi).load(author).then((data) => {
615
+ if (data) {
616
+ setUser(data);
617
+ } else {
618
+ setUser(null);
619
+ }
620
+ }).catch(() => {
621
+ setUser(null);
622
+ });
623
+ }, [catalogApi, author, anonymous]);
624
+ useEffect(() => {
625
+ identityApi.getBackstageIdentity().then((res) => {
626
+ setCurrentUser(res.userEntityRef ?? "user:default/guest");
627
+ });
628
+ }, [identityApi]);
629
+ useEffect(() => {
630
+ let displayName = userName;
631
+ if (author === currentUser) {
632
+ displayName = "You";
633
+ if (anonymous) {
634
+ displayName += " (anonymous)";
635
+ }
636
+ }
637
+ setName(displayName);
638
+ }, [author, anonymous, currentUser, userName]);
639
+ useEffect(() => {
640
+ const init = (name ?? "").split(" ").map((p) => p[0]).join("").substring(0, 2).toUpperCase();
641
+ setInitials(init);
642
+ }, [name]);
643
+ return { name, initials, user };
644
+ };
645
+ let followedTags = void 0;
646
+ const useTagsFollow = () => {
647
+ const [tags, setTags] = React.useState(followedTags ?? []);
648
+ const [loading, setLoading] = React.useState(followedTags === void 0);
649
+ const qetaApi = useApi(qetaApiRef);
650
+ useEffect(() => {
651
+ if (followedTags === void 0) {
652
+ qetaApi.getFollowedTags().then((res) => {
653
+ followedTags = res.tags;
654
+ setTags(res.tags);
655
+ setLoading(false);
656
+ });
657
+ } else {
658
+ setTags(followedTags);
659
+ }
660
+ }, [qetaApi]);
661
+ const followTag = useCallback(
662
+ (tag) => {
663
+ qetaApi.followTag(tag).then(() => {
664
+ setTags((prev) => [...prev, tag]);
665
+ followedTags?.push(tag);
666
+ });
667
+ },
668
+ [qetaApi]
669
+ );
670
+ const unfollowTag = useCallback(
671
+ (tag) => {
672
+ qetaApi.unfollowTag(tag).then(() => {
673
+ setTags((prev) => prev.filter((t) => t !== tag));
674
+ followedTags = followedTags?.filter((t) => t !== tag);
675
+ });
676
+ },
677
+ [qetaApi]
678
+ );
679
+ const isFollowingTag = useCallback(
680
+ (tag) => tags.includes(tag),
681
+ [tags]
682
+ );
683
+ return {
684
+ tags,
685
+ followTag,
686
+ unfollowTag,
687
+ isFollowingTag,
688
+ loading
689
+ };
690
+ };
691
+ let followedEntities = void 0;
692
+ const useEntityFollow = () => {
693
+ const [entities, setEntities] = React.useState(
694
+ followedEntities ?? []
695
+ );
696
+ const [loading, setLoading] = React.useState(followedEntities === void 0);
697
+ const qetaApi = useApi(qetaApiRef);
698
+ useEffect(() => {
699
+ if (followedEntities === void 0) {
700
+ qetaApi.getFollowedEntities().then((res) => {
701
+ followedEntities = res.entityRefs;
702
+ setEntities(res.entityRefs);
703
+ setLoading(false);
704
+ });
705
+ } else {
706
+ setEntities(followedEntities);
707
+ }
708
+ }, [qetaApi]);
709
+ const followEntity = useCallback(
710
+ (entityRef) => {
711
+ qetaApi.followEntity(entityRef).then(() => {
712
+ setEntities((prev) => [...prev, entityRef]);
713
+ followedEntities?.push(entityRef);
714
+ });
715
+ },
716
+ [qetaApi]
717
+ );
718
+ const unfollowEntity = useCallback(
719
+ (entityRef) => {
720
+ qetaApi.unfollowEntity(entityRef).then(() => {
721
+ setEntities((prev) => prev.filter((t) => t !== entityRef));
722
+ followedEntities = followedEntities?.filter((t) => t !== entityRef);
723
+ });
724
+ },
725
+ [qetaApi]
726
+ );
727
+ const isFollowingEntity = useCallback(
728
+ (entityRef) => entities.includes(entityRef),
729
+ [entities]
730
+ );
731
+ return { entities, followEntity, unfollowEntity, isFollowingEntity, loading };
732
+ };
733
+
734
+ export { useBasePath, useBaseUrl, useEntityAuthor, useEntityFollow, useEntityQueryParameter, useIdentityApi, useIsDarkTheme, usePaginatedPosts, useQetaApi, useStyles, useTagsFollow, useTranslation, useVoting };
735
+ //# sourceMappingURL=hooks.esm.js.map