@drodil/backstage-plugin-qeta-react 3.56.0 → 3.57.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 (63) hide show
  1. package/dist/components/CollectionsContainer/CollectionsContainer.esm.js +2 -1
  2. package/dist/components/CollectionsContainer/CollectionsContainer.esm.js.map +1 -1
  3. package/dist/components/FaviconItem/FaviconItem.esm.js +20 -13
  4. package/dist/components/FaviconItem/FaviconItem.esm.js.map +1 -1
  5. package/dist/components/FollowedLists/FollowedCollectionsList.esm.js +60 -18
  6. package/dist/components/FollowedLists/FollowedCollectionsList.esm.js.map +1 -1
  7. package/dist/components/FollowedLists/FollowedEntitiesList.esm.js +46 -18
  8. package/dist/components/FollowedLists/FollowedEntitiesList.esm.js.map +1 -1
  9. package/dist/components/FollowedLists/FollowedTagsList.esm.js +44 -18
  10. package/dist/components/FollowedLists/FollowedTagsList.esm.js.map +1 -1
  11. package/dist/components/FollowedLists/FollowedUsersList.esm.js +56 -26
  12. package/dist/components/FollowedLists/FollowedUsersList.esm.js.map +1 -1
  13. package/dist/components/Links/Links.esm.js +27 -18
  14. package/dist/components/Links/Links.esm.js.map +1 -1
  15. package/dist/components/MarkdownRenderer/MarkdownRenderer.esm.js +4 -2
  16. package/dist/components/MarkdownRenderer/MarkdownRenderer.esm.js.map +1 -1
  17. package/dist/components/PostHighlightList/PostHighlightList.esm.js +23 -144
  18. package/dist/components/PostHighlightList/PostHighlightList.esm.js.map +1 -1
  19. package/dist/components/PostsContainer/PostsGridItem.esm.js +6 -11
  20. package/dist/components/PostsContainer/PostsGridItem.esm.js.map +1 -1
  21. package/dist/components/TagsAndEntities/EntityChip.esm.js +42 -109
  22. package/dist/components/TagsAndEntities/EntityChip.esm.js.map +1 -1
  23. package/dist/components/TagsAndEntities/TagChip.esm.js +56 -118
  24. package/dist/components/TagsAndEntities/TagChip.esm.js.map +1 -1
  25. package/dist/components/TagsContainer/EditTagModal.esm.js.map +1 -1
  26. package/dist/components/TagsContainer/TagsContainer.esm.js +2 -1
  27. package/dist/components/TagsContainer/TagsContainer.esm.js.map +1 -1
  28. package/dist/components/Timeline/TimelineItem.esm.js +54 -2
  29. package/dist/components/Timeline/TimelineItem.esm.js.map +1 -1
  30. package/dist/components/Tooltips/CollectionTooltip.esm.js +151 -0
  31. package/dist/components/Tooltips/CollectionTooltip.esm.js.map +1 -0
  32. package/dist/components/Tooltips/EntityTooltip.esm.js +188 -0
  33. package/dist/components/Tooltips/EntityTooltip.esm.js.map +1 -0
  34. package/dist/components/Tooltips/PostTooltip.esm.js +257 -0
  35. package/dist/components/Tooltips/PostTooltip.esm.js.map +1 -0
  36. package/dist/components/Tooltips/TagTooltip.esm.js +199 -0
  37. package/dist/components/Tooltips/TagTooltip.esm.js.map +1 -0
  38. package/dist/components/Tooltips/UserTooltip.esm.js +259 -0
  39. package/dist/components/Tooltips/UserTooltip.esm.js.map +1 -0
  40. package/dist/components/UsersContainer/UserListItem.esm.js +1 -0
  41. package/dist/components/UsersContainer/UserListItem.esm.js.map +1 -1
  42. package/dist/components/UsersContainer/UsersGridItem.esm.js +1 -0
  43. package/dist/components/UsersContainer/UsersGridItem.esm.js.map +1 -1
  44. package/dist/hooks/useCollectionsFollow.esm.js +21 -45
  45. package/dist/hooks/useCollectionsFollow.esm.js.map +1 -1
  46. package/dist/hooks/useEntityAuthor.esm.js +3 -5
  47. package/dist/hooks/useEntityAuthor.esm.js.map +1 -1
  48. package/dist/hooks/useEntityFollow.esm.js +25 -36
  49. package/dist/hooks/useEntityFollow.esm.js.map +1 -1
  50. package/dist/hooks/useFavicon.esm.js +52 -0
  51. package/dist/hooks/useFavicon.esm.js.map +1 -0
  52. package/dist/hooks/useFollow.esm.js +62 -0
  53. package/dist/hooks/useFollow.esm.js.map +1 -0
  54. package/dist/hooks/useTagsFollow.esm.js +22 -39
  55. package/dist/hooks/useTagsFollow.esm.js.map +1 -1
  56. package/dist/hooks/useUserFollow.esm.js +22 -39
  57. package/dist/hooks/useUserFollow.esm.js.map +1 -1
  58. package/dist/index.d.ts +29 -13
  59. package/dist/index.esm.js +2 -0
  60. package/dist/index.esm.js.map +1 -1
  61. package/package.json +3 -3
  62. package/dist/components/TagsAndEntities/UserChip.esm.js +0 -87
  63. package/dist/components/TagsAndEntities/UserChip.esm.js.map +0 -1
@@ -1,46 +1,35 @@
1
- import { useState, useEffect, useCallback } from 'react';
1
+ import { useCallback } from 'react';
2
2
  import { useApi } from '@backstage/core-plugin-api';
3
3
  import { qetaApiRef } from '../api.esm.js';
4
+ import { useFollow } from './useFollow.esm.js';
4
5
 
