@drodil/backstage-plugin-qeta-react 3.52.6 → 3.53.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 (170) hide show
  1. package/dist/components/AIAnswerCard/AIAnswerCard.esm.js +11 -11
  2. package/dist/components/AIAnswerCard/AIAnswerCard.esm.js.map +1 -1
  3. package/dist/components/AnswerForm/AnswerForm.esm.js +5 -0
  4. package/dist/components/AnswerForm/AnswerForm.esm.js.map +1 -1
  5. package/dist/components/AnswersContainer/AnswerListItem.esm.js +4 -2
  6. package/dist/components/AnswersContainer/AnswerListItem.esm.js.map +1 -1
  7. package/dist/components/AnswersContainer/AnswersContainer.esm.js +59 -231
  8. package/dist/components/AnswersContainer/AnswersContainer.esm.js.map +1 -1
  9. package/dist/components/AnswersContainer/AnswersGridItem.esm.js +167 -0
  10. package/dist/components/AnswersContainer/AnswersGridItem.esm.js.map +1 -0
  11. package/dist/components/ArticleContent/ArticleButtons.esm.js +14 -1
  12. package/dist/components/ArticleContent/ArticleButtons.esm.js.map +1 -1
  13. package/dist/components/Badges/UserBadges.esm.js +7 -2
  14. package/dist/components/Badges/UserBadges.esm.js.map +1 -1
  15. package/dist/components/Buttons/AddToCollectionButton.esm.js +7 -2
  16. package/dist/components/Buttons/AddToCollectionButton.esm.js.map +1 -1
  17. package/dist/components/Buttons/EntityFollowButton.esm.js +7 -2
  18. package/dist/components/Buttons/EntityFollowButton.esm.js.map +1 -1
  19. package/dist/components/Buttons/RankingButtons.esm.js +94 -0
  20. package/dist/components/Buttons/RankingButtons.esm.js.map +1 -0
  21. package/dist/components/Buttons/TagFollowButton.esm.js +8 -3
  22. package/dist/components/Buttons/TagFollowButton.esm.js.map +1 -1
  23. package/dist/components/Buttons/UserFollowButton.esm.js +7 -2
  24. package/dist/components/Buttons/UserFollowButton.esm.js.map +1 -1
  25. package/dist/components/Buttons/VoteButtons.esm.js +12 -1
  26. package/dist/components/Buttons/VoteButtons.esm.js.map +1 -1
  27. package/dist/components/CollectionsContainer/CollectionListItem.esm.js +143 -0
  28. package/dist/components/CollectionsContainer/CollectionListItem.esm.js.map +1 -0
  29. package/dist/components/CollectionsContainer/CollectionsContainer.esm.js +45 -0
  30. package/dist/components/CollectionsContainer/CollectionsContainer.esm.js.map +1 -0
  31. package/dist/components/CollectionsContainer/CollectionsGridItem.esm.js.map +1 -0
  32. package/dist/components/ContentHeader/ContentHeader.esm.js +6 -0
  33. package/dist/components/ContentHeader/ContentHeader.esm.js.map +1 -1
  34. package/dist/components/EntitiesContainer/EntitiesContainer.esm.js +44 -0
  35. package/dist/components/EntitiesContainer/EntitiesContainer.esm.js.map +1 -0
  36. package/dist/components/{EntitiesGrid → EntitiesContainer}/EntitiesGridItem.esm.js +18 -26
  37. package/dist/components/EntitiesContainer/EntitiesGridItem.esm.js.map +1 -0
  38. package/dist/components/EntitiesContainer/EntityListItem.esm.js +102 -0
  39. package/dist/components/EntitiesContainer/EntityListItem.esm.js.map +1 -0
  40. package/dist/components/FilterPanel/FilterPanel.esm.js +2 -2
  41. package/dist/components/FilterPanel/FilterPanel.esm.js.map +1 -1
  42. package/dist/components/FollowedLists/FollowedEntitiesList.esm.js +7 -3
  43. package/dist/components/FollowedLists/FollowedEntitiesList.esm.js.map +1 -1
  44. package/dist/components/FollowedLists/FollowedTagsList.esm.js +8 -4
  45. package/dist/components/FollowedLists/FollowedTagsList.esm.js.map +1 -1
  46. package/dist/components/FollowedLists/FollowedUsersList.esm.js +7 -3
  47. package/dist/components/FollowedLists/FollowedUsersList.esm.js.map +1 -1
  48. package/dist/components/HomePageCards/ImpactCard.esm.js +7 -2
  49. package/dist/components/HomePageCards/ImpactCard.esm.js.map +1 -1
  50. package/dist/components/HomePageCards/PostsCard.esm.js +23 -15
  51. package/dist/components/HomePageCards/PostsCard.esm.js.map +1 -1
  52. package/dist/components/LeftMenu/LeftMenu.esm.js +15 -16
  53. package/dist/components/LeftMenu/LeftMenu.esm.js.map +1 -1
  54. package/dist/components/LinkCard/LinkCard.esm.js +4 -0
  55. package/dist/components/LinkCard/LinkCard.esm.js.map +1 -1
  56. package/dist/components/Links/Links.esm.js +11 -1
  57. package/dist/components/Links/Links.esm.js.map +1 -1
  58. package/dist/components/PostForm/EntitiesInput.esm.js +229 -226
  59. package/dist/components/PostForm/EntitiesInput.esm.js.map +1 -1
  60. package/dist/components/PostForm/PostForm.esm.js +13 -10
  61. package/dist/components/PostForm/PostForm.esm.js.map +1 -1
  62. package/dist/components/PostForm/TagInput.esm.js +4 -3
  63. package/dist/components/PostForm/TagInput.esm.js.map +1 -1
  64. package/dist/components/PostHighlightList/PostHighlightList.esm.js +6 -2
  65. package/dist/components/PostHighlightList/PostHighlightList.esm.js.map +1 -1
  66. package/dist/components/PostsContainer/PostListItem.esm.js +58 -19
  67. package/dist/components/PostsContainer/PostListItem.esm.js.map +1 -1
  68. package/dist/components/PostsContainer/PostsContainer.esm.js +137 -128
  69. package/dist/components/PostsContainer/PostsContainer.esm.js.map +1 -1
  70. package/dist/components/{PostsGrid → PostsContainer}/PostsGridItem.esm.js +29 -53
  71. package/dist/components/PostsContainer/PostsGridItem.esm.js.map +1 -0
  72. package/dist/components/PostsTable/PostsTable.esm.js +7 -2
  73. package/dist/components/PostsTable/PostsTable.esm.js.map +1 -1
  74. package/dist/components/QetaEntityContainer/QetaEntityContainer.esm.js +178 -0
  75. package/dist/components/QetaEntityContainer/QetaEntityContainer.esm.js.map +1 -0
  76. package/dist/components/QuestionCard/QuestionCard.esm.js +100 -88
  77. package/dist/components/QuestionCard/QuestionCard.esm.js.map +1 -1
  78. package/dist/components/SuggestionsCard/SuggestionsCard.esm.js +7 -3
  79. package/dist/components/SuggestionsCard/SuggestionsCard.esm.js.map +1 -1
  80. package/dist/components/TagsAndEntities/EntityChip.esm.js +6 -2
  81. package/dist/components/TagsAndEntities/EntityChip.esm.js.map +1 -1
  82. package/dist/components/TagsAndEntities/TagChip.esm.js +6 -2
  83. package/dist/components/TagsAndEntities/TagChip.esm.js.map +1 -1
  84. package/dist/components/TagsAndEntities/UserChip.esm.js +7 -3
  85. package/dist/components/TagsAndEntities/UserChip.esm.js.map +1 -1
  86. package/dist/components/TagsContainer/CreateTagModal.esm.js.map +1 -0
  87. package/dist/components/TagsContainer/EditTagModal.esm.js.map +1 -0
  88. package/dist/components/{TagsGrid → TagsContainer}/TagGridItem.esm.js +3 -3
  89. package/dist/components/TagsContainer/TagGridItem.esm.js.map +1 -0
  90. package/dist/components/TagsContainer/TagListItem.esm.js +243 -0
  91. package/dist/components/TagsContainer/TagListItem.esm.js.map +1 -0
  92. package/dist/components/TagsContainer/TagsContainer.esm.js +86 -0
  93. package/dist/components/TagsContainer/TagsContainer.esm.js.map +1 -0
  94. package/dist/components/TemplateList/TemplateForm.esm.js +7 -2
  95. package/dist/components/TemplateList/TemplateForm.esm.js.map +1 -1
  96. package/dist/components/TemplateList/TemplateList.esm.js +7 -3
  97. package/dist/components/TemplateList/TemplateList.esm.js.map +1 -1
  98. package/dist/components/Timeline/TimelineItem.esm.js +4 -0
  99. package/dist/components/Timeline/TimelineItem.esm.js.map +1 -1
  100. package/dist/components/TopRankingUsersCard/TopRankingUsersCard.esm.js +7 -2
  101. package/dist/components/TopRankingUsersCard/TopRankingUsersCard.esm.js.map +1 -1
  102. package/dist/components/UsersContainer/UserListItem.esm.js +133 -0
  103. package/dist/components/UsersContainer/UserListItem.esm.js.map +1 -0
  104. package/dist/components/UsersContainer/UsersContainer.esm.js +44 -0
  105. package/dist/components/UsersContainer/UsersContainer.esm.js.map +1 -0
  106. package/dist/components/{UsersGrid → UsersContainer}/UsersGridItem.esm.js +35 -38
  107. package/dist/components/UsersContainer/UsersGridItem.esm.js.map +1 -0
  108. package/dist/components/Utility/QetaGridHeader.esm.js +15 -11
  109. package/dist/components/Utility/QetaGridHeader.esm.js.map +1 -1
  110. package/dist/components/Utility/QetaPagination.esm.js +69 -0
  111. package/dist/components/Utility/QetaPagination.esm.js.map +1 -0
  112. package/dist/hooks/useEntityAuthor.esm.js +3 -1
  113. package/dist/hooks/useEntityAuthor.esm.js.map +1 -1
  114. package/dist/hooks/useListItemStyles.esm.js +22 -0
  115. package/dist/hooks/useListItemStyles.esm.js.map +1 -0
  116. package/dist/hooks/{usePaginatedPosts.esm.js → useQetaEntities.esm.js} +59 -86
  117. package/dist/hooks/useQetaEntities.esm.js.map +1 -0
  118. package/dist/hooks/useUserSettings.esm.js +83 -0
  119. package/dist/hooks/useUserSettings.esm.js.map +1 -0
  120. package/dist/index.d.ts +211 -58
  121. package/dist/index.esm.js +21 -8
  122. package/dist/index.esm.js.map +1 -1
  123. package/dist/routes.esm.js +6 -1
  124. package/dist/routes.esm.js.map +1 -1
  125. package/dist/translation.esm.js +49 -0
  126. package/dist/translation.esm.js.map +1 -1
  127. package/package.json +2 -2
  128. package/dist/components/AnswersContainer/AnswerList.esm.js +0 -54
  129. package/dist/components/AnswersContainer/AnswerList.esm.js.map +0 -1
  130. package/dist/components/CollectionsGrid/CollectionsGrid.esm.js +0 -204
  131. package/dist/components/CollectionsGrid/CollectionsGrid.esm.js.map +0 -1
  132. package/dist/components/CollectionsGrid/CollectionsGridContent.esm.js +0 -55
  133. package/dist/components/CollectionsGrid/CollectionsGridContent.esm.js.map +0 -1
  134. package/dist/components/CollectionsGrid/CollectionsGridItem.esm.js.map +0 -1
  135. package/dist/components/CollectionsGrid/NoCollectionsCard.esm.js +0 -21
  136. package/dist/components/CollectionsGrid/NoCollectionsCard.esm.js.map +0 -1
  137. package/dist/components/EntitiesGrid/EntitiesGrid.esm.js +0 -136
  138. package/dist/components/EntitiesGrid/EntitiesGrid.esm.js.map +0 -1
  139. package/dist/components/EntitiesGrid/EntitiesGridContent.esm.js +0 -46
  140. package/dist/components/EntitiesGrid/EntitiesGridContent.esm.js.map +0 -1
  141. package/dist/components/EntitiesGrid/EntitiesGridItem.esm.js.map +0 -1
  142. package/dist/components/EntitiesGrid/NoEntitiesCard.esm.js +0 -21
  143. package/dist/components/EntitiesGrid/NoEntitiesCard.esm.js.map +0 -1
  144. package/dist/components/PostsContainer/PostList.esm.js +0 -83
  145. package/dist/components/PostsContainer/PostList.esm.js.map +0 -1
  146. package/dist/components/PostsGrid/PostsGrid.esm.js +0 -197
  147. package/dist/components/PostsGrid/PostsGrid.esm.js.map +0 -1
  148. package/dist/components/PostsGrid/PostsGridContent.esm.js +0 -90
  149. package/dist/components/PostsGrid/PostsGridContent.esm.js.map +0 -1
  150. package/dist/components/PostsGrid/PostsGridItem.esm.js.map +0 -1
  151. package/dist/components/TagsGrid/CreateTagModal.esm.js.map +0 -1
  152. package/dist/components/TagsGrid/EditTagModal.esm.js.map +0 -1
  153. package/dist/components/TagsGrid/NoTagsCard.esm.js +0 -21
  154. package/dist/components/TagsGrid/NoTagsCard.esm.js.map +0 -1
  155. package/dist/components/TagsGrid/TagGridItem.esm.js.map +0 -1
  156. package/dist/components/TagsGrid/TagsGrid.esm.js +0 -190
  157. package/dist/components/TagsGrid/TagsGrid.esm.js.map +0 -1
  158. package/dist/components/TagsGrid/TagsGridContent.esm.js +0 -62
  159. package/dist/components/TagsGrid/TagsGridContent.esm.js.map +0 -1
  160. package/dist/components/UsersGrid/NoUsersCard.esm.js +0 -21
  161. package/dist/components/UsersGrid/NoUsersCard.esm.js.map +0 -1
  162. package/dist/components/UsersGrid/UsersGrid.esm.js +0 -137
  163. package/dist/components/UsersGrid/UsersGrid.esm.js.map +0 -1
  164. package/dist/components/UsersGrid/UsersGridContent.esm.js +0 -46
  165. package/dist/components/UsersGrid/UsersGridContent.esm.js.map +0 -1
  166. package/dist/components/UsersGrid/UsersGridItem.esm.js.map +0 -1
  167. package/dist/hooks/usePaginatedPosts.esm.js.map +0 -1
  168. /package/dist/components/{CollectionsGrid → CollectionsContainer}/CollectionsGridItem.esm.js +0 -0
  169. /package/dist/components/{TagsGrid → TagsContainer}/CreateTagModal.esm.js +0 -0
  170. /package/dist/components/{TagsGrid → TagsContainer}/EditTagModal.esm.js +0 -0