5
- let followedEntities = void 0;
6
6
  const useEntityFollow = () => {
7
- const [entities, setEntities] = useState(followedEntities ?? []);
8
- const [loading, setLoading] = useState(followedEntities === void 0);
9
7
  const qetaApi = useApi(qetaApiRef);
10
- useEffect(() => {
11
- if (followedEntities === void 0) {
12
- qetaApi.getFollowedEntities().then((res) => {
13
- followedEntities = res.entityRefs;
14
- setEntities(res.entityRefs);
15
- setLoading(false);
16
- });
17
- } else {
18
- setEntities(followedEntities);
8
+ const { items, follow, unfollow, isFollowing, loading } = useFollow(
9
+ "entities",
10
+ {
11
+ fetchFollowed: useCallback(
12
+ () => qetaApi.getFollowedEntities().then((res) => res.entityRefs),
13
+ [qetaApi]
14
+ ),
15
+ followItem: useCallback(
16
+ (entityRef) => qetaApi.followEntity(entityRef),
17
+ [qetaApi]
18
+ ),
19
+ unfollowItem: useCallback(
20
+ (entityRef) => qetaApi.unfollowEntity(entityRef),
21
+ [qetaApi]
22
+ ),
23
+ isEqual: (a, b) => a === b
19
24
  }
20
- }, [qetaApi]);
21
- const followEntity = useCallback(
22
- (entityRef) => {
23
- qetaApi.followEntity(entityRef).then(() => {
24
- setEntities((prev) => [...prev, entityRef]);
25
- followedEntities?.push(entityRef);
26
- });
27
- },
28
- [qetaApi]
29
25
  );
30
- const unfollowEntity = useCallback(
31
- (entityRef) => {
32
- qetaApi.unfollowEntity(entityRef).then(() => {
33
- setEntities((prev) => prev.filter((t) => t !== entityRef));
34
- followedEntities = followedEntities?.filter((t) => t !== entityRef);
35
- });
36
- },
37
- [qetaApi]
38
- );
39
- const isFollowingEntity = useCallback(
40
- (entityRef) => entities.includes(entityRef),
41
- [entities]
42
- );
43
- return { entities, followEntity, unfollowEntity, isFollowingEntity, loading };
26
+ return {
27
+ entities: items,
28
+ followEntity: follow,
29
+ unfollowEntity: unfollow,
30
+ isFollowingEntity: isFollowing,
31
+ loading
32
+ };
44
33
  };
45
34
 
46
35
  export { useEntityFollow };
@@ -1 +1 @@
1
- {"version":3,"file":"useEntityFollow.esm.js","sources":["../../src/hooks/useEntityFollow.ts"],"sourcesContent":["import { useState, useCallback, useEffect } from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { qetaApiRef } from '../api';\n\nlet followedEntities: string[] | undefined = undefined;\n\nexport const useEntityFollow = () => {\n const [entities, setEntities] = useState<string[]>(followedEntities ?? []);\n const [loading, setLoading] = useState(followedEntities === undefined);\n const qetaApi = useApi(qetaApiRef);\n\n useEffect(() => {\n if (followedEntities === undefined) {\n qetaApi.getFollowedEntities().then(res => {\n followedEntities = res.entityRefs;\n setEntities(res.entityRefs);\n setLoading(false);\n });\n } else {\n setEntities(followedEntities);\n }\n }, [qetaApi]);\n\n const followEntity = useCallback(\n (entityRef: string) => {\n qetaApi.followEntity(entityRef).then(() => {\n setEntities(prev => [...prev, entityRef]);\n followedEntities?.push(entityRef);\n });\n },\n [qetaApi],\n );\n\n const unfollowEntity = useCallback(\n (entityRef: string) => {\n qetaApi.unfollowEntity(entityRef).then(() => {\n setEntities(prev => prev.filter(t => t !== entityRef));\n followedEntities = followedEntities?.filter(t => t !== entityRef);\n });\n },\n [qetaApi],\n );\n\n const isFollowingEntity = useCallback(\n (entityRef: string) => entities.includes(entityRef),\n [entities],\n );\n return { entities, followEntity, unfollowEntity, isFollowingEntity, loading };\n};\n"],"names":[],"mappings":";;;;AAIA,IAAI,gBAAyC,GAAA,KAAA,CAAA;AAEtC,MAAM,kBAAkB,MAAM;AACnC,EAAA,MAAM,CAAC,QAAU,EAAA,WAAW,IAAI,QAAmB,CAAA,gBAAA,IAAoB,EAAE,CAAA;AACzE,EAAA,MAAM,CAAC,OAAS,EAAA,UAAU,CAAI,GAAA,QAAA,CAAS,qBAAqB,KAAS,CAAA,CAAA;AACrE,EAAM,MAAA,OAAA,GAAU,OAAO,UAAU,CAAA;AAEjC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,qBAAqB,KAAW,CAAA,EAAA;AAClC,MAAQ,OAAA,CAAA,mBAAA,EAAsB,CAAA,IAAA,CAAK,CAAO,GAAA,KAAA;AACxC,QAAA,gBAAA,GAAmB,GAAI,CAAA,UAAA;AACvB,QAAA,WAAA,CAAY,IAAI,UAAU,CAAA;AAC1B,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA,OACjB,CAAA;AAAA,KACI,MAAA;AACL,MAAA,WAAA,CAAY,gBAAgB,CAAA;AAAA;AAC9B,GACF,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,MAAM,YAAe,GAAA,WAAA;AAAA,IACnB,CAAC,SAAsB,KAAA;AACrB,MAAA,OAAA,CAAQ,YAAa,CAAA,SAAS,CAAE,CAAA,IAAA,CAAK,MAAM;AACzC,QAAA,WAAA,CAAY,CAAQ,IAAA,KAAA,CAAC,GAAG,IAAA,EAAM,SAAS,CAAC,CAAA;AACxC,QAAA,gBAAA,EAAkB,KAAK,SAAS,CAAA;AAAA,OACjC,CAAA;AAAA,KACH;AAAA,IACA,CAAC,OAAO;AAAA,GACV;AAEA,EAAA,MAAM,cAAiB,GAAA,WAAA;AAAA,IACrB,CAAC,SAAsB,KAAA;AACrB,MAAA,OAAA,CAAQ,cAAe,CAAA,SAAS,CAAE,CAAA,IAAA,CAAK,MAAM;AAC3C,QAAA,WAAA,CAAY,UAAQ,IAAK,CAAA,MAAA,CAAO,CAAK,CAAA,KAAA,CAAA,KAAM,SAAS,CAAC,CAAA;AACrD,QAAA,gBAAA,GAAmB,gBAAkB,EAAA,MAAA,CAAO,CAAK,CAAA,KAAA,CAAA,KAAM,SAAS,CAAA;AAAA,OACjE,CAAA;AAAA,KACH;AAAA,IACA,CAAC,OAAO;AAAA,GACV;AAEA,EAAA,MAAM,iBAAoB,GAAA,WAAA;AAAA,IACxB,CAAC,SAAA,KAAsB,QAAS,CAAA,QAAA,CAAS,SAAS,CAAA;AAAA,IAClD,CAAC,QAAQ;AAAA,GACX;AACA,EAAA,OAAO,EAAE,QAAA,EAAU,YAAc,EAAA,cAAA,EAAgB,mBAAmB,OAAQ,EAAA;AAC9E;;;;"}
1
+ {"version":3,"file":"useEntityFollow.esm.js","sources":["../../src/hooks/useEntityFollow.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { qetaApiRef } from '../api';\nimport { useFollow } from './useFollow';\n\nexport const useEntityFollow = () => {\n const qetaApi = useApi(qetaApiRef);\n\n const { items, follow, unfollow, isFollowing, loading } = useFollow<string>(\n 'entities',\n {\n fetchFollowed: useCallback(\n () => qetaApi.getFollowedEntities().then(res => res.entityRefs),\n [qetaApi],\n ),\n followItem: useCallback(\n (entityRef: string) => qetaApi.followEntity(entityRef),\n [qetaApi],\n ),\n unfollowItem: useCallback(\n (entityRef: string) => qetaApi.unfollowEntity(entityRef),\n [qetaApi],\n ),\n isEqual: (a, b) => a === b,\n },\n );\n\n return {\n entities: items,\n followEntity: follow,\n unfollowEntity: unfollow,\n isFollowingEntity: isFollowing,\n loading,\n };\n};\n"],"names":[],"mappings":";;;;;AAKO,MAAM,kBAAkB,MAAM;AACnC,EAAM,MAAA,OAAA,GAAU,OAAO,UAAU,CAAA;AAEjC,EAAA,MAAM,EAAE,KAAO,EAAA,MAAA,EAAQ,QAAU,EAAA,WAAA,EAAa,SAAY,GAAA,SAAA;AAAA,IACxD,UAAA;AAAA,IACA;AAAA,MACE,aAAe,EAAA,WAAA;AAAA,QACb,MAAM,OAAQ,CAAA,mBAAA,GAAsB,IAAK,CAAA,CAAA,GAAA,KAAO,IAAI,UAAU,CAAA;AAAA,QAC9D,CAAC,OAAO;AAAA,OACV;AAAA,MACA,UAAY,EAAA,WAAA;AAAA,QACV,CAAC,SAAA,KAAsB,OAAQ,CAAA,YAAA,CAAa,SAAS,CAAA;AAAA,QACrD,CAAC,OAAO;AAAA,OACV;AAAA,MACA,YAAc,EAAA,WAAA;AAAA,QACZ,CAAC,SAAA,KAAsB,OAAQ,CAAA,cAAA,CAAe,SAAS,CAAA;AAAA,QACvD,CAAC,OAAO;AAAA,OACV;AAAA,MACA,OAAS,EAAA,CAAC,CAAG,EAAA,CAAA,KAAM,CAAM,KAAA;AAAA;AAC3B,GACF;AAEA,EAAO,OAAA;AAAA,IACL,QAAU,EAAA,KAAA;AAAA,IACV,YAAc,EAAA,MAAA;AAAA,IACd,cAAgB,EAAA,QAAA;AAAA,IAChB,iBAAmB,EAAA,WAAA;AAAA,IACnB;AAAA,GACF;AACF;;;;"}
@@ -0,0 +1,52 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { useApi } from '@backstage/core-plugin-api';
3
+ import { qetaApiRef } from '../api.esm.js';
4
+ import DataLoader from 'dataloader';
5
+
6
+ const loaderCache = /* @__PURE__ */ new WeakMap();
7
+ const getFaviconLoader = (qetaApi) => {
8
+ const cached = loaderCache.get(qetaApi);
9
+ if (cached) {
10
+ return cached;
11
+ }
12
+ const loader = new DataLoader(
13
+ async (urls) => {
14
+ try {
15
+ const response = await qetaApi.fetchBatchURLMetadata({
16
+ urls: [...urls]
17
+ });
18
+ return urls.map((url) => {
19
+ const metadata = response.metadata?.[url];
20
+ return metadata?.favicon;
21
+ });
22
+ } catch {
23
+ return urls.map(() => void 0);
24
+ }
25
+ },
26
+ {
27
+ cache: true,
28
+ batchScheduleFn: (callback) => setTimeout(callback, 10),
29
+ maxBatchSize: 50
30
+ }
31
+ );
32
+ loaderCache.set(qetaApi, loader);
33
+ return loader;
34
+ };
35
+ const useFavicon = (url) => {
36
+ const qetaApi = useApi(qetaApiRef);
37
+ const [favicon, setFavicon] = useState(void 0);
38
+ useEffect(() => {
39
+ if (!url) {
40
+ setFavicon(void 0);
41
+ return;
42
+ }
43
+ const loader = getFaviconLoader(qetaApi);
44
+ loader.load(url).then(setFavicon).catch(() => {
45
+ setFavicon(void 0);
46
+ });
47
+ }, [url, qetaApi]);
48
+ return favicon;
49
+ };
50
+
51
+ export { useFavicon };
52
+ //# sourceMappingURL=useFavicon.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFavicon.esm.js","sources":["../../src/hooks/useFavicon.ts"],"sourcesContent":["import { useEffect, useState } from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { qetaApiRef } from '../api';\nimport DataLoader from 'dataloader';\nimport { QetaApi } from '@drodil/backstage-plugin-qeta-common';\n\nconst loaderCache = new WeakMap<\n QetaApi,\n DataLoader<string, string | undefined>\n>();\n\nconst getFaviconLoader = (\n qetaApi: QetaApi,\n): DataLoader<string, string | undefined> => {\n const cached = loaderCache.get(qetaApi);\n if (cached) {\n return cached;\n }\n\n const loader = new DataLoader<string, string | undefined>(\n async (urls: readonly string[]) => {\n try {\n const response = await qetaApi.fetchBatchURLMetadata({\n urls: [...urls],\n });\n\n return urls.map(url => {\n const metadata = response.metadata?.[url];\n return metadata?.favicon;\n });\n } catch {\n return urls.map(() => undefined);\n }\n },\n {\n cache: true,\n batchScheduleFn: callback => setTimeout(callback, 10),\n maxBatchSize: 50,\n },\n );\n\n loaderCache.set(qetaApi, loader);\n return loader;\n};\n\nexport const useFavicon = (url?: string): string | undefined => {\n const qetaApi = useApi(qetaApiRef);\n const [favicon, setFavicon] = useState<string | undefined>(undefined);\n\n useEffect(() => {\n if (!url) {\n setFavicon(undefined);\n return;\n }\n\n const loader = getFaviconLoader(qetaApi);\n\n loader\n .load(url)\n .then(setFavicon)\n .catch(() => {\n setFavicon(undefined);\n });\n }, [url, qetaApi]);\n\n return favicon;\n};\n"],"names":[],"mappings":";;;;;AAMA,MAAM,WAAA,uBAAkB,OAGtB,EAAA;AAEF,MAAM,gBAAA,GAAmB,CACvB,OAC2C,KAAA;AAC3C,EAAM,MAAA,MAAA,GAAS,WAAY,CAAA,GAAA,CAAI,OAAO,CAAA;AACtC,EAAA,IAAI,MAAQ,EAAA;AACV,IAAO,OAAA,MAAA;AAAA;AAGT,EAAA,MAAM,SAAS,IAAI,UAAA;AAAA,IACjB,OAAO,IAA4B,KAAA;AACjC,MAAI,IAAA;AACF,QAAM,MAAA,QAAA,GAAW,MAAM,OAAA,CAAQ,qBAAsB,CAAA;AAAA,UACnD,IAAA,EAAM,CAAC,GAAG,IAAI;AAAA,SACf,CAAA;AAED,QAAO,OAAA,IAAA,CAAK,IAAI,CAAO,GAAA,KAAA;AACrB,UAAM,MAAA,QAAA,GAAW,QAAS,CAAA,QAAA,GAAW,GAAG,CAAA;AACxC,UAAA,OAAO,QAAU,EAAA,OAAA;AAAA,SAClB,CAAA;AAAA,OACK,CAAA,MAAA;AACN,QAAO,OAAA,IAAA,CAAK,GAAI,CAAA,MAAM,KAAS,CAAA,CAAA;AAAA;AACjC,KACF;AAAA,IACA;AAAA,MACE,KAAO,EAAA,IAAA;AAAA,MACP,eAAiB,EAAA,CAAA,QAAA,KAAY,UAAW,CAAA,QAAA,EAAU,EAAE,CAAA;AAAA,MACpD,YAAc,EAAA;AAAA;AAChB,GACF;AAEA,EAAY,WAAA,CAAA,GAAA,CAAI,SAAS,MAAM,CAAA;AAC/B,EAAO,OAAA,MAAA;AACT,CAAA;AAEa,MAAA,UAAA,GAAa,CAAC,GAAqC,KAAA;AAC9D,EAAM,MAAA,OAAA,GAAU,OAAO,UAAU,CAAA;AACjC,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAA6B,KAAS,CAAA,CAAA;AAEpE,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,GAAK,EAAA;AACR,MAAA,UAAA,CAAW,KAAS,CAAA,CAAA;AACpB,MAAA;AAAA;AAGF,IAAM,MAAA,MAAA,GAAS,iBAAiB,OAAO,CAAA;AAEvC,IAAA,MAAA,CACG,KAAK,GAAG,CAAA,CACR,KAAK,UAAU,CAAA,CACf,MAAM,MAAM;AACX,MAAA,UAAA,CAAW,KAAS,CAAA,CAAA;AAAA,KACrB,CAAA;AAAA,GACF,EAAA,CAAC,GAAK,EAAA,OAAO,CAAC,CAAA;AAEjB,EAAO,OAAA,OAAA;AACT;;;;"}
@@ -0,0 +1,62 @@
1
+ import { useState, useEffect, useCallback } from 'react';
2
+ import { useApi } from '@backstage/core-plugin-api';
3
+ import { qetaApiRef } from '../api.esm.js';
4
+
5
+ const caches = /* @__PURE__ */ new Map();
6
+ const useFollow = (key, options) => {
7
+ const { fetchFollowed, followItem, unfollowItem, isEqual } = options;
8
+ if (!caches.has(key)) {
9
+ caches.set(key, { data: void 0, promise: void 0 });
10
+ }
11
+ const cache = caches.get(key);
12
+ const [items, setItems] = useState(cache.data ?? []);
13
+ const [loading, setLoading] = useState(cache.data === void 0);
14
+ const qetaApi = useApi(qetaApiRef);
15
+ useEffect(() => {
16
+ if (cache.data === void 0) {
17
+ cache.promise ??= fetchFollowed().then((result) => {
18
+ cache.data = result;
19
+ cache.promise = void 0;
20
+ return result;
21
+ });
22
+ cache.promise.then((result) => {
23
+ setItems(result);
24
+ setLoading(false);
25
+ });
26
+ } else {
27
+ setItems(cache.data);
28
+ }
29
+ }, [qetaApi, fetchFollowed, cache]);
30
+ const follow = useCallback(
31
+ (item) => {
32
+ followItem(item).then(() => {
33
+ setItems((prev) => [...prev, item]);
34
+ cache.data?.push(item);
35
+ });
36
+ },
37
+ [followItem, cache]
38
+ );
39
+ const unfollow = useCallback(
40
+ (item) => {
41
+ unfollowItem(item).then(() => {
42
+ setItems((prev) => prev.filter((i) => !isEqual(i, item)));
43
+ cache.data = cache.data?.filter((i) => !isEqual(i, item));
44
+ });
45
+ },
46
+ [unfollowItem, isEqual, cache]
47
+ );
48
+ const isFollowing = useCallback(
49
+ (item) => items.some((i) => isEqual(i, item)),
50
+ [items, isEqual]
51
+ );
52
+ return {
53
+ items,
54
+ follow,
55
+ unfollow,
56
+ isFollowing,
57
+ loading
58
+ };
59
+ };
60
+
61
+ export { useFollow };
62
+ //# sourceMappingURL=useFollow.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useFollow.esm.js","sources":["../../src/hooks/useFollow.ts"],"sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { qetaApiRef } from '../api';\n\ninterface UseFollowOptions<T> {\n fetchFollowed: () => Promise<T[]>;\n followItem: (item: T) => Promise<unknown>;\n unfollowItem: (item: T) => Promise<unknown>;\n isEqual: (a: T, b: T) => boolean;\n}\n\ninterface UseFollowCache<T> {\n data: T[] | undefined;\n promise: Promise<T[]> | undefined;\n}\n\nconst caches = new Map<string, UseFollowCache<any>>();\n\nexport const useFollow = <T>(key: string, options: UseFollowOptions<T>) => {\n const { fetchFollowed, followItem, unfollowItem, isEqual } = options;\n\n // Get or create cache for this key\n if (!caches.has(key)) {\n caches.set(key, { data: undefined, promise: undefined });\n }\n const cache = caches.get(key) as UseFollowCache<T>;\n\n const [items, setItems] = useState<T[]>(cache.data ?? []);\n const [loading, setLoading] = useState(cache.data === undefined);\n const qetaApi = useApi(qetaApiRef);\n\n useEffect(() => {\n if (cache.data === undefined) {\n cache.promise ??= fetchFollowed().then(result => {\n cache.data = result;\n cache.promise = undefined;\n return result;\n });\n cache.promise.then(result => {\n setItems(result);\n setLoading(false);\n });\n } else {\n setItems(cache.data);\n }\n }, [qetaApi, fetchFollowed, cache]);\n\n const follow = useCallback(\n (item: T) => {\n followItem(item).then(() => {\n setItems(prev => [...prev, item]);\n cache.data?.push(item);\n });\n },\n [followItem, cache],\n );\n\n const unfollow = useCallback(\n (item: T) => {\n unfollowItem(item).then(() => {\n setItems(prev => prev.filter(i => !isEqual(i, item)));\n cache.data = cache.data?.filter(i => !isEqual(i, item));\n });\n },\n [unfollowItem, isEqual, cache],\n );\n\n const isFollowing = useCallback(\n (item: T) => items.some(i => isEqual(i, item)),\n [items, isEqual],\n );\n\n return {\n items,\n follow,\n unfollow,\n isFollowing,\n loading,\n };\n};\n"],"names":[],"mappings":";;;;AAgBA,MAAM,MAAA,uBAAa,GAAiC,EAAA;AAEvC,MAAA,SAAA,GAAY,CAAI,GAAA,EAAa,OAAiC,KAAA;AACzE,EAAA,MAAM,EAAE,aAAA,EAAe,UAAY,EAAA,YAAA,EAAc,SAAY,GAAA,OAAA;AAG7D,EAAA,IAAI,CAAC,MAAA,CAAO,GAAI,CAAA,GAAG,CAAG,EAAA;AACpB,IAAA,MAAA,CAAO,IAAI,GAAK,EAAA,EAAE,MAAM,KAAW,CAAA,EAAA,OAAA,EAAS,QAAW,CAAA;AAAA;AAEzD,EAAM,MAAA,KAAA,GAAQ,MAAO,CAAA,GAAA,CAAI,GAAG,CAAA;AAE5B,EAAM,MAAA,CAAC,OAAO,QAAQ,CAAA,GAAI,SAAc,KAAM,CAAA,IAAA,IAAQ,EAAE,CAAA;AACxD,EAAA,MAAM,CAAC,OAAS,EAAA,UAAU,IAAI,QAAS,CAAA,KAAA,CAAM,SAAS,KAAS,CAAA,CAAA;AAC/D,EAAM,MAAA,OAAA,GAAU,OAAO,UAAU,CAAA;AAEjC,EAAA,SAAA,CAAU,MAAM;AACd,IAAI,IAAA,KAAA,CAAM,SAAS,KAAW,CAAA,EAAA;AAC5B,MAAA,KAAA,CAAM,OAAY,KAAA,aAAA,EAAgB,CAAA,IAAA,CAAK,CAAU,MAAA,KAAA;AAC/C,QAAA,KAAA,CAAM,IAAO,GAAA,MAAA;AACb,QAAA,KAAA,CAAM,OAAU,GAAA,KAAA,CAAA;AAChB,QAAO,OAAA,MAAA;AAAA,OACR,CAAA;AACD,MAAM,KAAA,CAAA,OAAA,CAAQ,KAAK,CAAU,MAAA,KAAA;AAC3B,QAAA,QAAA,CAAS,MAAM,CAAA;AACf,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA,OACjB,CAAA;AAAA,KACI,MAAA;AACL,MAAA,QAAA,CAAS,MAAM,IAAI,CAAA;AAAA;AACrB,GACC,EAAA,CAAC,OAAS,EAAA,aAAA,EAAe,KAAK,CAAC,CAAA;AAElC,EAAA,MAAM,MAAS,GAAA,WAAA;AAAA,IACb,CAAC,IAAY,KAAA;AACX,MAAW,UAAA,CAAA,IAAI,CAAE,CAAA,IAAA,CAAK,MAAM;AAC1B,QAAA,QAAA,CAAS,CAAQ,IAAA,KAAA,CAAC,GAAG,IAAA,EAAM,IAAI,CAAC,CAAA;AAChC,QAAM,KAAA,CAAA,IAAA,EAAM,KAAK,IAAI,CAAA;AAAA,OACtB,CAAA;AAAA,KACH;AAAA,IACA,CAAC,YAAY,KAAK;AAAA,GACpB;AAEA,EAAA,MAAM,QAAW,GAAA,WAAA;AAAA,IACf,CAAC,IAAY,KAAA;AACX,MAAa,YAAA,CAAA,IAAI,CAAE,CAAA,IAAA,CAAK,MAAM;AAC5B,QAAS,QAAA,CAAA,CAAA,IAAA,KAAQ,KAAK,MAAO,CAAA,CAAA,CAAA,KAAK,CAAC,OAAQ,CAAA,CAAA,EAAG,IAAI,CAAC,CAAC,CAAA;AACpD,QAAM,KAAA,CAAA,IAAA,GAAO,MAAM,IAAM,EAAA,MAAA,CAAO,OAAK,CAAC,OAAA,CAAQ,CAAG,EAAA,IAAI,CAAC,CAAA;AAAA,OACvD,CAAA;AAAA,KACH;AAAA,IACA,CAAC,YAAc,EAAA,OAAA,EAAS,KAAK;AAAA,GAC/B;AAEA,EAAA,MAAM,WAAc,GAAA,WAAA;AAAA,IAClB,CAAC,SAAY,KAAM,CAAA,IAAA,CAAK,OAAK,OAAQ,CAAA,CAAA,EAAG,IAAI,CAAC,CAAA;AAAA,IAC7C,CAAC,OAAO,OAAO;AAAA,GACjB;AAEA,EAAO,OAAA;AAAA,IACL,KAAA;AAAA,IACA,MAAA;AAAA,IACA,QAAA;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
@@ -1,50 +1,33 @@
1
- import { useState, useEffect, useCallback } from 'react';
1
+ import { useCallback } from 'react';
2
2
  import { useApi } from '@backstage/core-plugin-api';
3
3
  import { qetaApiRef } from '../api.esm.js';
4
+ import { useFollow } from './useFollow.esm.js';
4
5
 
5
- let followedTags = void 0;
6
6
  const useTagsFollow = () => {
7
- const [tags, setTags] = useState(followedTags ?? []);
8
- const [loading, setLoading] = useState(followedTags === void 0);
9
7
  const qetaApi = useApi(qetaApiRef);
10
- useEffect(() => {
11
- if (followedTags === void 0) {
12
- qetaApi.getFollowedTags().then((res) => {
13
- followedTags = res.tags;
14
- setTags(res.tags);
15
- setLoading(false);
16
- });
17
- } else {
18
- setTags(followedTags);
8
+ const { items, follow, unfollow, isFollowing, loading } = useFollow(
9
+ "tags",
10
+ {
11
+ fetchFollowed: useCallback(
12
+ () => qetaApi.getFollowedTags().then((res) => res.tags),
13
+ [qetaApi]
14
+ ),
15
+ followItem: useCallback(
16
+ (tag) => qetaApi.followTag(tag),
17
+ [qetaApi]
18
+ ),
19
+ unfollowItem: useCallback(
20
+ (tag) => qetaApi.unfollowTag(tag),
21
+ [qetaApi]
22
+ ),
23
+ isEqual: (a, b) => a === b
19
24
  }
20
- }, [qetaApi]);
21
- const followTag = useCallback(
22
- (tag) => {
23
- qetaApi.followTag(tag).then(() => {
24
- setTags((prev) => [...prev, tag]);
25
- followedTags?.push(tag);
26
- });
27
- },
28
- [qetaApi]
29
- );
30
- const unfollowTag = useCallback(
31
- (tag) => {
32
- qetaApi.unfollowTag(tag).then(() => {
33
- setTags((prev) => prev.filter((t) => t !== tag));
34
- followedTags = followedTags?.filter((t) => t !== tag);
35
- });
36
- },
37
- [qetaApi]
38
- );
39
- const isFollowingTag = useCallback(
40
- (tag) => tags.includes(tag),
41
- [tags]
42
25
  );
43
26
  return {
44
- tags,
45
- followTag,
46
- unfollowTag,
47
- isFollowingTag,
27
+ tags: items,
28
+ followTag: follow,
29
+ unfollowTag: unfollow,
30
+ isFollowingTag: isFollowing,
48
31
  loading
49
32
  };
50
33
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useTagsFollow.esm.js","sources":["../../src/hooks/useTagsFollow.ts"],"sourcesContent":["import { useState, useCallback, useEffect } from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { qetaApiRef } from '../api';\n\nlet followedTags: string[] | undefined = undefined;\n\nexport const useTagsFollow = () => {\n const [tags, setTags] = useState<string[]>(followedTags ?? []);\n const [loading, setLoading] = useState(followedTags === undefined);\n const qetaApi = useApi(qetaApiRef);\n\n useEffect(() => {\n if (followedTags === undefined) {\n qetaApi.getFollowedTags().then(res => {\n followedTags = res.tags;\n setTags(res.tags);\n setLoading(false);\n });\n } else {\n setTags(followedTags);\n }\n }, [qetaApi]);\n\n const followTag = useCallback(\n (tag: string) => {\n qetaApi.followTag(tag).then(() => {\n setTags(prev => [...prev, tag]);\n followedTags?.push(tag);\n });\n },\n [qetaApi],\n );\n\n const unfollowTag = useCallback(\n (tag: string) => {\n qetaApi.unfollowTag(tag).then(() => {\n setTags(prev => prev.filter(t => t !== tag));\n followedTags = followedTags?.filter(t => t !== tag);\n });\n },\n [qetaApi],\n );\n\n const isFollowingTag = useCallback(\n (tag: string) => tags.includes(tag),\n [tags],\n );\n return {\n tags,\n followTag,\n unfollowTag,\n isFollowingTag,\n loading,\n };\n};\n"],"names":[],"mappings":";;;;AAIA,IAAI,YAAqC,GAAA,KAAA,CAAA;AAElC,MAAM,gBAAgB,MAAM;AACjC,EAAA,MAAM,CAAC,IAAM,EAAA,OAAO,IAAI,QAAmB,CAAA,YAAA,IAAgB,EAAE,CAAA;AAC7D,EAAA,MAAM,CAAC,OAAS,EAAA,UAAU,CAAI,GAAA,QAAA,CAAS,iBAAiB,KAAS,CAAA,CAAA;AACjE,EAAM,MAAA,OAAA,GAAU,OAAO,UAAU,CAAA;AAEjC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,iBAAiB,KAAW,CAAA,EAAA;AAC9B,MAAQ,OAAA,CAAA,eAAA,EAAkB,CAAA,IAAA,CAAK,CAAO,GAAA,KAAA;AACpC,QAAA,YAAA,GAAe,GAAI,CAAA,IAAA;AACnB,QAAA,OAAA,CAAQ,IAAI,IAAI,CAAA;AAChB,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA,OACjB,CAAA;AAAA,KACI,MAAA;AACL,MAAA,OAAA,CAAQ,YAAY,CAAA;AAAA;AACtB,GACF,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,MAAM,SAAY,GAAA,WAAA;AAAA,IAChB,CAAC,GAAgB,KAAA;AACf,MAAA,OAAA,CAAQ,SAAU,CAAA,GAAG,CAAE,CAAA,IAAA,CAAK,MAAM;AAChC,QAAA,OAAA,CAAQ,CAAQ,IAAA,KAAA,CAAC,GAAG,IAAA,EAAM,GAAG,CAAC,CAAA;AAC9B,QAAA,YAAA,EAAc,KAAK,GAAG,CAAA;AAAA,OACvB,CAAA;AAAA,KACH;AAAA,IACA,CAAC,OAAO;AAAA,GACV;AAEA,EAAA,MAAM,WAAc,GAAA,WAAA;AAAA,IAClB,CAAC,GAAgB,KAAA;AACf,MAAA,OAAA,CAAQ,WAAY,CAAA,GAAG,CAAE,CAAA,IAAA,CAAK,MAAM;AAClC,QAAA,OAAA,CAAQ,UAAQ,IAAK,CAAA,MAAA,CAAO,CAAK,CAAA,KAAA,CAAA,KAAM,GAAG,CAAC,CAAA;AAC3C,QAAA,YAAA,GAAe,YAAc,EAAA,MAAA,CAAO,CAAK,CAAA,KAAA,CAAA,KAAM,GAAG,CAAA;AAAA,OACnD,CAAA;AAAA,KACH;AAAA,IACA,CAAC,OAAO;AAAA,GACV;AAEA,EAAA,MAAM,cAAiB,GAAA,WAAA;AAAA,IACrB,CAAC,GAAA,KAAgB,IAAK,CAAA,QAAA,CAAS,GAAG,CAAA;AAAA,IAClC,CAAC,IAAI;AAAA,GACP;AACA,EAAO,OAAA;AAAA,IACL,IAAA;AAAA,IACA,SAAA;AAAA,IACA,WAAA;AAAA,IACA,cAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
1
+ {"version":3,"file":"useTagsFollow.esm.js","sources":["../../src/hooks/useTagsFollow.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { qetaApiRef } from '../api';\nimport { useFollow } from './useFollow';\n\nexport const useTagsFollow = () => {\n const qetaApi = useApi(qetaApiRef);\n\n const { items, follow, unfollow, isFollowing, loading } = useFollow<string>(\n 'tags',\n {\n fetchFollowed: useCallback(\n () => qetaApi.getFollowedTags().then(res => res.tags),\n [qetaApi],\n ),\n followItem: useCallback(\n (tag: string) => qetaApi.followTag(tag),\n [qetaApi],\n ),\n unfollowItem: useCallback(\n (tag: string) => qetaApi.unfollowTag(tag),\n [qetaApi],\n ),\n isEqual: (a, b) => a === b,\n },\n );\n\n return {\n tags: items,\n followTag: follow,\n unfollowTag: unfollow,\n isFollowingTag: isFollowing,\n loading,\n };\n};\n"],"names":[],"mappings":";;;;;AAKO,MAAM,gBAAgB,MAAM;AACjC,EAAM,MAAA,OAAA,GAAU,OAAO,UAAU,CAAA;AAEjC,EAAA,MAAM,EAAE,KAAO,EAAA,MAAA,EAAQ,QAAU,EAAA,WAAA,EAAa,SAAY,GAAA,SAAA;AAAA,IACxD,MAAA;AAAA,IACA;AAAA,MACE,aAAe,EAAA,WAAA;AAAA,QACb,MAAM,OAAQ,CAAA,eAAA,GAAkB,IAAK,CAAA,CAAA,GAAA,KAAO,IAAI,IAAI,CAAA;AAAA,QACpD,CAAC,OAAO;AAAA,OACV;AAAA,MACA,UAAY,EAAA,WAAA;AAAA,QACV,CAAC,GAAA,KAAgB,OAAQ,CAAA,SAAA,CAAU,GAAG,CAAA;AAAA,QACtC,CAAC,OAAO;AAAA,OACV;AAAA,MACA,YAAc,EAAA,WAAA;AAAA,QACZ,CAAC,GAAA,KAAgB,OAAQ,CAAA,WAAA,CAAY,GAAG,CAAA;AAAA,QACxC,CAAC,OAAO;AAAA,OACV;AAAA,MACA,OAAS,EAAA,CAAC,CAAG,EAAA,CAAA,KAAM,CAAM,KAAA;AAAA;AAC3B,GACF;AAEA,EAAO,OAAA;AAAA,IACL,IAAM,EAAA,KAAA;AAAA,IACN,SAAW,EAAA,MAAA;AAAA,IACX,WAAa,EAAA,QAAA;AAAA,IACb,cAAgB,EAAA,WAAA;AAAA,IAChB;AAAA,GACF;AACF;;;;"}
@@ -1,50 +1,33 @@
1
- import { useState, useEffect, useCallback } from 'react';
1
+ import { useCallback } from 'react';
2
2
  import { useApi } from '@backstage/core-plugin-api';
3
3
  import { qetaApiRef } from '../api.esm.js';
4
+ import { useFollow } from './useFollow.esm.js';
4
5
 
5
- let followedUsers = void 0;
6
6
  const useUserFollow = () => {
7
- const [users, setUsers] = useState(followedUsers ?? []);
8
- const [loading, setLoading] = useState(followedUsers === void 0);
9
7
  const qetaApi = useApi(qetaApiRef);
10
- useEffect(() => {
11
- if (followedUsers === void 0) {
12
- qetaApi.getFollowedUsers().then((res) => {
13
- followedUsers = res.followedUserRefs;
14
- setUsers(res.followedUserRefs);
15
- setLoading(false);
16
- });
17
- } else {
18
- setUsers(followedUsers);
8
+ const { items, follow, unfollow, isFollowing, loading } = useFollow(
9
+ "users",
10
+ {
11
+ fetchFollowed: useCallback(
12
+ () => qetaApi.getFollowedUsers().then((res) => res.followedUserRefs),
13
+ [qetaApi]
14
+ ),
15
+ followItem: useCallback(
16
+ (user) => qetaApi.followUser(user),
17
+ [qetaApi]
18
+ ),
19
+ unfollowItem: useCallback(
20
+ (user) => qetaApi.unfollowUser(user),
21
+ [qetaApi]
22
+ ),
23
+ isEqual: (a, b) => a === b
19
24
  }
20
- }, [qetaApi]);
21
- const followUser = useCallback(
22
- (user) => {
23
- qetaApi.followUser(user).then(() => {
24
- setUsers((prev) => [...prev, user]);
25
- followedUsers?.push(user);
26
- });
27
- },
28
- [qetaApi]
29
- );
30
- const unfollowUser = useCallback(
31
- (user) => {
32
- qetaApi.unfollowUser(user).then(() => {
33
- setUsers((prev) => prev.filter((t) => t !== user));
34
- followedUsers = followedUsers?.filter((t) => t !== user);
35
- });
36
- },
37
- [qetaApi]
38
- );
39
- const isFollowingUser = useCallback(
40
- (user) => users.includes(user),
41
- [users]
42
25
  );
43
26
  return {
44
- users,
45
- followUser,
46
- unfollowUser,
47
- isFollowingUser,
27
+ users: items,
28
+ followUser: follow,
29
+ unfollowUser: unfollow,
30
+ isFollowingUser: isFollowing,
48
31
  loading
49
32
  };
50
33
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useUserFollow.esm.js","sources":["../../src/hooks/useUserFollow.ts"],"sourcesContent":["import { useState, useCallback, useEffect } from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { qetaApiRef } from '../api';\n\nlet followedUsers: string[] | undefined = undefined;\n\nexport const useUserFollow = () => {\n const [users, setUsers] = useState<string[]>(followedUsers ?? []);\n const [loading, setLoading] = useState(followedUsers === undefined);\n const qetaApi = useApi(qetaApiRef);\n\n useEffect(() => {\n if (followedUsers === undefined) {\n qetaApi.getFollowedUsers().then(res => {\n followedUsers = res.followedUserRefs;\n setUsers(res.followedUserRefs);\n setLoading(false);\n });\n } else {\n setUsers(followedUsers);\n }\n }, [qetaApi]);\n\n const followUser = useCallback(\n (user: string) => {\n qetaApi.followUser(user).then(() => {\n setUsers(prev => [...prev, user]);\n followedUsers?.push(user);\n });\n },\n [qetaApi],\n );\n\n const unfollowUser = useCallback(\n (user: string) => {\n qetaApi.unfollowUser(user).then(() => {\n setUsers(prev => prev.filter(t => t !== user));\n followedUsers = followedUsers?.filter(t => t !== user);\n });\n },\n [qetaApi],\n );\n\n const isFollowingUser = useCallback(\n (user: string) => users.includes(user),\n [users],\n );\n return {\n users,\n followUser,\n unfollowUser,\n isFollowingUser,\n loading,\n };\n};\n"],"names":[],"mappings":";;;;AAIA,IAAI,aAAsC,GAAA,KAAA,CAAA;AAEnC,MAAM,gBAAgB,MAAM;AACjC,EAAA,MAAM,CAAC,KAAO,EAAA,QAAQ,IAAI,QAAmB,CAAA,aAAA,IAAiB,EAAE,CAAA;AAChE,EAAA,MAAM,CAAC,OAAS,EAAA,UAAU,CAAI,GAAA,QAAA,CAAS,kBAAkB,KAAS,CAAA,CAAA;AAClE,EAAM,MAAA,OAAA,GAAU,OAAO,UAAU,CAAA;AAEjC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,kBAAkB,KAAW,CAAA,EAAA;AAC/B,MAAQ,OAAA,CAAA,gBAAA,EAAmB,CAAA,IAAA,CAAK,CAAO,GAAA,KAAA;AACrC,QAAA,aAAA,GAAgB,GAAI,CAAA,gBAAA;AACpB,QAAA,QAAA,CAAS,IAAI,gBAAgB,CAAA;AAC7B,QAAA,UAAA,CAAW,KAAK,CAAA;AAAA,OACjB,CAAA;AAAA,KACI,MAAA;AACL,MAAA,QAAA,CAAS,aAAa,CAAA;AAAA;AACxB,GACF,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,MAAM,UAAa,GAAA,WAAA;AAAA,IACjB,CAAC,IAAiB,KAAA;AAChB,MAAA,OAAA,CAAQ,UAAW,CAAA,IAAI,CAAE,CAAA,IAAA,CAAK,MAAM;AAClC,QAAA,QAAA,CAAS,CAAQ,IAAA,KAAA,CAAC,GAAG,IAAA,EAAM,IAAI,CAAC,CAAA;AAChC,QAAA,aAAA,EAAe,KAAK,IAAI,CAAA;AAAA,OACzB,CAAA;AAAA,KACH;AAAA,IACA,CAAC,OAAO;AAAA,GACV;AAEA,EAAA,MAAM,YAAe,GAAA,WAAA;AAAA,IACnB,CAAC,IAAiB,KAAA;AAChB,MAAA,OAAA,CAAQ,YAAa,CAAA,IAAI,CAAE,CAAA,IAAA,CAAK,MAAM;AACpC,QAAA,QAAA,CAAS,UAAQ,IAAK,CAAA,MAAA,CAAO,CAAK,CAAA,KAAA,CAAA,KAAM,IAAI,CAAC,CAAA;AAC7C,QAAA,aAAA,GAAgB,aAAe,EAAA,MAAA,CAAO,CAAK,CAAA,KAAA,CAAA,KAAM,IAAI,CAAA;AAAA,OACtD,CAAA;AAAA,KACH;AAAA,IACA,CAAC,OAAO;AAAA,GACV;AAEA,EAAA,MAAM,eAAkB,GAAA,WAAA;AAAA,IACtB,CAAC,IAAA,KAAiB,KAAM,CAAA,QAAA,CAAS,IAAI,CAAA;AAAA,IACrC,CAAC,KAAK;AAAA,GACR;AACA,EAAO,OAAA;AAAA,IACL,KAAA;AAAA,IACA,UAAA;AAAA,IACA,YAAA;AAAA,IACA,eAAA;AAAA,IACA;AAAA,GACF;AACF;;;;"}
1
+ {"version":3,"file":"useUserFollow.esm.js","sources":["../../src/hooks/useUserFollow.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { qetaApiRef } from '../api';\nimport { useFollow } from './useFollow';\n\nexport const useUserFollow = () => {\n const qetaApi = useApi(qetaApiRef);\n\n const { items, follow, unfollow, isFollowing, loading } = useFollow<string>(\n 'users',\n {\n fetchFollowed: useCallback(\n () => qetaApi.getFollowedUsers().then(res => res.followedUserRefs),\n [qetaApi],\n ),\n followItem: useCallback(\n (user: string) => qetaApi.followUser(user),\n [qetaApi],\n ),\n unfollowItem: useCallback(\n (user: string) => qetaApi.unfollowUser(user),\n [qetaApi],\n ),\n isEqual: (a, b) => a === b,\n },\n );\n\n return {\n users: items,\n followUser: follow,\n unfollowUser: unfollow,\n isFollowingUser: isFollowing,\n loading,\n };\n};\n"],"names":[],"mappings":";;;;;AAKO,MAAM,gBAAgB,MAAM;AACjC,EAAM,MAAA,OAAA,GAAU,OAAO,UAAU,CAAA;AAEjC,EAAA,MAAM,EAAE,KAAO,EAAA,MAAA,EAAQ,QAAU,EAAA,WAAA,EAAa,SAAY,GAAA,SAAA;AAAA,IACxD,OAAA;AAAA,IACA;AAAA,MACE,aAAe,EAAA,WAAA;AAAA,QACb,MAAM,OAAQ,CAAA,gBAAA,GAAmB,IAAK,CAAA,CAAA,GAAA,KAAO,IAAI,gBAAgB,CAAA;AAAA,QACjE,CAAC,OAAO;AAAA,OACV;AAAA,MACA,UAAY,EAAA,WAAA;AAAA,QACV,CAAC,IAAA,KAAiB,OAAQ,CAAA,UAAA,CAAW,IAAI,CAAA;AAAA,QACzC,CAAC,OAAO;AAAA,OACV;AAAA,MACA,YAAc,EAAA,WAAA;AAAA,QACZ,CAAC,IAAA,KAAiB,OAAQ,CAAA,YAAA,CAAa,IAAI,CAAA;AAAA,QAC3C,CAAC,OAAO;AAAA,OACV;AAAA,MACA,OAAS,EAAA,CAAC,CAAG,EAAA,CAAA,KAAM,CAAM,KAAA;AAAA;AAC3B,GACF;AAEA,EAAO,OAAA;AAAA,IACL,KAAO,EAAA,KAAA;AAAA,IACP,UAAY,EAAA,MAAA;AAAA,IACZ,YAAc,EAAA,QAAA;AAAA,IACd,eAAiB,EAAA,WAAA;AAAA,IACjB;AAAA,GACF;AACF;;;;"}
package/dist/index.d.ts CHANGED
@@ -646,19 +646,33 @@ declare function useVoting(resp: PostResponse | AnswerResponse): {
646
646
 
647
647
  declare const useListItemStyles: (props?: any) => _material_ui_styles.ClassNameMap<"root">;
648
648
 
649
+ interface UseFollowOptions<T> {
650
+ fetchFollowed: () => Promise<T[]>;
651
+ followItem: (item: T) => Promise<unknown>;
652
+ unfollowItem: (item: T) => Promise<unknown>;
653
+ isEqual: (a: T, b: T) => boolean;
654
+ }
655
+ declare const useFollow: <T>(key: string, options: UseFollowOptions<T>) => {
656
+ items: T[];
657
+ follow: (item: T) => void;
658
+ unfollow: (item: T) => void;
659
+ isFollowing: (item: T) => boolean;
660
+ loading: boolean;
661
+ };
662
+
649
663
  declare const useTagsFollow: () => {
650
664
  tags: string[];
651
- followTag: (tag: string) => void;
652
- unfollowTag: (tag: string) => void;
653
- isFollowingTag: (tag: string) => boolean;
665
+ followTag: (item: string) => void;
666
+ unfollowTag: (item: string) => void;
667
+ isFollowingTag: (item: string) => boolean;
654
668
  loading: boolean;
655
669
  };
656
670
 
657
671
  declare const useEntityFollow: () => {
658
672
  entities: string[];
659
- followEntity: (entityRef: string) => void;
660
- unfollowEntity: (entityRef: string) => void;
661
- isFollowingEntity: (entityRef: string) => boolean;
673
+ followEntity: (item: string) => void;
674
+ unfollowEntity: (item: string) => void;
675
+ isFollowingEntity: (item: string) => boolean;
662
676
  loading: boolean;
663
677
  };
664
678
 
@@ -681,9 +695,9 @@ declare const useEntityAuthor: (entity: PostResponse | AnswerResponse | Collecti
681
695
 
682
696
  declare const useUserFollow: () => {
683
697
  users: string[];
684
- followUser: (user: string) => void;
685
- unfollowUser: (user: string) => void;
686
- isFollowingUser: (user: string) => boolean;
698
+ followUser: (item: string) => void;
699
+ unfollowUser: (item: string) => void;
700
+ isFollowingUser: (item: string) => boolean;
687
701
  loading: boolean;
688
702
  };
689
703
 
@@ -691,9 +705,9 @@ declare function useIdentityApi<T>(f: (api: IdentityApi) => Promise<T>, deps?: a
691
705
 
692
706
  declare const useCollectionsFollow: () => {
693
707
  collections: Collection[];
694
- followCollection: (collection: Collection) => void;
695
- unfollowCollection: (collection: Collection) => void;
696
- isFollowingCollection: (collection: Collection) => boolean;
708
+ followCollection: (item: Collection) => void;
709
+ unfollowCollection: (item: Collection) => void;
710
+ isFollowingCollection: (item: Collection) => boolean;
697
711
  loading: boolean;
698
712
  };
699
713
 
@@ -781,6 +795,8 @@ type UserSettingsHook = {
781
795
  };
782
796
  declare const useUserSettings: () => UserSettingsHook;
783
797
 
798
+ declare const useFavicon: (url?: string) => string | undefined;
799
+
784
800
  /** @alpha */
785
801
  declare const qetaTranslationRef: _backstage_frontend_plugin_api.TranslationRef<"qeta", {
786
802
  readonly "answer.questionTitle": "Q: {{question}}";
@@ -1494,4 +1510,4 @@ type QetaOverrides = Overrides & {
1494
1510
  [Name in keyof QetaComponentsNameToClassKey]?: Partial<StyleRules<QetaComponentsNameToClassKey[Name]>>;
1495
1511
  };
1496
1512
 
1497
- export { AIAnswerCard, AddToCollectionButton, AnswerCard, AnswerForm, AnswerListItem, AnswersContainer, AnswersGridItem, ArticleContent, AskQuestionButton, AuthorLink, BadgeChip, ButtonContainer, CollectionFollowButton, CollectionForm, CollectionsContainer, ContentHeader, ContentHeaderButton, ContentHeaderCard, type ContentHeaderCardProps, type ContentHeaderProps, CreateCollectionButton, CreateLinkButton, CreateTagModal, DeleteModal, DeletedBanner, DraftBanner, EditTagModal, EntitiesContainer, EntitiesGridItem, EntityFollowButton, EntityListItem, FaviconItem, type FilterChange, FilterPanel, FollowedCollectionsList, FollowedEntitiesList, FollowedTagsList, FollowedUsersList, type GridType, ImpactCard, LeftMenu, LeftMenuButton, LinkCard, MarkdownRenderer, ObsoleteBanner, ObsoleteModal, OpenLinkButton, PostForm, PostHighlightList, PostHighlightListContainer, PostHighlightListContent, PostListItem, PostsCard, PostsContainer, type PostsContainerProps, PostsGridItem, PostsTable, QetaContext, type QetaContextProps, type QetaEntitiesProps, type QetaOverrides, QetaProvider, QuestionCard, type QuestionFormValues, QuestionsTable, RankingButtons, RelativeTimeWithTooltip, SelectTemplateList, StatsChart, StatusChip, SuggestionsCard, type TagAndEntitiesFormValues, TagFollowButton, TagGridItem, TagListItem, TagsContainer, type TemplateFormValues, TemplateList, Timeline, TimelineItemCard, TopRankingUsers, TrophyIcon, UpdatedByLink, UserBadges, UserFollowButton, UserLink, UserListItem, type UserSettings, type UserSettingsHook, UsersContainer, UsersGridItem, ValidReviewModal, ViewToggle, type ViewType, WriteArticleButton, articleRouteRef, articlesRouteRef, askRouteRef, collectionCreateRouteRef, collectionEditRouteRef, collectionRouteRef, collectionsRouteRef, createLinkRouteRef, editArticleRouteRef, editLinkRouteRef, editQuestionRouteRef, entitiesRouteRef, entityRouteRef, favoriteQuestionsRouteRef, linkRouteRef, linksRouteRef, moderatorRouteRef, qetaApiRef, qetaRouteRef, qetaTranslationRef, qetaTranslations, questionRouteRef, questionsRouteRef, reviewRouteRef, settingsRouteRef, statisticsRouteRef, tagRouteRef, tagsRouteRef, useAI, useCanReview, useCollectionsFollow, useEntityAuthor, useEntityFollow, useGridPageSize, useIdentityApi, useIsModerator, useListItemStyles, useQetaApi, useQetaContext, useQetaEntities, useTagsFollow, useUserFollow, useUserInfo, useUserSettings, useVoting, userRouteRef, usersRouteRef, writeRouteRef };
1513
+ export { AIAnswerCard, AddToCollectionButton, AnswerCard, AnswerForm, AnswerListItem, AnswersContainer, AnswersGridItem, ArticleContent, AskQuestionButton, AuthorLink, BadgeChip, ButtonContainer, CollectionFollowButton, CollectionForm, CollectionsContainer, ContentHeader, ContentHeaderButton, ContentHeaderCard, type ContentHeaderCardProps, type ContentHeaderProps, CreateCollectionButton, CreateLinkButton, CreateTagModal, DeleteModal, DeletedBanner, DraftBanner, EditTagModal, EntitiesContainer, EntitiesGridItem, EntityFollowButton, EntityListItem, FaviconItem, type FilterChange, FilterPanel, FollowedCollectionsList, FollowedEntitiesList, FollowedTagsList, FollowedUsersList, type GridType, ImpactCard, LeftMenu, LeftMenuButton, LinkCard, MarkdownRenderer, ObsoleteBanner, ObsoleteModal, OpenLinkButton, PostForm, PostHighlightList, PostHighlightListContainer, PostHighlightListContent, PostListItem, PostsCard, PostsContainer, type PostsContainerProps, PostsGridItem, PostsTable, QetaContext, type QetaContextProps, type QetaEntitiesProps, type QetaOverrides, QetaProvider, QuestionCard, type QuestionFormValues, QuestionsTable, RankingButtons, RelativeTimeWithTooltip, SelectTemplateList, StatsChart, StatusChip, SuggestionsCard, type TagAndEntitiesFormValues, TagFollowButton, TagGridItem, TagListItem, TagsContainer, type TemplateFormValues, TemplateList, Timeline, TimelineItemCard, TopRankingUsers, TrophyIcon, UpdatedByLink, UserBadges, UserFollowButton, UserLink, UserListItem, type UserSettings, type UserSettingsHook, UsersContainer, UsersGridItem, ValidReviewModal, ViewToggle, type ViewType, WriteArticleButton, articleRouteRef, articlesRouteRef, askRouteRef, collectionCreateRouteRef, collectionEditRouteRef, collectionRouteRef, collectionsRouteRef, createLinkRouteRef, editArticleRouteRef, editLinkRouteRef, editQuestionRouteRef, entitiesRouteRef, entityRouteRef, favoriteQuestionsRouteRef, linkRouteRef, linksRouteRef, moderatorRouteRef, qetaApiRef, qetaRouteRef, qetaTranslationRef, qetaTranslations, questionRouteRef, questionsRouteRef, reviewRouteRef, settingsRouteRef, statisticsRouteRef, tagRouteRef, tagsRouteRef, useAI, useCanReview, useCollectionsFollow, useEntityAuthor, useEntityFollow, useFavicon, useFollow, useGridPageSize, useIdentityApi, useIsModerator, useListItemStyles, useQetaApi, useQetaContext, useQetaEntities, useTagsFollow, useUserFollow, useUserInfo, useUserSettings, useVoting, userRouteRef, usersRouteRef, writeRouteRef };
package/dist/index.esm.js CHANGED
@@ -82,6 +82,7 @@ export { ViewToggle } from './components/ViewToggle/ViewToggle.esm.js';
82
82
  export { useQetaApi } from './hooks/useQetaApi.esm.js';
83
83
  export { useVoting } from './hooks/useVoting.esm.js';
84
84
  export { useListItemStyles } from './hooks/useListItemStyles.esm.js';
85
+ export { useFollow } from './hooks/useFollow.esm.js';
85
86
  export { useTagsFollow } from './hooks/useTagsFollow.esm.js';
86
87
  export { useEntityFollow } from './hooks/useEntityFollow.esm.js';
87
88
  export { useEntityAuthor, useUserInfo } from './hooks/useEntityAuthor.esm.js';
@@ -94,5 +95,6 @@ export { useCanReview } from './hooks/useCanReview.esm.js';
94
95
  export { useGridPageSize } from './hooks/useGridPageSize.esm.js';
95
96
  export { useQetaEntities } from './hooks/useQetaEntities.esm.js';
96
97
  export { useUserSettings } from './hooks/useUserSettings.esm.js';
98
+ export { useFavicon } from './hooks/useFavicon.esm.js';
97
99
  export { qetaTranslationRef, qetaTranslations } from './translation.esm.js';
98
100
  //# sourceMappingURL=index.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "frontend",
8
8
  "backstage.io"
9
9
  ],
10
- "version": "3.56.0",
10
+ "version": "3.57.0",
11
11
  "main": "dist/index.esm.js",
12
12
  "types": "dist/index.d.ts",
13
13
  "prepublishOnly": "yarn tsc && yarn build",
@@ -57,7 +57,7 @@
57
57
  "@backstage/plugin-permission-common": "^0.9.4",
58
58
  "@backstage/plugin-permission-react": "^0.4.39",
59
59
  "@backstage/plugin-signals-react": "^0.0.18",
60
- "@drodil/backstage-plugin-qeta-common": "^3.56.0",
60
+ "@drodil/backstage-plugin-qeta-common": "^3.57.0",
61
61
  "@jsdevtools/rehype-toc": "^3.0.2",
62
62
  "@material-ui/core": "^4.12.2",
63
63
  "@material-ui/icons": "^4.11.3",
@@ -68,7 +68,7 @@
68
68
  "github-slugger": "^2.0.0",
69
69
  "i18next": "^23.16.2",
70
70
  "infinite-scroll-hook": "^1.0.2",
71
- "lodash": "^4.17.21",
71
+ "lodash": "^4.17.23",
72
72
  "numeral": "^2.0.6",
73
73
  "react-dropzone": "^14.3.8",
74
74
  "react-hook-form": "^7.46.2",
@@ -1,87 +0,0 @@
1
- import { jsxs, jsx } from 'react/jsx-runtime';
2
- import '@backstage/core-plugin-api';
3
- import '../../routes.esm.js';
4
- import '../../api.esm.js';
5
- import 'react-use';
6
- import 'react';
7
- import '@backstage/plugin-signals-react';
8
- import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
9
- import { qetaTranslationRef } from '../../translation.esm.js';
10
- import '../../hooks/useListItemStyles.esm.js';
11
- import { useUserInfo } from '../../hooks/useEntityAuthor.esm.js';
12
- import { useUserFollow } from '../../hooks/useUserFollow.esm.js';
13
- import { useIdentityApi } from '../../hooks/useIdentityApi.esm.js';
14
- import '@backstage/plugin-permission-react';
15
- import '@drodil/backstage-plugin-qeta-common';
16
- import '@backstage/plugin-permission-common';
17
- import { Grid, Box, Avatar, Typography, Button } from '@material-ui/core';
18
- import 'react-router-dom';
19
- import 'react-use/lib/useDebounce';
20
- import '../FilterPanel/FilterPanel.esm.js';
21
- import '../QetaContext/QetaContext.esm.js';
22
- import '@backstage/plugin-catalog-react';
23
- import VisibilityOff from '@material-ui/icons/VisibilityOff';
24
- import Visibility from '@material-ui/icons/Visibility';
25
-
26
- const UserTooltip = (props) => {
27
- const { entityRef, anonymous } = props;
28
- const { t } = useTranslationRef(qetaTranslationRef);
29
- const { name, initials, user, secondaryTitle } = useUserInfo(
30
- entityRef,
31
- anonymous ?? entityRef === "anonymous"
32
- );
33
- const { value: currentUser } = useIdentityApi(
34
- (api) => api.getBackstageIdentity(),
35
- []
36
- );
37
- const users = useUserFollow();
38
- return /* @__PURE__ */ jsxs(Grid, { container: true, style: { padding: "0.5em" }, spacing: 1, children: [
39
- /* @__PURE__ */ jsxs(Grid, { item: true, xs: 12, children: [
40
- /* @__PURE__ */ jsxs(Box, { style: { display: "flex", alignItems: "center" }, children: [
41
- /* @__PURE__ */ jsx(Box, { style: { display: "inline-block", marginRight: "0.5em" }, children: /* @__PURE__ */ jsx(
42
- Avatar,
43
- {
44
- src: user?.spec?.profile?.picture,
45
- alt: name,
46
- variant: "rounded",
47
- style: { width: "20px", height: "20px" },
48
- children: initials
49
- }
50
- ) }),
51
- /* @__PURE__ */ jsx(
52
- Typography,
53
- {
54
- variant: "subtitle1",
55
- style: {
56
- textOverflow: "ellipsis",
57
- overflow: "hidden",
58
- whiteSpace: "nowrap"
59
- },
60
- children: name
61
- }
62
- )
63
- ] }),
64
- /* @__PURE__ */ jsx(Typography, { variant: "subtitle2", children: secondaryTitle })
65
- ] }),
66
- !users.loading && currentUser?.userEntityRef !== entityRef && /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(
67
- Button,
68
- {
69
- size: "small",
70
- variant: "outlined",
71
- color: users.isFollowingUser(entityRef) ? "secondary" : "primary",
72
- onClick: () => {
73
- if (users.isFollowingUser(entityRef)) {
74
- users.unfollowUser(entityRef);
75
- } else {
76
- users.followUser(entityRef);
77
- }
78
- },
79
- startIcon: users.isFollowingUser(entityRef) ? /* @__PURE__ */ jsx(VisibilityOff, {}) : /* @__PURE__ */ jsx(Visibility, {}),
80
- children: users.isFollowingUser(entityRef) ? t("userButton.unfollow") : t("userButton.follow")
81
- }
82
- ) })
83
- ] });
84
- };
85
-
86
- export { UserTooltip };
87
- //# sourceMappingURL=UserChip.esm.js.map