@@ -38,6 +38,7 @@ const useUserInfo = (entityRef, anonymous) => {
38
38
  const identityApi = useApi(identityApiRef);
39
39
  const { t } = useTranslationRef(qetaTranslationRef);
40
40
  const [name, setName] = useState("");
41
+ const [isCurrentUser, setIsCurrentUser] = useState(false);
41
42
  const [user, setUser] = useState(null);
42
43
  const [initials, setInitials] = useState(null);
43
44
  const [currentUser, setCurrentUser] = useState(null);
@@ -80,6 +81,7 @@ const useUserInfo = (entityRef, anonymous) => {
80
81
  });
81
82
  const userRef = parseEntityRef(ref, { defaultKind: "user" });
82
83
  if (currentUserRef.name === userRef.name && currentUserRef.namespace === userRef.namespace) {
84
+ setIsCurrentUser(true);
83
85
  displayName = `${t("userLink.you")}${anonymous ? ` (${t("userLink.anonymous").toLocaleLowerCase("en-US")})` : ""}`;
84
86
  } else if (anonymous) {
85
87
  displayName = t("userLink.anonymous");
@@ -93,7 +95,7 @@ const useUserInfo = (entityRef, anonymous) => {
93
95
  const init = (name ?? "").replace(/[^a-zA-Z]/g, "").split(" ").map((p) => p[0]).join("").substring(0, 2).toUpperCase();
94
96
  setInitials(init);
95
97
  }, [name]);
96
- return { name, initials, user, secondaryTitle, Icon };
98
+ return { name, initials, user, secondaryTitle, Icon, isCurrentUser };
97
99
  };
98
100
  const useEntityAuthor = (entity) => {
99
101
  const anonymous = "anonymous" in entity ? entity.anonymous ?? false : false;
@@ -1 +1 @@
1
- {"version":3,"file":"useEntityAuthor.esm.js","sources":["../../src/hooks/useEntityAuthor.ts"],"sourcesContent":["import {\n parseEntityRef,\n stringifyEntityRef,\n UserEntity,\n} from '@backstage/catalog-model';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport DataLoader from 'dataloader';\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\nimport {\n catalogApiRef,\n useEntityPresentation,\n} from '@backstage/plugin-catalog-react';\nimport { useEffect, useState } from 'react';\nimport {\n AnswerResponse,\n CollectionResponse,\n PostResponse,\n UserResponse,\n} from '@drodil/backstage-plugin-qeta-common';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { qetaTranslationRef } from '../translation.ts';\n\nconst userCache: Map<string, UserEntity> = new Map();\nconst dataLoaderFactory = (catalogApi: CatalogApi) =>\n new DataLoader(\n async (entityRefs: readonly string[]) => {\n const { items } = await catalogApi.getEntitiesByRefs({\n fields: [\n 'kind',\n 'metadata.name',\n 'metadata.namespace',\n 'spec.profile.displayName',\n 'spec.profile.picture',\n ],\n entityRefs: entityRefs as string[],\n });\n\n entityRefs.forEach((entityRef, index) => {\n userCache.set(entityRef, items[index] as UserEntity);\n });\n return items;\n },\n {\n name: 'EntityAuthorLoader',\n cacheMap: new Map(),\n maxBatchSize: 100,\n batchScheduleFn: callback => {\n setTimeout(callback, 50);\n },\n },\n );\n\nexport const useUserInfo = (entityRef: string, anonymous?: boolean) => {\n const catalogApi = useApi(catalogApiRef);\n const identityApi = useApi(identityApiRef);\n const { t } = useTranslationRef(qetaTranslationRef);\n const [name, setName] = useState<string>('');\n const [user, setUser] = useState<UserEntity | null>(null);\n const [initials, setInitials] = useState<string | null>(null);\n const [currentUser, setCurrentUser] = useState<string | null>(null);\n const ref = stringifyEntityRef(\n parseEntityRef(entityRef, { defaultKind: 'user' }),\n );\n\n const {\n primaryTitle: userName,\n secondaryTitle,\n Icon,\n } = useEntityPresentation(ref, { defaultKind: 'user' });\n\n useEffect(() => {\n if (anonymous) {\n return;\n }\n\n if (userCache.get(ref)) {\n setUser(userCache.get(ref) as UserEntity);\n return;\n }\n\n dataLoaderFactory(catalogApi)\n .load(ref)\n .then(data => {\n if (data) {\n setUser(data as UserEntity);\n } else {\n setUser(null);\n }\n })\n .catch(() => {\n setUser(null);\n });\n }, [catalogApi, ref, anonymous]);\n\n useEffect(() => {\n identityApi.getBackstageIdentity().then(res => {\n setCurrentUser(res.userEntityRef ?? 'user:default/guest');\n });\n }, [identityApi]);\n\n useEffect(() => {\n let displayName = userName;\n if (currentUser) {\n const currentUserRef = parseEntityRef(currentUser, {\n defaultKind: 'user',\n });\n const userRef = parseEntityRef(ref, { defaultKind: 'user' });\n if (\n currentUserRef.name === userRef.name &&\n currentUserRef.namespace === userRef.namespace\n ) {\n displayName = `${t('userLink.you')}${\n anonymous\n ? ` (${t('userLink.anonymous').toLocaleLowerCase('en-US')})`\n : ''\n }`;\n } else if (anonymous) {\n displayName = t('userLink.anonymous');\n }\n } else if (anonymous) {\n displayName = t('userLink.anonymous');\n }\n setName(displayName);\n }, [ref, anonymous, currentUser, userName, t]);\n\n useEffect(() => {\n const init = (name ?? '')\n .replace(/[^a-zA-Z]/g, '')\n .split(' ')\n .map(p => p[0])\n .join('')\n .substring(0, 2)\n .toUpperCase();\n setInitials(init);\n }, [name]);\n\n return { name, initials, user, secondaryTitle, Icon };\n};\n\nexport const useEntityAuthor = (\n entity: PostResponse | AnswerResponse | CollectionResponse | UserResponse,\n) => {\n const anonymous = 'anonymous' in entity ? entity.anonymous ?? false : false;\n const author =\n // eslint-disable-next-line no-nested-ternary\n 'author' in entity\n ? entity.author\n : 'userRef' in entity\n ? entity.userRef\n : entity.owner;\n return useUserInfo(author, anonymous);\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useEntityAuthor.esm.js","sources":["../../src/hooks/useEntityAuthor.ts"],"sourcesContent":["import {\n parseEntityRef,\n stringifyEntityRef,\n UserEntity,\n} from '@backstage/catalog-model';\nimport { CatalogApi } from '@backstage/catalog-client';\nimport DataLoader from 'dataloader';\nimport { identityApiRef, useApi } from '@backstage/core-plugin-api';\nimport {\n catalogApiRef,\n useEntityPresentation,\n} from '@backstage/plugin-catalog-react';\nimport { useEffect, useState } from 'react';\nimport {\n AnswerResponse,\n CollectionResponse,\n PostResponse,\n UserResponse,\n} from '@drodil/backstage-plugin-qeta-common';\nimport { useTranslationRef } from '@backstage/core-plugin-api/alpha';\nimport { qetaTranslationRef } from '../translation.ts';\n\nconst userCache: Map<string, UserEntity> = new Map();\nconst dataLoaderFactory = (catalogApi: CatalogApi) =>\n new DataLoader(\n async (entityRefs: readonly string[]) => {\n const { items } = await catalogApi.getEntitiesByRefs({\n fields: [\n 'kind',\n 'metadata.name',\n 'metadata.namespace',\n 'spec.profile.displayName',\n 'spec.profile.picture',\n ],\n entityRefs: entityRefs as string[],\n });\n\n entityRefs.forEach((entityRef, index) => {\n userCache.set(entityRef, items[index] as UserEntity);\n });\n return items;\n },\n {\n name: 'EntityAuthorLoader',\n cacheMap: new Map(),\n maxBatchSize: 100,\n batchScheduleFn: callback => {\n setTimeout(callback, 50);\n },\n },\n );\n\nexport const useUserInfo = (entityRef: string, anonymous?: boolean) => {\n const catalogApi = useApi(catalogApiRef);\n const identityApi = useApi(identityApiRef);\n const { t } = useTranslationRef(qetaTranslationRef);\n const [name, setName] = useState<string>('');\n const [isCurrentUser, setIsCurrentUser] = useState<boolean>(false);\n const [user, setUser] = useState<UserEntity | null>(null);\n const [initials, setInitials] = useState<string | null>(null);\n const [currentUser, setCurrentUser] = useState<string | null>(null);\n const ref = stringifyEntityRef(\n parseEntityRef(entityRef, { defaultKind: 'user' }),\n );\n\n const {\n primaryTitle: userName,\n secondaryTitle,\n Icon,\n } = useEntityPresentation(ref, { defaultKind: 'user' });\n\n useEffect(() => {\n if (anonymous) {\n return;\n }\n\n if (userCache.get(ref)) {\n setUser(userCache.get(ref) as UserEntity);\n return;\n }\n\n dataLoaderFactory(catalogApi)\n .load(ref)\n .then(data => {\n if (data) {\n setUser(data as UserEntity);\n } else {\n setUser(null);\n }\n })\n .catch(() => {\n setUser(null);\n });\n }, [catalogApi, ref, anonymous]);\n\n useEffect(() => {\n identityApi.getBackstageIdentity().then(res => {\n setCurrentUser(res.userEntityRef ?? 'user:default/guest');\n });\n }, [identityApi]);\n\n useEffect(() => {\n let displayName = userName;\n if (currentUser) {\n const currentUserRef = parseEntityRef(currentUser, {\n defaultKind: 'user',\n });\n const userRef = parseEntityRef(ref, { defaultKind: 'user' });\n if (\n currentUserRef.name === userRef.name &&\n currentUserRef.namespace === userRef.namespace\n ) {\n setIsCurrentUser(true);\n displayName = `${t('userLink.you')}${\n anonymous\n ? ` (${t('userLink.anonymous').toLocaleLowerCase('en-US')})`\n : ''\n }`;\n } else if (anonymous) {\n displayName = t('userLink.anonymous');\n }\n } else if (anonymous) {\n displayName = t('userLink.anonymous');\n }\n setName(displayName);\n }, [ref, anonymous, currentUser, userName, t]);\n\n useEffect(() => {\n const init = (name ?? '')\n .replace(/[^a-zA-Z]/g, '')\n .split(' ')\n .map(p => p[0])\n .join('')\n .substring(0, 2)\n .toUpperCase();\n setInitials(init);\n }, [name]);\n\n return { name, initials, user, secondaryTitle, Icon, isCurrentUser };\n};\n\nexport const useEntityAuthor = (\n entity: PostResponse | AnswerResponse | CollectionResponse | UserResponse,\n) => {\n const anonymous = 'anonymous' in entity ? entity.anonymous ?? false : false;\n const author =\n // eslint-disable-next-line no-nested-ternary\n 'author' in entity\n ? entity.author\n : 'userRef' in entity\n ? entity.userRef\n : entity.owner;\n return useUserInfo(author, anonymous);\n};\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { makeStyles } from '@material-ui/core';
2
+
3
+ const useListItemStyles = makeStyles((theme) => ({
4
+ root: {
5
+ display: "flex",
6
+ flexDirection: "row",
7
+ alignItems: "center",
8
+ padding: theme.spacing(1, 2),
9
+ "&:hover": {
10
+ backgroundColor: theme.palette.action.hover
11
+ },
12
+ textDecoration: "none",
13
+ color: "inherit",
14
+ width: "100%",
15
+ "&:first-child": {
16
+ paddingTop: theme.spacing(2)
17
+ }
18
+ }
19
+ }));
20
+
21
+ export { useListItemStyles };
22
+ //# sourceMappingURL=useListItemStyles.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useListItemStyles.esm.js","sources":["../../src/hooks/useListItemStyles.ts"],"sourcesContent":["import { makeStyles } from '@material-ui/core';\n\nexport const useListItemStyles = makeStyles(theme => ({\n root: {\n display: 'flex',\n flexDirection: 'row',\n alignItems: 'center',\n padding: theme.spacing(1, 2),\n '&:hover': {\n backgroundColor: theme.palette.action.hover,\n },\n textDecoration: 'none',\n color: 'inherit',\n width: '100%',\n '&:first-child': {\n paddingTop: theme.spacing(2),\n },\n },\n}));\n"],"names":[],"mappings":";;AAEa,MAAA,iBAAA,GAAoB,WAAW,CAAU,KAAA,MAAA;AAAA,EACpD,IAAM,EAAA;AAAA,IACJ,OAAS,EAAA,MAAA;AAAA,IACT,aAAe,EAAA,KAAA;AAAA,IACf,UAAY,EAAA,QAAA;AAAA,IACZ,OAAS,EAAA,KAAA,CAAM,OAAQ,CAAA,CAAA,EAAG,CAAC,CAAA;AAAA,IAC3B,SAAW,EAAA;AAAA,MACT,eAAA,EAAiB,KAAM,CAAA,OAAA,CAAQ,MAAO,CAAA;AAAA,KACxC;AAAA,IACA,cAAgB,EAAA,MAAA;AAAA,IAChB,KAAO,EAAA,SAAA;AAAA,IACP,KAAO,EAAA,MAAA;AAAA,IACP,eAAiB,EAAA;AAAA,MACf,UAAA,EAAY,KAAM,CAAA,OAAA,CAAQ,CAAC;AAAA;AAC7B;AAEJ,CAAE,CAAA;;;;"}
@@ -1,68 +1,48 @@
1
1
  import { useAnalytics } from '@backstage/core-plugin-api';
2
2
  import { useState, useEffect } from 'react';
3
3
  import { useSearchParams } from 'react-router-dom';
4
- import { filterKeys } from '../components/FilterPanel/FilterPanel.esm.js';
5
4
  import useDebounce from 'react-use/lib/useDebounce';
6
- import { getFiltersWithDateRange } from '../utils/utils.esm.js';
7
- import { filterTags } from '@drodil/backstage-plugin-qeta-common';
8
5
  import { useQetaApi } from './useQetaApi.esm.js';
6
+ import { filterKeys } from '../components/FilterPanel/FilterPanel.esm.js';
7
+ import { filterTags } from '@drodil/backstage-plugin-qeta-common';
8
+ import { useUserSettings } from './useUserSettings.esm.js';
9
9
 
10
- const EXPANDED_LOCAL_STORAGE_KEY = "qeta-post-filters-expanded";
11
- function usePaginatedPosts(props) {
10
+ function useQetaEntities(props) {
12
11
  const {
13
- type,
14
- tags,
15
- author,
16
- entities,
17
- entity,
18
- favorite,
19
- initialPageSize,
20
- status
12
+ fetch,
13
+ initialFilters,
14
+ prefix,
15
+ defaultPageSize,
16
+ filterKeys: filterKeys$1,
17
+ fetchDeps,
18
+ getKey,
19
+ usePagination
21
20
  } = props;
22
21
  const analytics = useAnalytics();
23
22
  const [page, setPage] = useState(1);
24
- const pageCount = 1;
25
- const [postsPerPage, setPostsPerPage] = useState(initialPageSize ?? 25);
23
+ const [pageSize, setPageSize] = useState(defaultPageSize ?? 24);
24
+ const { getSetting, setSetting } = useUserSettings();
26
25
  const [showFilterPanel, setShowFilterPanel] = useState(
27
- localStorage.getItem(EXPANDED_LOCAL_STORAGE_KEY) === "true"
26
+ getSetting("filterPanelExpanded")[prefix] ?? false
28
27
  );
29
28
  const [searchParams, setSearchParams] = useSearchParams();
30
29
  const [searchQuery, setSearchQuery] = useState("");
31
- const [filters, setFilters] = useState({
32
- order: props.order ?? "desc",
33
- orderBy: props.orderBy ?? "created",
34
- noAnswers: props.noAnswers ?? "false",
35
- noCorrectAnswer: props.noCorrectAnswer ?? "false",
36
- noVotes: props.noVotes ?? "false",
37
- searchQuery: props.searchQuery ?? "",
38
- entities: entities ?? (entity ? [entity] : void 0),
39
- tags,
40
- dateRange: "",
41
- collectionId: props.collectionId,
42
- status,
43
- type
44
- });
45
- const [posts, setPosts] = useState([]);
30
+ const [filters, setFilters] = useState(initialFilters);
31
+ const [items, setItems] = useState([]);
46
32
  const [hasMore, setHasMore] = useState(true);
47
33
  const [total, setTotal] = useState(0);
48
34
  useEffect(() => {
49
- localStorage.setItem(
50
- EXPANDED_LOCAL_STORAGE_KEY,
51
- showFilterPanel ? "true" : "false"
52
- );
53
- }, [showFilterPanel]);
35
+ if (defaultPageSize) {
36
+ setPageSize(defaultPageSize);
37
+ }
38
+ }, [defaultPageSize]);
54
39
  useEffect(() => {
55
- setFilters((prev) => ({
56
- ...prev,
57
- tags,
58
- entities: entities ?? (entity ? [entity] : void 0),
59
- type,
60
- status,
61
- collectionId: props.collectionId
62
- }));
63
- setPage(1);
64
- setPosts([]);
65
- }, [tags, entities, entity, type, status, props.collectionId]);
40
+ const currentExpanded = getSetting("filterPanelExpanded");
41
+ setSetting("filterPanelExpanded", {
42
+ ...currentExpanded,
43
+ [prefix]: showFilterPanel
44
+ });
45
+ }, [showFilterPanel, prefix, getSetting, setSetting]);
66
46
  const onPageChange = (value) => {
67
47
  setPage(value);
68
48
  setSearchParams((prev) => {
@@ -77,7 +57,6 @@ function usePaginatedPosts(props) {
77
57
  const onFilterChange = (changes) => {
78
58
  const changesArray = Array.isArray(changes) ? changes : [changes];
79
59
  setPage(1);
80
- setPosts([]);
81
60
  setFilters((prev) => {
82
61
  const newValue = { ...prev };
83
62
  for (const { key, value } of changesArray) {
@@ -88,7 +67,8 @@ function usePaginatedPosts(props) {
88
67
  setSearchParams((prev) => {
89
68
  const newValue = prev;
90
69
  for (const { key, value } of changesArray) {
91
- if (!filterKeys.includes(key)) {
70
+ const allowedKeys = filterKeys$1 ?? filterKeys;
71
+ if (!allowedKeys.includes(key)) {
92
72
  continue;
93
73
  }
94
74
  if (!value || value === "false") {
@@ -112,17 +92,14 @@ function usePaginatedPosts(props) {
112
92
  };
113
93
  const onSearchQueryChange = (query) => {
114
94
  onPageChange(1);
115
- setPosts([]);
116
95
  if (query) {
117
- analytics.captureEvent("qeta_search", query);
96
+ analytics.captureEvent(`qeta_search_${prefix}`, query);
118
97
  }
119
98
  setSearchQuery(query);
120
99
  };
121
100
  useDebounce(
122
101
  () => {
123
102
  if (filters.searchQuery !== searchQuery) {
124
- setPage(1);
125
- setPosts([]);
126
103
  setFilters({ ...filters, searchQuery });
127
104
  }
128
105
  },
@@ -139,12 +116,12 @@ function usePaginatedPosts(props) {
139
116
  setPage(pv);
140
117
  } else {
141
118
  setPage(1);
142
- setPosts([]);
119
+ setItems([]);
143
120
  }
144
- } else if (key === "postsPerPage") {
121
+ } else if (key === `${prefix}PerPage`) {
145
122
  const qpp = Number.parseInt(value, 10);
146
- if (qpp > 0) setPostsPerPage(qpp);
147
- } else if (filterKeys.includes(key)) {
123
+ if (qpp > 0) setPageSize(qpp);
124
+ } else if ((filterKeys$1 ?? filterKeys).includes(key)) {
148
125
  filtersApplied = true;
149
126
  if (key === "tags") {
150
127
  filters.tags = filterTags(value.split(",")) ?? [];
@@ -161,7 +138,7 @@ function usePaginatedPosts(props) {
161
138
  if (filtersApplied) {
162
139
  setShowFilterPanel(true);
163
140
  }
164
- }, [searchParams, filters]);
141
+ }, [searchParams, filterKeys$1, prefix]);
165
142
  const {
166
143
  value: response,
167
144
  loading,
@@ -169,34 +146,31 @@ function usePaginatedPosts(props) {
169
146
  retry
170
147
  } = useQetaApi(
171
148
  (api) => {
172
- return api.getPosts({
173
- type,
174
- limit: postsPerPage,
175
- offset: (page - 1) * postsPerPage,
176
- includeEntities: true,
177
- includeAnswers: false,
178
- includeComments: false,
179
- includeAttachments: false,
180
- includeExperts: false,
181
- author,
182
- favorite,
183
- status,
184
- ...getFiltersWithDateRange(filters)
185
- });
149
+ return fetch(api, pageSize, (page - 1) * pageSize, filters);
186
150
  },
187
- [type, page, filters, postsPerPage]
151
+ [page, filters, pageSize, ...fetchDeps ?? []]
188
152
  );
189
153
  useEffect(() => {
190
154
  if (response) {
191
- if (page === 1) {
192
- setPosts(response.posts);
155
+ if (page === 1 || usePagination) {
156
+ setItems(response.items);
193
157
  } else {
194
- setPosts((prev) => [...prev, ...response.posts]);
158
+ setItems((prev) => {
159
+ const newItems = response.items.filter(
160
+ (newItem) => !prev.some((prevItem) => {
161
+ if (getKey) {
162
+ return getKey(prevItem) === getKey(newItem);
163
+ }
164
+ return prevItem.id === newItem.id;
165
+ })
166
+ );
167
+ return [...prev, ...newItems];
168
+ });
195
169
  }
196
- setHasMore((response.posts ?? []).length >= postsPerPage);
170
+ setHasMore((response.items ?? []).length >= pageSize);
197
171
  setTotal(response.total);
198
172
  }
199
- }, [response, page, postsPerPage]);
173
+ }, [response, page, pageSize, getKey, usePagination]);
200
174
  const onPageSizeChange = (value) => {
201
175
  if (response) {
202
176
  let newPage = page;
@@ -205,18 +179,18 @@ function usePaginatedPosts(props) {
205
179
  }
206
180
  onPageChange(Math.max(1, newPage));
207
181
  }
208
- setPostsPerPage(value);
182
+ setPageSize(value);
209
183
  setSearchParams((prev) => {
210
184
  const newValue = prev;
211
- newValue.set("postsPerPage", String(value));
185
+ newValue.set(`${prefix}PerPage`, String(value));
212
186
  return newValue;
213
187
  });
214
188
  };
215
189
  return {
216
190
  page,
217
191
  setPage,
218
- postsPerPage,
219
- setPostsPerPage,
192
+ pageSize,
193
+ setPageSize,
220
194
  showFilterPanel,
221
195
  setShowFilterPanel,
222
196
  searchParams,
@@ -230,17 +204,16 @@ function usePaginatedPosts(props) {
230
204
  onFilterChange,
231
205
  onSearchQueryChange,
232
206
  response,
233
- posts: page === 1 && !loading && response ? response.posts : posts,
207
+ items: (page === 1 || usePagination) && !loading && response ? response.items : items,
234
208
  hasMore,
235
209
  total,
236
210
  loading,
237
211
  error,
238
212
  loadNextPage,
239
- pageCount,
240
213
  retry,
241
214
  fetchNextPage: loadNextPage
242
215
  };
243
216
  }
244
217
 
245
- export { usePaginatedPosts };
246
- //# sourceMappingURL=usePaginatedPosts.esm.js.map
218
+ export { useQetaEntities };
219
+ //# sourceMappingURL=useQetaEntities.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useQetaEntities.esm.js","sources":["../../src/hooks/useQetaEntities.ts"],"sourcesContent":["import { useAnalytics } from '@backstage/core-plugin-api';\nimport { useEffect, useState } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport useDebounce from 'react-use/lib/useDebounce';\nimport { useQetaApi } from './useQetaApi';\nimport { QetaApi } from '@drodil/backstage-plugin-qeta-common';\nimport { filterKeys as globalFilterKeys } from '../components/FilterPanel/FilterPanel';\nimport { filterTags } from '@drodil/backstage-plugin-qeta-common';\nimport { useUserSettings } from './useUserSettings';\n\nexport type QetaEntitiesProps<T, F> = {\n fetch: (\n api: QetaApi,\n limit: number,\n offset: number,\n filters: F,\n ) => Promise<{ items: T[]; total: number }>;\n initialFilters: F;\n prefix: string;\n defaultPageSize?: number;\n filterKeys?: string[];\n fetchDeps?: any[];\n getKey?: (item: T) => string | number;\n usePagination?: boolean;\n};\n\nexport type FilterChange<F> = {\n key: keyof F;\n value?: F[keyof F] | string | string[];\n};\n\nexport function useQetaEntities<T, F>(props: QetaEntitiesProps<T, F>) {\n const {\n fetch,\n initialFilters,\n prefix,\n defaultPageSize,\n filterKeys,\n fetchDeps,\n getKey,\n usePagination,\n } = props;\n const analytics = useAnalytics();\n const [page, setPage] = useState(1);\n const [pageSize, setPageSize] = useState(defaultPageSize ?? 24);\n const { getSetting, setSetting } = useUserSettings();\n const [showFilterPanel, setShowFilterPanel] = useState(\n getSetting('filterPanelExpanded')[prefix] ?? false,\n );\n const [searchParams, setSearchParams] = useSearchParams();\n const [searchQuery, setSearchQuery] = useState('');\n const [filters, setFilters] = useState<F>(initialFilters);\n\n const [items, setItems] = useState<T[]>([]);\n const [hasMore, setHasMore] = useState(true);\n const [total, setTotal] = useState(0);\n\n useEffect(() => {\n if (defaultPageSize) {\n setPageSize(defaultPageSize);\n }\n }, [defaultPageSize]);\n\n useEffect(() => {\n const currentExpanded = getSetting('filterPanelExpanded');\n setSetting('filterPanelExpanded', {\n ...currentExpanded,\n [prefix]: showFilterPanel,\n });\n }, [showFilterPanel, prefix, getSetting, setSetting]);\n\n const onPageChange = (value: number) => {\n setPage(value);\n setSearchParams(prev => {\n const newValue = prev;\n newValue.set('page', String(value));\n return newValue;\n });\n };\n\n const loadNextPage = () => {\n setPage(prev => prev + 1);\n };\n\n const onFilterChange = (changes: FilterChange<F> | FilterChange<F>[]) => {\n const changesArray = Array.isArray(changes) ? changes : [changes];\n setPage(1);\n setFilters(prev => {\n const newValue = { ...prev };\n for (const { key, value } of changesArray) {\n (newValue as any)[key] = value;\n }\n return newValue;\n });\n setSearchParams(prev => {\n const newValue = prev;\n for (const { key, value } of changesArray) {\n const allowedKeys = filterKeys ?? globalFilterKeys;\n if (!allowedKeys.includes(key as any)) {\n continue;\n }\n if (!value || value === 'false') {\n newValue.delete(key as string);\n } else if (Array.isArray(value)) {\n if (value.length === 0) {\n newValue.delete(key as string);\n } else {\n newValue.set(key as string, value.join(','));\n }\n } else if (typeof value === 'number') {\n newValue.set(key as string, String(value));\n } else if ((value as any).length > 0) {\n newValue.set(key as string, value as any);\n } else {\n newValue.delete(key as string);\n }\n }\n return newValue;\n });\n };\n\n const onSearchQueryChange = (query: string) => {\n onPageChange(1);\n if (query) {\n analytics.captureEvent(`qeta_search_${prefix}`, query);\n }\n setSearchQuery(query);\n };\n\n useDebounce(\n () => {\n if ((filters as any).searchQuery !== searchQuery) {\n setFilters({ ...filters, searchQuery: searchQuery });\n }\n },\n 400,\n [searchQuery],\n );\n\n useEffect(() => {\n let filtersApplied = false;\n searchParams.forEach((value, key) => {\n try {\n if (key === 'page') {\n const pv = Number.parseInt(value, 10);\n if (pv > 0) {\n setPage(pv);\n } else {\n setPage(1);\n setItems([]);\n }\n } else if (key === `${prefix}PerPage`) {\n const qpp = Number.parseInt(value, 10);\n if (qpp > 0) setPageSize(qpp);\n } else if ((filterKeys ?? globalFilterKeys).includes(key as any)) {\n filtersApplied = true;\n if (key === 'tags') {\n (filters as any).tags = filterTags(value.split(',')) ?? [];\n } else if (key === 'entities') {\n (filters as any).entities = value.split(',');\n } else {\n (filters as any)[key] = value;\n }\n }\n } catch (_e) {\n // NOOP\n }\n });\n setFilters(filters);\n if (filtersApplied) {\n setShowFilterPanel(true);\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [searchParams, filterKeys, prefix]);\n\n const {\n value: response,\n loading,\n error,\n retry,\n } = useQetaApi(\n api => {\n return fetch(api, pageSize, (page - 1) * pageSize, filters);\n },\n [page, filters, pageSize, ...(fetchDeps ?? [])],\n );\n\n useEffect(() => {\n if (response) {\n if (page === 1 || usePagination) {\n setItems(response.items);\n } else {\n setItems(prev => {\n const newItems = response.items.filter(\n newItem =>\n !prev.some(prevItem => {\n if (getKey) {\n return getKey(prevItem) === getKey(newItem);\n }\n return (prevItem as any).id === (newItem as any).id;\n }),\n );\n return [...prev, ...newItems];\n });\n }\n setHasMore((response.items ?? []).length >= pageSize);\n setTotal(response.total);\n }\n }, [response, page, pageSize, getKey, usePagination]);\n\n const onPageSizeChange = (value: number) => {\n if (response) {\n let newPage = page;\n while (newPage * value > response.total) {\n newPage -= 1;\n }\n onPageChange(Math.max(1, newPage));\n }\n setPageSize(value);\n setSearchParams(prev => {\n const newValue = prev;\n newValue.set(`${prefix}PerPage`, String(value));\n return newValue;\n });\n };\n\n return {\n page,\n setPage,\n pageSize,\n setPageSize,\n showFilterPanel,\n setShowFilterPanel,\n searchParams,\n setSearchParams,\n searchQuery,\n setSearchQuery,\n filters,\n setFilters,\n onPageChange,\n onPageSizeChange,\n onFilterChange,\n onSearchQueryChange,\n response,\n items:\n (page === 1 || usePagination) && !loading && response\n ? response.items\n : items,\n hasMore,\n total,\n loading,\n error,\n loadNextPage,\n retry,\n fetchNextPage: loadNextPage,\n };\n}\n"],"names":["filterKeys","globalFilterKeys"],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import { useApi, storageApiRef } from '@backstage/core-plugin-api';
2
+ import { useState, useRef, useEffect, useCallback } from 'react';
3
+
4
+ const DEFAULT_SETTINGS = {
5
+ autoSaveEnabled: false,
6
+ filterPanelExpanded: {},
7
+ viewType: {},
8
+ aiAnswerExpanded: false,
9
+ usePagination: false
10
+ };
11
+ const BUCKET_KEY = "qeta";
12
+ const STORAGE_KEY = "qeta-user-settings";
13
+ const useUserSettings = () => {
14
+ const storageApi = useApi(storageApiRef);
15
+ const [settings, setSettings] = useState(DEFAULT_SETTINGS);
16
+ const [isLoaded, setIsLoaded] = useState(false);
17
+ const settingsRef = useRef(DEFAULT_SETTINGS);
18
+ useEffect(() => {
19
+ settingsRef.current = settings;
20
+ }, [settings]);
21
+ useEffect(() => {
22
+ const bucket = storageApi.forBucket(BUCKET_KEY);
23
+ const snapshot = bucket.snapshot(STORAGE_KEY);
24
+ const stored = snapshot.value;
25
+ if (stored) {
26
+ setSettings(stored);
27
+ }
28
+ setIsLoaded(true);
29
+ const subscription = bucket.observe$(STORAGE_KEY).subscribe({
30
+ next: (newSnapshot) => {
31
+ const value = newSnapshot.value;
32
+ if (value) {
33
+ setSettings(value);
34
+ } else {
35
+ setSettings(DEFAULT_SETTINGS);
36
+ }
37
+ }
38
+ });
39
+ return () => {
40
+ subscription.unsubscribe();
41
+ };
42
+ }, [storageApi]);
43
+ const updateSettings = useCallback(
44
+ async (updates) => {
45
+ const bucket = storageApi.forBucket(BUCKET_KEY);
46
+ const snapshot = bucket.snapshot(STORAGE_KEY);
47
+ const currentSettings = snapshot.value || DEFAULT_SETTINGS;
48
+ const newSettings = {
49
+ ...currentSettings,
50
+ ...updates
51
+ };
52
+ await bucket.set(STORAGE_KEY, newSettings);
53
+ },
54
+ [storageApi]
55
+ );
56
+ const getSetting = useCallback(
57
+ (key) => {
58
+ return settingsRef.current[key];
59
+ },
60
+ []
61
+ );
62
+ const setSetting = useCallback(
63
+ async (key, value) => {
64
+ await updateSettings({ [key]: value });
65
+ },
66
+ [updateSettings]
67
+ );
68
+ const resetSettings = useCallback(async () => {
69
+ const bucket = storageApi.forBucket("qeta");
70
+ await bucket.set(STORAGE_KEY, DEFAULT_SETTINGS);
71
+ }, [storageApi]);
72
+ return {
73
+ settings,
74
+ updateSettings,
75
+ getSetting,
76
+ setSetting,
77
+ resetSettings,
78
+ isLoaded
79
+ };
80
+ };
81
+
82
+ export { useUserSettings };
83
+ //# sourceMappingURL=useUserSettings.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useUserSettings.esm.js","sources":["../../src/hooks/useUserSettings.ts"],"sourcesContent":["import { useApi } from '@backstage/core-plugin-api';\nimport { storageApiRef } from '@backstage/core-plugin-api';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { ViewType } from '../components/ViewToggle/ViewToggle';\n\nexport type UserSettings = {\n autoSaveEnabled: boolean;\n filterPanelExpanded: Record<string, boolean>;\n viewType: Record<string, ViewType>;\n aiAnswerExpanded: boolean;\n usePagination: boolean;\n};\n\nconst DEFAULT_SETTINGS: UserSettings = {\n autoSaveEnabled: false,\n filterPanelExpanded: {},\n viewType: {},\n aiAnswerExpanded: false,\n usePagination: false,\n};\n\nconst BUCKET_KEY = 'qeta';\nconst STORAGE_KEY = 'qeta-user-settings';\n\nexport const useUserSettings = () => {\n const storageApi = useApi(storageApiRef);\n const [settings, setSettings] = useState<UserSettings>(DEFAULT_SETTINGS);\n const [isLoaded, setIsLoaded] = useState(false);\n const settingsRef = useRef<UserSettings>(DEFAULT_SETTINGS);\n\n useEffect(() => {\n settingsRef.current = settings;\n }, [settings]);\n\n useEffect(() => {\n const bucket = storageApi.forBucket(BUCKET_KEY);\n const snapshot = bucket.snapshot(STORAGE_KEY);\n const stored = snapshot.value as UserSettings | undefined;\n\n if (stored) {\n setSettings(stored);\n }\n\n setIsLoaded(true);\n\n const subscription = bucket.observe$<UserSettings>(STORAGE_KEY).subscribe({\n next: newSnapshot => {\n const value = newSnapshot.value as UserSettings | undefined;\n if (value) {\n setSettings(value);\n } else {\n setSettings(DEFAULT_SETTINGS);\n }\n },\n });\n\n return () => {\n subscription.unsubscribe();\n };\n }, [storageApi]);\n\n const updateSettings = useCallback(\n async (updates: Partial<UserSettings>) => {\n const bucket = storageApi.forBucket(BUCKET_KEY);\n const snapshot = bucket.snapshot(STORAGE_KEY);\n const currentSettings =\n (snapshot.value as UserSettings) || DEFAULT_SETTINGS;\n const newSettings = {\n ...currentSettings,\n ...(updates as Partial<UserSettings>),\n };\n await bucket.set(STORAGE_KEY, newSettings);\n },\n [storageApi],\n );\n\n const getSetting = useCallback(\n <K extends keyof UserSettings>(key: K): UserSettings[K] => {\n return settingsRef.current[key];\n },\n [],\n );\n\n const setSetting = useCallback(\n async <K extends keyof UserSettings>(\n key: K,\n value: UserSettings[K],\n ): Promise<void> => {\n await updateSettings({ [key]: value } as Partial<UserSettings>);\n },\n [updateSettings],\n );\n\n const resetSettings = useCallback(async () => {\n const bucket = storageApi.forBucket('qeta');\n await bucket.set(STORAGE_KEY, DEFAULT_SETTINGS);\n }, [storageApi]);\n\n return {\n settings,\n updateSettings,\n getSetting,\n setSetting,\n resetSettings,\n isLoaded,\n };\n};\n"],"names":[],"mappings":"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"}