@knocklabs/react-core 0.11.3 → 0.12.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 (34) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/cjs/modules/guide/hooks/useGuide.js +1 -1
  3. package/dist/cjs/modules/guide/hooks/useGuide.js.map +1 -1
  4. package/dist/cjs/modules/guide/hooks/useGuides.js +1 -1
  5. package/dist/cjs/modules/guide/hooks/useGuides.js.map +1 -1
  6. package/dist/cjs/modules/ms-teams/hooks/useMsTeamsChannels.js +1 -1
  7. package/dist/cjs/modules/ms-teams/hooks/useMsTeamsChannels.js.map +1 -1
  8. package/dist/cjs/modules/ms-teams/hooks/useMsTeamsTeams.js +1 -1
  9. package/dist/cjs/modules/ms-teams/hooks/useMsTeamsTeams.js.map +1 -1
  10. package/dist/cjs/modules/slack/hooks/useSlackChannels.js +1 -1
  11. package/dist/cjs/modules/slack/hooks/useSlackChannels.js.map +1 -1
  12. package/dist/esm/modules/guide/hooks/useGuide.mjs +9 -9
  13. package/dist/esm/modules/guide/hooks/useGuide.mjs.map +1 -1
  14. package/dist/esm/modules/guide/hooks/useGuides.mjs +9 -9
  15. package/dist/esm/modules/guide/hooks/useGuides.mjs.map +1 -1
  16. package/dist/esm/modules/ms-teams/hooks/useMsTeamsChannels.mjs +31 -25
  17. package/dist/esm/modules/ms-teams/hooks/useMsTeamsChannels.mjs.map +1 -1
  18. package/dist/esm/modules/ms-teams/hooks/useMsTeamsTeams.mjs +37 -33
  19. package/dist/esm/modules/ms-teams/hooks/useMsTeamsTeams.mjs.map +1 -1
  20. package/dist/esm/modules/slack/hooks/useSlackChannels.mjs +41 -37
  21. package/dist/esm/modules/slack/hooks/useSlackChannels.mjs.map +1 -1
  22. package/dist/types/modules/guide/hooks/useGuide.d.ts +2 -2
  23. package/dist/types/modules/guide/hooks/useGuide.d.ts.map +1 -1
  24. package/dist/types/modules/guide/hooks/useGuides.d.ts +2 -2
  25. package/dist/types/modules/guide/hooks/useGuides.d.ts.map +1 -1
  26. package/dist/types/modules/ms-teams/hooks/useMsTeamsChannels.d.ts.map +1 -1
  27. package/dist/types/modules/ms-teams/hooks/useMsTeamsTeams.d.ts.map +1 -1
  28. package/dist/types/modules/slack/hooks/useSlackChannels.d.ts.map +1 -1
  29. package/package.json +2 -2
  30. package/src/modules/guide/hooks/useGuide.ts +3 -1
  31. package/src/modules/guide/hooks/useGuides.ts +7 -2
  32. package/src/modules/ms-teams/hooks/useMsTeamsChannels.ts +48 -13
  33. package/src/modules/ms-teams/hooks/useMsTeamsTeams.ts +81 -31
  34. package/src/modules/slack/hooks/useSlackChannels.ts +82 -32
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2d29ebf: [guides] update selectGuides and useGuides to be subject to throttling by default
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [2d29ebf]
12
+ - @knocklabs/client@0.20.0
13
+
14
+ ## 0.11.5
15
+
16
+ ### Patch Changes
17
+
18
+ - 98a9464: Fix cache issues in `useMsTeamsChannels`, `useMsTeamsTeams`, and `useSlackChannels` hooks
19
+
20
+ The cache keys for these hooks now include `tenantId` and `knockChannelId` to ensure that different tenants and Knock channels have separate cache entries. Additionally, the hooks now clear their SWR cache when:
21
+
22
+ - The tenant ID changes
23
+ - The Knock channel ID changes
24
+ - The access token is revoked
25
+ - The connection status transitions from disconnected/error to connected
26
+
27
+ This prevents stale data from being displayed when switching between different workspaces, revoking access tokens, or reconnecting.
28
+
29
+ ## 0.11.4
30
+
31
+ ### Patch Changes
32
+
33
+ - Updated dependencies [a56bf70]
34
+ - @knocklabs/client@0.19.4
35
+
3
36
  ## 0.11.3
4
37
 
5
38
  ### Patch Changes
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("@tanstack/react-store"),c=require("./useGuideContext.js"),d=e=>{const r=c.useGuideContext();if(!e.key&&!e.type)throw new Error("useGuide must be given at least one filter: { key?: string; type?: string; }");const{client:t,colorMode:s}=r,o=i.useStore(t.store,n=>t.selectGuide(n,e)),u=o&&o.getStep();return{client:t,colorMode:s,guide:o,step:u}};exports.useGuide=d;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("@tanstack/react-store"),d=require("./useGuideContext.js"),g=(e,r)=>{const s=d.useGuideContext();if(!e.key&&!e.type)throw new Error("useGuide must be given at least one filter: { key?: string; type?: string; }");const{client:t,colorMode:u}=s,o=c.useStore(t.store,i=>t.selectGuide(i,e,r)),n=o&&o.getStep();return{client:t,colorMode:u,guide:o,step:n}};exports.useGuide=g;
2
2
  //# sourceMappingURL=useGuide.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useGuide.js","sources":["../../../../../src/modules/guide/hooks/useGuide.ts"],"sourcesContent":["import {\n KnockGuide,\n KnockGuideFilterParams,\n KnockGuideStep,\n} from \"@knocklabs/client\";\nimport { useStore } from \"@tanstack/react-store\";\n\nimport { UseGuideContextReturn, useGuideContext } from \"./useGuideContext\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\ninterface UseGuideReturn<C = Any> extends UseGuideContextReturn {\n guide: KnockGuide<C> | undefined;\n step: KnockGuideStep<C> | undefined;\n}\n\nexport const useGuide = <C = Any>(\n filters: KnockGuideFilterParams,\n): UseGuideReturn<C> => {\n const context = useGuideContext();\n\n if (!filters.key && !filters.type) {\n throw new Error(\n \"useGuide must be given at least one filter: { key?: string; type?: string; }\",\n );\n }\n\n const { client, colorMode } = context;\n\n const guide = useStore(client.store, (state) =>\n client.selectGuide<C>(state, filters),\n );\n\n const step = guide && guide.getStep();\n\n return { client, colorMode, guide, step };\n};\n"],"names":["useGuide","filters","context","useGuideContext","key","type","Error","client","colorMode","guide","useStore","store","selectGuide","state","step","getStep"],"mappings":"2JAiBaA,EACXC,GACsB,CACtB,MAAMC,EAAUC,EAAAA,gBAAgB,EAEhC,GAAI,CAACF,EAAQG,KAAO,CAACH,EAAQI,KACrB,MAAA,IAAIC,MACR,8EACF,EAGI,KAAA,CAAEC,OAAAA,EAAQC,UAAAA,CAAAA,EAAcN,EAExBO,EAAQC,EAAAA,SAASH,EAAOI,SAC5BJ,EAAOK,YAAeC,EAAOZ,CAAO,CACtC,EAEMa,EAAOL,GAASA,EAAMM,QAAQ,EAE7B,MAAA,CAAER,OAAAA,EAAQC,UAAAA,EAAWC,MAAAA,EAAOK,KAAAA,CAAK,CAC1C"}
1
+ {"version":3,"file":"useGuide.js","sources":["../../../../../src/modules/guide/hooks/useGuide.ts"],"sourcesContent":["import {\n KnockGuide,\n KnockGuideFilterParams,\n KnockGuideStep,\n KnockSelectGuideOpts,\n} from \"@knocklabs/client\";\nimport { useStore } from \"@tanstack/react-store\";\n\nimport { UseGuideContextReturn, useGuideContext } from \"./useGuideContext\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\ninterface UseGuideReturn<C = Any> extends UseGuideContextReturn {\n guide: KnockGuide<C> | undefined;\n step: KnockGuideStep<C> | undefined;\n}\n\nexport const useGuide = <C = Any>(\n filters: KnockGuideFilterParams,\n opts?: KnockSelectGuideOpts,\n): UseGuideReturn<C> => {\n const context = useGuideContext();\n\n if (!filters.key && !filters.type) {\n throw new Error(\n \"useGuide must be given at least one filter: { key?: string; type?: string; }\",\n );\n }\n\n const { client, colorMode } = context;\n\n const guide = useStore(client.store, (state) =>\n client.selectGuide<C>(state, filters, opts),\n );\n\n const step = guide && guide.getStep();\n\n return { client, colorMode, guide, step };\n};\n"],"names":["useGuide","filters","opts","context","useGuideContext","key","type","Error","client","colorMode","guide","useStore","store","state","selectGuide","step","getStep"],"mappings":"2JAkBaA,EAAW,CACtBC,EACAC,IACsB,CACtB,MAAMC,EAAUC,EAAAA,gBAAgB,EAEhC,GAAI,CAACH,EAAQI,KAAO,CAACJ,EAAQK,KACrB,MAAA,IAAIC,MACR,8EACF,EAGI,KAAA,CAAEC,OAAAA,EAAQC,UAAAA,CAAAA,EAAcN,EAExBO,EAAQC,EAAAA,SAASH,EAAOI,MAAQC,GACpCL,EAAOM,YAAeD,EAAOZ,EAASC,CAAI,CAC5C,EAEMa,EAAOL,GAASA,EAAMM,QAAQ,EAE7B,MAAA,CAAER,OAAAA,EAAQC,UAAAA,EAAWC,MAAAA,EAAOK,KAAAA,CAAK,CAC1C"}
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("@tanstack/react-store"),n=require("./useGuideContext.js"),i=t=>{const o=n.useGuideContext(),{client:e,colorMode:s}=o,u=c.useStore(e.store,r=>e.selectGuides(r,t));return{client:e,colorMode:s,guides:u}};exports.useGuides=i;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("@tanstack/react-store"),i=require("./useGuideContext.js"),d=(t,o)=>{const s=i.useGuideContext(),{client:e,colorMode:u}=s,r=n.useStore(e.store,c=>e.selectGuides(c,t,o));return{client:e,colorMode:u,guides:r}};exports.useGuides=d;
2
2
  //# sourceMappingURL=useGuides.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useGuides.js","sources":["../../../../../src/modules/guide/hooks/useGuides.ts"],"sourcesContent":["import { KnockGuide, KnockGuideFilterParams } from \"@knocklabs/client\";\nimport { useStore } from \"@tanstack/react-store\";\n\nimport { UseGuideContextReturn, useGuideContext } from \"./useGuideContext\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\ninterface UseGuidesReturn<C = Any> extends UseGuideContextReturn {\n guides: KnockGuide<C>[];\n}\n\nexport const useGuides = <C = Any>(\n filters: Pick<KnockGuideFilterParams, \"type\">,\n): UseGuidesReturn<C> => {\n const context = useGuideContext();\n const { client, colorMode } = context;\n\n const guides = useStore(client.store, (state) =>\n client.selectGuides<C>(state, filters),\n );\n\n return { client, colorMode, guides };\n};\n"],"names":["useGuides","filters","context","useGuideContext","client","colorMode","guides","useStore","store","selectGuides","state"],"mappings":"2JAYaA,EACXC,GACuB,CACvB,MAAMC,EAAUC,EAAAA,gBAAgB,EAC1B,CAAEC,OAAAA,EAAQC,UAAAA,CAAAA,EAAcH,EAExBI,EAASC,EAAAA,SAASH,EAAOI,SAC7BJ,EAAOK,aAAgBC,EAAOT,CAAO,CACvC,EAEO,MAAA,CAAEG,OAAAA,EAAQC,UAAAA,EAAWC,OAAAA,CAAO,CACrC"}
1
+ {"version":3,"file":"useGuides.js","sources":["../../../../../src/modules/guide/hooks/useGuides.ts"],"sourcesContent":["import {\n KnockGuide,\n KnockGuideFilterParams,\n KnockSelectGuidesOpts,\n} from \"@knocklabs/client\";\nimport { useStore } from \"@tanstack/react-store\";\n\nimport { UseGuideContextReturn, useGuideContext } from \"./useGuideContext\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\ninterface UseGuidesReturn<C = Any> extends UseGuideContextReturn {\n guides: KnockGuide<C>[];\n}\n\nexport const useGuides = <C = Any>(\n filters: Pick<KnockGuideFilterParams, \"type\">,\n opts?: KnockSelectGuidesOpts,\n): UseGuidesReturn<C> => {\n const context = useGuideContext();\n const { client, colorMode } = context;\n\n const guides = useStore(client.store, (state) =>\n client.selectGuides<C>(state, filters, opts),\n );\n\n return { client, colorMode, guides };\n};\n"],"names":["useGuides","filters","opts","context","useGuideContext","client","colorMode","guides","useStore","store","state","selectGuides"],"mappings":"2JAgBaA,EAAY,CACvBC,EACAC,IACuB,CACvB,MAAMC,EAAUC,EAAAA,gBAAgB,EAC1B,CAAEC,OAAAA,EAAQC,UAAAA,CAAAA,EAAcH,EAExBI,EAASC,EAAAA,SAASH,EAAOI,MAAQC,GACrCL,EAAOM,aAAgBD,EAAOT,EAASC,CAAI,CAC7C,EAEO,MAAA,CAAEG,OAAAA,EAAQC,UAAAA,EAAWC,OAAAA,CAAO,CACrC"}
@@ -1,2 +1,2 @@
1
- "use strict";const u=require("swr"),f=require("../../core/context/KnockProvider.js");require("@knocklabs/client");require("react");require("fast-deep-equal");require("date-fns");const d=require("../context/KnockMsTeamsProvider.js"),k=e=>e&&typeof e=="object"&&"default"in e?e:{default:e},C=k(u),h="MS_TEAMS_CHANNELS";function m({teamId:e,queryOptions:n}){const t=f.useKnockClient(),{knockMsTeamsChannelId:a,tenantId:c}=d.useKnockMsTeamsClient(),l=()=>t.msTeams.getChannels({knockChannelId:a,tenant:c,teamId:e,queryOptions:{$filter:n==null?void 0:n.filter,$select:n==null?void 0:n.select}}),{data:s,isLoading:o,isValidating:r,mutate:i}=C.default(e?[h,e]:null,l,{revalidateOnFocus:!1});return{data:(s==null?void 0:s.ms_teams_channels)??[],isLoading:o||r,refetch:()=>i()}}module.exports=m;
1
+ "use strict";const a=require("react"),v=require("swr"),T=require("../../core/context/KnockProvider.js");require("@knocklabs/client");require("fast-deep-equal");require("date-fns");const _=require("../context/KnockMsTeamsProvider.js"),S=e=>e&&typeof e=="object"&&"default"in e?e:{default:e},g=S(v),M="MS_TEAMS_CHANNELS";function E({teamId:e,queryOptions:n}){const l=T.useKnockClient(),{knockMsTeamsChannelId:t,tenantId:c,connectionStatus:s}=_.useKnockMsTeamsClient(),u=a.useRef(c),i=a.useRef(t),d=a.useRef(s),f=a.useCallback(()=>l.msTeams.getChannels({knockChannelId:t,tenant:c,teamId:e,queryOptions:{$filter:n==null?void 0:n.filter,$select:n==null?void 0:n.select}}),[l.msTeams,t,c,e,n]),{data:o,isLoading:C,isValidating:h,mutate:r}=g.default(e&&s==="connected"?[M,c,t,e]:null,f,{revalidateOnFocus:!1});return a.useEffect(()=>{const k=u.current!==c,R=i.current!==t,m=!(d.current==="connected")&&s==="connected";(k||R||m)&&r(void 0,{revalidate:!1}),u.current=c,i.current=t,d.current=s},[c,t,s,r]),{data:(o==null?void 0:o.ms_teams_channels)??[],isLoading:C||h,refetch:()=>r()}}module.exports=E;
2
2
  //# sourceMappingURL=useMsTeamsChannels.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useMsTeamsChannels.js","sources":["../../../../../src/modules/ms-teams/hooks/useMsTeamsChannels.ts"],"sourcesContent":["import { GetMsTeamsChannelsResponse, MsTeamsChannel } from \"@knocklabs/client\";\nimport useSWR from \"swr\";\n\nimport { useKnockClient } from \"../../core\";\nimport { useKnockMsTeamsClient } from \"../context\";\nimport { MsTeamsChannelQueryOptions } from \"../interfaces\";\n\nconst QUERY_KEY = \"MS_TEAMS_CHANNELS\";\n\ntype UseMsTeamsChannelsProps = {\n teamId?: string;\n queryOptions?: MsTeamsChannelQueryOptions;\n};\n\ntype UseMsTeamsChannelsOutput = {\n data: MsTeamsChannel[];\n isLoading: boolean;\n refetch: () => void;\n};\n\nfunction useMsTeamsChannels({\n teamId,\n queryOptions,\n}: UseMsTeamsChannelsProps): UseMsTeamsChannelsOutput {\n const knock = useKnockClient();\n const { knockMsTeamsChannelId, tenantId } = useKnockMsTeamsClient();\n\n const fetchChannels = () =>\n knock.msTeams.getChannels({\n knockChannelId: knockMsTeamsChannelId,\n tenant: tenantId,\n teamId: teamId!,\n queryOptions: {\n $filter: queryOptions?.filter,\n $select: queryOptions?.select,\n },\n });\n\n const { data, isLoading, isValidating, mutate } =\n useSWR<GetMsTeamsChannelsResponse>(\n teamId ? [QUERY_KEY, teamId] : null,\n fetchChannels,\n { revalidateOnFocus: false },\n );\n\n return {\n data: data?.ms_teams_channels ?? [],\n isLoading: isLoading || isValidating,\n refetch: () => mutate(),\n };\n}\n\nexport default useMsTeamsChannels;\n"],"names":["QUERY_KEY","useMsTeamsChannels","teamId","queryOptions","knock","useKnockClient","knockMsTeamsChannelId","tenantId","useKnockMsTeamsClient","fetchChannels","msTeams","getChannels","knockChannelId","tenant","$filter","filter","$select","select","data","isLoading","isValidating","mutate","useSWR","revalidateOnFocus","ms_teams_channels","refetch"],"mappings":"uSAOMA,EAAY,oBAalB,SAASC,EAAmB,CAC1BC,OAAAA,EACAC,aAAAA,CACuB,EAA6B,CACpD,MAAMC,EAAQC,EAAAA,eAAe,EACvB,CAAEC,sBAAAA,EAAuBC,SAAAA,GAAaC,wBAAsB,EAE5DC,EAAgBA,IACpBL,EAAMM,QAAQC,YAAY,CACxBC,eAAgBN,EAChBO,OAAQN,EACRL,OAAAA,EACAC,aAAc,CACZW,QAASX,GAAAA,YAAAA,EAAcY,OACvBC,QAASb,GAAAA,YAAAA,EAAcc,MAAAA,CACzB,CACD,EAEG,CAAEC,KAAAA,EAAMC,UAAAA,EAAWC,aAAAA,EAAcC,OAAAA,CAAAA,EACrCC,EAAAA,QACEpB,EAAS,CAACF,EAAWE,CAAM,EAAI,KAC/BO,EACA,CAAEc,kBAAmB,EAAA,CACvB,EAEK,MAAA,CACLL,MAAMA,GAAAA,YAAAA,EAAMM,oBAAqB,CAAE,EACnCL,UAAWA,GAAaC,EACxBK,QAASA,IAAMJ,EAAO,CACxB,CACF"}
1
+ {"version":3,"file":"useMsTeamsChannels.js","sources":["../../../../../src/modules/ms-teams/hooks/useMsTeamsChannels.ts"],"sourcesContent":["import { GetMsTeamsChannelsResponse, MsTeamsChannel } from \"@knocklabs/client\";\nimport { useCallback, useEffect, useRef } from \"react\";\nimport useSWR from \"swr\";\n\nimport { useKnockClient } from \"../../core\";\nimport { useKnockMsTeamsClient } from \"../context\";\nimport { MsTeamsChannelQueryOptions } from \"../interfaces\";\n\nconst QUERY_KEY = \"MS_TEAMS_CHANNELS\";\n\ntype UseMsTeamsChannelsProps = {\n teamId?: string;\n queryOptions?: MsTeamsChannelQueryOptions;\n};\n\ntype UseMsTeamsChannelsOutput = {\n data: MsTeamsChannel[];\n isLoading: boolean;\n refetch: () => void;\n};\n\nfunction useMsTeamsChannels({\n teamId,\n queryOptions,\n}: UseMsTeamsChannelsProps): UseMsTeamsChannelsOutput {\n const knock = useKnockClient();\n const { knockMsTeamsChannelId, tenantId, connectionStatus } =\n useKnockMsTeamsClient();\n\n // Track previous tenant/channel/connectionStatus to detect changes and clear cache\n const prevTenantRef = useRef(tenantId);\n const prevChannelRef = useRef(knockMsTeamsChannelId);\n const prevConnectionStatusRef = useRef(connectionStatus);\n\n const fetchChannels = useCallback(\n () =>\n knock.msTeams.getChannels({\n knockChannelId: knockMsTeamsChannelId,\n tenant: tenantId,\n teamId: teamId!,\n queryOptions: {\n $filter: queryOptions?.filter,\n $select: queryOptions?.select,\n },\n }),\n [knock.msTeams, knockMsTeamsChannelId, tenantId, teamId, queryOptions],\n );\n\n // Include tenantId and knockMsTeamsChannelId in the cache key so that\n // SWR treats different tenants as different cache entries\n const { data, isLoading, isValidating, mutate } =\n useSWR<GetMsTeamsChannelsResponse>(\n teamId && connectionStatus === \"connected\"\n ? [QUERY_KEY, tenantId, knockMsTeamsChannelId, teamId]\n : null,\n fetchChannels,\n { revalidateOnFocus: false },\n );\n\n // Clear cache when tenant, channel, or connection status changes\n // This ensures that when the user disconnects and reconnects (possibly to a different\n // MS Teams workspace), or when the access token is revoked, the cached channels are cleared\n useEffect(() => {\n const tenantChanged = prevTenantRef.current !== tenantId;\n const channelChanged = prevChannelRef.current !== knockMsTeamsChannelId;\n // Detect when connection is re-established (was not connected, now is connected)\n const wasConnected = prevConnectionStatusRef.current === \"connected\";\n const isConnected = connectionStatus === \"connected\";\n const connectionReestablished = !wasConnected && isConnected;\n\n if (tenantChanged || channelChanged || connectionReestablished) {\n // Reset the SWR state to clear cached data\n mutate(undefined, { revalidate: false });\n }\n\n prevTenantRef.current = tenantId;\n prevChannelRef.current = knockMsTeamsChannelId;\n prevConnectionStatusRef.current = connectionStatus;\n }, [tenantId, knockMsTeamsChannelId, connectionStatus, mutate]);\n\n return {\n data: data?.ms_teams_channels ?? [],\n isLoading: isLoading || isValidating,\n refetch: () => mutate(),\n };\n}\n\nexport default useMsTeamsChannels;\n"],"names":["QUERY_KEY","useMsTeamsChannels","teamId","queryOptions","knock","useKnockClient","knockMsTeamsChannelId","tenantId","connectionStatus","useKnockMsTeamsClient","prevTenantRef","useRef","prevChannelRef","prevConnectionStatusRef","fetchChannels","useCallback","msTeams","getChannels","knockChannelId","tenant","$filter","filter","$select","select","data","isLoading","isValidating","mutate","useSWR","revalidateOnFocus","useEffect","tenantChanged","current","channelChanged","connectionReestablished","undefined","revalidate","ms_teams_channels","refetch"],"mappings":"ySAQMA,EAAY,oBAalB,SAASC,EAAmB,CAC1BC,OAAAA,EACAC,aAAAA,CACuB,EAA6B,CACpD,MAAMC,EAAQC,EAAAA,eAAe,EACvB,CAAEC,sBAAAA,EAAuBC,SAAAA,EAAUC,iBAAAA,GACvCC,wBAAsB,EAGlBC,EAAgBC,SAAOJ,CAAQ,EAC/BK,EAAiBD,SAAOL,CAAqB,EAC7CO,EAA0BF,SAAOH,CAAgB,EAEjDM,EAAgBC,EAAAA,YACpB,IACEX,EAAMY,QAAQC,YAAY,CACxBC,eAAgBZ,EAChBa,OAAQZ,EACRL,OAAAA,EACAC,aAAc,CACZiB,QAASjB,GAAAA,YAAAA,EAAckB,OACvBC,QAASnB,GAAAA,YAAAA,EAAcoB,MAAAA,CACzB,CACD,EACH,CAACnB,EAAMY,QAASV,EAAuBC,EAAUL,EAAQC,CAAY,CACvE,EAIM,CAAEqB,KAAAA,EAAMC,UAAAA,EAAWC,aAAAA,EAAcC,OAAAA,CACrCC,EAAAA,UACE1B,GAAUM,IAAqB,YAC3B,CAACR,EAAWO,EAAUD,EAAuBJ,CAAM,EACnD,KACJY,EACA,CAAEe,kBAAmB,EAAA,CACvB,EAKFC,OAAAA,EAAAA,UAAU,IAAM,CACRC,MAAAA,EAAgBrB,EAAcsB,UAAYzB,EAC1C0B,EAAiBrB,EAAeoB,UAAY1B,EAI5C4B,EAA0B,EAFXrB,EAAwBmB,UAAY,cACrCxB,IAAqB,aAGrCuB,GAAiBE,GAAkBC,IAErCP,EAAOQ,OAAW,CAAEC,WAAY,EAAA,CAAO,EAGzC1B,EAAcsB,QAAUzB,EACxBK,EAAeoB,QAAU1B,EACzBO,EAAwBmB,QAAUxB,GACjC,CAACD,EAAUD,EAAuBE,EAAkBmB,CAAM,CAAC,EAEvD,CACLH,MAAMA,GAAAA,YAAAA,EAAMa,oBAAqB,CAAE,EACnCZ,UAAWA,GAAaC,EACxBY,QAASA,IAAMX,EAAO,CACxB,CACF"}
@@ -1,2 +1,2 @@
1
- "use strict";const r=require("react"),S=require("swr/infinite"),h=require("../../core/context/KnockProvider.js");require("@knocklabs/client");require("fast-deep-equal");require("date-fns");const g=require("../context/KnockMsTeamsProvider.js"),E=e=>e&&typeof e=="object"&&"default"in e?e:{default:e},I=E(S),P=1e3,_="MS_TEAMS_TEAMS";function x(e,n){return e===0?[_,""]:n&&["",null].includes(n.skip_token)?null:[_,n.skip_token??""]}function R({queryOptions:e={}}){const n=h.useKnockClient(),{knockMsTeamsChannelId:u,tenantId:T,connectionStatus:l}=g.useKnockMsTeamsClient(),M=t=>n.msTeams.getTeams({knockChannelId:u,tenant:T,queryOptions:{$skiptoken:t==null?void 0:t[1],$top:e==null?void 0:e.limitPerPage,$filter:e==null?void 0:e.filter,$select:e==null?void 0:e.select}}),{data:s,error:o,isLoading:c,isValidating:a,setSize:f,mutate:C}=I.default(x,M,{initialSize:0,revalidateOnFocus:!1,revalidateFirstPage:!1}),m=s==null?void 0:s.at(-1),k=m===void 0||!!m.skip_token,i=r.useMemo(()=>(s??[]).flatMap(t=>t==null?void 0:t.ms_teams_teams).filter(t=>!!t),[s]),d=(e==null?void 0:e.maxCount)||P;return r.useEffect(()=>{l==="connected"&&!o&&k&&!c&&!a&&i.length<d&&f(t=>t+1)},[i.length,f,k,c,a,d,o,l]),{data:i,isLoading:c||a,refetch:()=>C()}}module.exports=R;
1
+ "use strict";const o=require("react"),v=require("swr/infinite"),x=require("../../core/context/KnockProvider.js");require("@knocklabs/client");require("fast-deep-equal");require("date-fns");const $=require("../context/KnockMsTeamsProvider.js"),g=e=>e&&typeof e=="object"&&"default"in e?e:{default:e},A=g(v),w=1e3,S="MS_TEAMS_TEAMS";function z({queryOptions:e={}}){const k=x.useKnockClient(),{knockMsTeamsChannelId:t,tenantId:c,connectionStatus:s}=$.useKnockMsTeamsClient(),u=o.useRef(c),C=o.useRef(t),T=o.useRef(s),E=o.useCallback((n,a)=>s!=="connected"?null:n===0?[S,c,t,""]:a&&["",null].includes(a.skip_token)?null:[S,c,t,(a==null?void 0:a.skip_token)??""],[c,t,s]),I=o.useCallback(n=>k.msTeams.getTeams({knockChannelId:t,tenant:c,queryOptions:{$skiptoken:n==null?void 0:n[3],$top:e==null?void 0:e.limitPerPage,$filter:e==null?void 0:e.filter,$select:e==null?void 0:e.select}}),[k.msTeams,t,c,e]),{data:l,error:h,isLoading:i,isValidating:r,setSize:f,mutate:d}=A.default(E,I,{initialSize:0,revalidateOnFocus:!1,revalidateFirstPage:!1});o.useEffect(()=>{const n=u.current!==c,a=C.current!==t,b=!(T.current==="connected")&&s==="connected";(n||a||b)&&(d(void 0,{revalidate:!1}),f(0)),u.current=c,C.current=t,T.current=s},[c,t,s,d,f]);const _=l==null?void 0:l.at(-1),R=_===void 0||!!_.skip_token,m=o.useMemo(()=>(l??[]).flatMap(n=>n==null?void 0:n.ms_teams_teams).filter(n=>!!n),[l]),M=(e==null?void 0:e.maxCount)||w;return o.useEffect(()=>{s==="connected"&&!h&&R&&!i&&!r&&m.length<M&&f(n=>n+1)},[m.length,f,R,i,r,M,h,s]),{data:m,isLoading:i||r,refetch:()=>d()}}module.exports=z;
2
2
  //# sourceMappingURL=useMsTeamsTeams.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useMsTeamsTeams.js","sources":["../../../../../src/modules/ms-teams/hooks/useMsTeamsTeams.ts"],"sourcesContent":["import { GetMsTeamsTeamsResponse, MsTeamsTeam } from \"@knocklabs/client\";\nimport { useEffect, useMemo } from \"react\";\nimport useSWRInfinite from \"swr/infinite\";\n\nimport { useKnockClient } from \"../../core\";\nimport { useKnockMsTeamsClient } from \"../context\";\nimport { MsTeamsTeamQueryOptions } from \"../interfaces\";\n\nconst MAX_COUNT = 1000;\n\nconst QUERY_KEY = \"MS_TEAMS_TEAMS\";\n\ntype UseMsTeamsTeamsOptions = {\n queryOptions?: MsTeamsTeamQueryOptions;\n};\n\ntype UseMsTeamsTeamsOutput = {\n data: MsTeamsTeam[];\n isLoading: boolean;\n refetch: () => void;\n};\n\ntype QueryKey = [key: string, skiptoken: string] | null;\n\nfunction getQueryKey(\n pageIndex: number,\n previousPageData: GetMsTeamsTeamsResponse,\n): QueryKey {\n // First page so just pass empty\n if (pageIndex === 0) {\n return [QUERY_KEY, \"\"];\n }\n\n // If there's no more data then return an empty next skiptoken\n if (previousPageData && [\"\", null].includes(previousPageData.skip_token)) {\n return null;\n }\n\n // Next skiptoken exists so pass it\n return [QUERY_KEY, previousPageData.skip_token ?? \"\"];\n}\n\nfunction useMsTeamsTeams({\n queryOptions = {},\n}: UseMsTeamsTeamsOptions): UseMsTeamsTeamsOutput {\n const knock = useKnockClient();\n const { knockMsTeamsChannelId, tenantId, connectionStatus } =\n useKnockMsTeamsClient();\n\n const fetchTeams = (queryKey: QueryKey) =>\n knock.msTeams.getTeams({\n knockChannelId: knockMsTeamsChannelId,\n tenant: tenantId,\n queryOptions: {\n $skiptoken: queryKey?.[1],\n $top: queryOptions?.limitPerPage,\n $filter: queryOptions?.filter,\n $select: queryOptions?.select,\n },\n });\n\n const { data, error, isLoading, isValidating, setSize, mutate } =\n useSWRInfinite<GetMsTeamsTeamsResponse>(getQueryKey, fetchTeams, {\n initialSize: 0,\n revalidateOnFocus: false,\n revalidateFirstPage: false,\n });\n\n const lastPage = data?.at(-1);\n const hasNextPage = lastPage === undefined || !!lastPage.skip_token;\n\n const teams = useMemo(\n () =>\n (data ?? [])\n .flatMap((page) => page?.ms_teams_teams)\n .filter((team) => !!team),\n [data],\n );\n\n const maxCount = queryOptions?.maxCount || MAX_COUNT;\n\n useEffect(() => {\n if (\n connectionStatus === \"connected\" &&\n !error &&\n hasNextPage &&\n !isLoading &&\n !isValidating &&\n teams.length < maxCount\n ) {\n // Fetch a page at a time until we have nothing else left to fetch\n // or we've already hit the max amount of teams to fetch\n setSize((size) => size + 1);\n }\n }, [\n teams.length,\n setSize,\n hasNextPage,\n isLoading,\n isValidating,\n maxCount,\n error,\n connectionStatus,\n ]);\n\n return {\n data: teams,\n isLoading: isLoading || isValidating,\n refetch: () => mutate(),\n };\n}\n\nexport default useMsTeamsTeams;\n"],"names":["MAX_COUNT","QUERY_KEY","getQueryKey","pageIndex","previousPageData","includes","skip_token","useMsTeamsTeams","queryOptions","knock","useKnockClient","knockMsTeamsChannelId","tenantId","connectionStatus","useKnockMsTeamsClient","fetchTeams","queryKey","msTeams","getTeams","knockChannelId","tenant","$skiptoken","$top","limitPerPage","$filter","filter","$select","select","data","error","isLoading","isValidating","setSize","mutate","useSWRInfinite","initialSize","revalidateOnFocus","revalidateFirstPage","lastPage","at","hasNextPage","undefined","teams","useMemo","flatMap","page","ms_teams_teams","team","maxCount","useEffect","length","size","refetch"],"mappings":"kTAQMA,EAAY,IAEZC,EAAY,iBAclB,SAASC,EACPC,EACAC,EACU,CAEV,OAAID,IAAc,EACT,CAACF,EAAW,EAAE,EAInBG,GAAoB,CAAC,GAAI,IAAI,EAAEC,SAASD,EAAiBE,UAAU,EAC9D,KAIF,CAACL,EAAWG,EAAiBE,YAAc,EAAE,CACtD,CAEA,SAASC,EAAgB,CACvBC,aAAAA,EAAe,CAAA,CACO,EAA0B,CAChD,MAAMC,EAAQC,EAAAA,eAAe,EACvB,CAAEC,sBAAAA,EAAuBC,SAAAA,EAAUC,iBAAAA,GACvCC,wBAAsB,EAElBC,EAAcC,GAClBP,EAAMQ,QAAQC,SAAS,CACrBC,eAAgBR,EAChBS,OAAQR,EACRJ,aAAc,CACZa,WAAYL,GAAAA,YAAAA,EAAW,GACvBM,KAAMd,GAAAA,YAAAA,EAAce,aACpBC,QAAShB,GAAAA,YAAAA,EAAciB,OACvBC,QAASlB,GAAAA,YAAAA,EAAcmB,MAAAA,CACzB,CACD,EAEG,CAAEC,KAAAA,EAAMC,MAAAA,EAAOC,UAAAA,EAAWC,aAAAA,EAAcC,QAAAA,EAASC,OAAAA,CAAAA,EACrDC,EAAwChC,QAAAA,EAAaa,EAAY,CAC/DoB,YAAa,EACbC,kBAAmB,GACnBC,oBAAqB,EAAA,CACtB,EAEGC,EAAWV,GAAAA,YAAAA,EAAMW,GAAG,IACpBC,EAAcF,IAAaG,QAAa,CAAC,CAACH,EAAShC,WAEnDoC,EAAQC,EAAAA,QACZ,KACGf,GAAQ,IACNgB,QAAkBC,GAAAA,GAAAA,YAAAA,EAAMC,cAAc,EACtCrB,UAAiB,CAAC,CAACsB,CAAI,EAC5B,CAACnB,CAAI,CACP,EAEMoB,GAAWxC,GAAAA,YAAAA,EAAcwC,WAAYhD,EAE3CiD,OAAAA,EAAAA,UAAU,IAAM,CAEZpC,IAAqB,aACrB,CAACgB,GACDW,GACA,CAACV,GACD,CAACC,GACDW,EAAMQ,OAASF,GAING,EAAAA,GAASA,EAAO,CAAC,CAE9B,EAAG,CACDT,EAAMQ,OACNlB,EACAQ,EACAV,EACAC,EACAiB,EACAnB,EACAhB,CAAgB,CACjB,EAEM,CACLe,KAAMc,EACNZ,UAAWA,GAAaC,EACxBqB,QAASA,IAAMnB,EAAO,CACxB,CACF"}
1
+ {"version":3,"file":"useMsTeamsTeams.js","sources":["../../../../../src/modules/ms-teams/hooks/useMsTeamsTeams.ts"],"sourcesContent":["import { GetMsTeamsTeamsResponse, MsTeamsTeam } from \"@knocklabs/client\";\nimport { useCallback, useEffect, useMemo, useRef } from \"react\";\nimport useSWRInfinite from \"swr/infinite\";\n\nimport { useKnockClient } from \"../../core\";\nimport { useKnockMsTeamsClient } from \"../context\";\nimport { MsTeamsTeamQueryOptions } from \"../interfaces\";\n\nconst MAX_COUNT = 1000;\n\nconst QUERY_KEY = \"MS_TEAMS_TEAMS\";\n\ntype UseMsTeamsTeamsOptions = {\n queryOptions?: MsTeamsTeamQueryOptions;\n};\n\ntype UseMsTeamsTeamsOutput = {\n data: MsTeamsTeam[];\n isLoading: boolean;\n refetch: () => void;\n};\n\ntype QueryKey =\n | [key: string, tenantId: string, channelId: string, skiptoken: string]\n | null;\n\nfunction useMsTeamsTeams({\n queryOptions = {},\n}: UseMsTeamsTeamsOptions): UseMsTeamsTeamsOutput {\n const knock = useKnockClient();\n const { knockMsTeamsChannelId, tenantId, connectionStatus } =\n useKnockMsTeamsClient();\n\n // Track previous tenant/channel/connectionStatus to detect changes and clear cache\n const prevTenantRef = useRef(tenantId);\n const prevChannelRef = useRef(knockMsTeamsChannelId);\n const prevConnectionStatusRef = useRef(connectionStatus);\n\n // Create a getQueryKey function that includes tenantId and knockMsTeamsChannelId\n // so that SWR treats different tenants as different cache entries\n const getQueryKey = useCallback(\n (\n pageIndex: number,\n previousPageData: GetMsTeamsTeamsResponse | null,\n ): QueryKey => {\n // Don't fetch if not connected\n if (connectionStatus !== \"connected\") {\n return null;\n }\n\n // First page so just pass empty\n if (pageIndex === 0) {\n return [QUERY_KEY, tenantId, knockMsTeamsChannelId, \"\"];\n }\n\n // If there's no more data then return an empty next skiptoken\n if (\n previousPageData &&\n [\"\", null].includes(previousPageData.skip_token)\n ) {\n return null;\n }\n\n // Next skiptoken exists so pass it\n return [\n QUERY_KEY,\n tenantId,\n knockMsTeamsChannelId,\n previousPageData?.skip_token ?? \"\",\n ];\n },\n [tenantId, knockMsTeamsChannelId, connectionStatus],\n );\n\n const fetchTeams = useCallback(\n (queryKey: QueryKey) =>\n knock.msTeams.getTeams({\n knockChannelId: knockMsTeamsChannelId,\n tenant: tenantId,\n queryOptions: {\n $skiptoken: queryKey?.[3],\n $top: queryOptions?.limitPerPage,\n $filter: queryOptions?.filter,\n $select: queryOptions?.select,\n },\n }),\n [knock.msTeams, knockMsTeamsChannelId, tenantId, queryOptions],\n );\n\n const { data, error, isLoading, isValidating, setSize, mutate } =\n useSWRInfinite<GetMsTeamsTeamsResponse>(getQueryKey, fetchTeams, {\n initialSize: 0,\n revalidateOnFocus: false,\n revalidateFirstPage: false,\n });\n\n // Clear cache when tenant, channel, or connection status changes\n // This ensures that when the user disconnects and reconnects (possibly to a different\n // MS Teams workspace), or when the access token is revoked, the cached teams are cleared\n useEffect(() => {\n const tenantChanged = prevTenantRef.current !== tenantId;\n const channelChanged = prevChannelRef.current !== knockMsTeamsChannelId;\n // Detect when connection is re-established (was not connected, now is connected)\n const wasConnected = prevConnectionStatusRef.current === \"connected\";\n const isConnected = connectionStatus === \"connected\";\n const connectionReestablished = !wasConnected && isConnected;\n\n if (tenantChanged || channelChanged || connectionReestablished) {\n // Reset the SWR state to clear cached data\n mutate(undefined, { revalidate: false });\n setSize(0);\n }\n\n prevTenantRef.current = tenantId;\n prevChannelRef.current = knockMsTeamsChannelId;\n prevConnectionStatusRef.current = connectionStatus;\n }, [tenantId, knockMsTeamsChannelId, connectionStatus, mutate, setSize]);\n\n const lastPage = data?.at(-1);\n const hasNextPage = lastPage === undefined || !!lastPage.skip_token;\n\n const teams = useMemo(\n () =>\n (data ?? [])\n .flatMap((page) => page?.ms_teams_teams)\n .filter((team) => !!team),\n [data],\n );\n\n const maxCount = queryOptions?.maxCount || MAX_COUNT;\n\n useEffect(() => {\n if (\n connectionStatus === \"connected\" &&\n !error &&\n hasNextPage &&\n !isLoading &&\n !isValidating &&\n teams.length < maxCount\n ) {\n // Fetch a page at a time until we have nothing else left to fetch\n // or we've already hit the max amount of teams to fetch\n setSize((size) => size + 1);\n }\n }, [\n teams.length,\n setSize,\n hasNextPage,\n isLoading,\n isValidating,\n maxCount,\n error,\n connectionStatus,\n ]);\n\n return {\n data: teams,\n isLoading: isLoading || isValidating,\n refetch: () => mutate(),\n };\n}\n\nexport default useMsTeamsTeams;\n"],"names":["MAX_COUNT","QUERY_KEY","useMsTeamsTeams","queryOptions","knock","useKnockClient","knockMsTeamsChannelId","tenantId","connectionStatus","useKnockMsTeamsClient","prevTenantRef","useRef","prevChannelRef","prevConnectionStatusRef","getQueryKey","useCallback","pageIndex","previousPageData","includes","skip_token","fetchTeams","queryKey","msTeams","getTeams","knockChannelId","tenant","$skiptoken","$top","limitPerPage","$filter","filter","$select","select","data","error","isLoading","isValidating","setSize","mutate","useSWRInfinite","initialSize","revalidateOnFocus","revalidateFirstPage","useEffect","tenantChanged","current","channelChanged","connectionReestablished","undefined","revalidate","lastPage","at","hasNextPage","teams","useMemo","flatMap","page","ms_teams_teams","team","maxCount","length","size","refetch"],"mappings":"kTAQMA,EAAY,IAEZC,EAAY,iBAgBlB,SAASC,EAAgB,CACvBC,aAAAA,EAAe,CAAA,CACO,EAA0B,CAChD,MAAMC,EAAQC,EAAAA,eAAe,EACvB,CAAEC,sBAAAA,EAAuBC,SAAAA,EAAUC,iBAAAA,GACvCC,wBAAsB,EAGlBC,EAAgBC,SAAOJ,CAAQ,EAC/BK,EAAiBD,SAAOL,CAAqB,EAC7CO,EAA0BF,SAAOH,CAAgB,EAIjDM,EAAcC,EAAAA,YAClB,CACEC,EACAC,IAGIT,IAAqB,YAChB,KAILQ,IAAc,EACT,CAACf,EAAWM,EAAUD,EAAuB,EAAE,EAKtDW,GACA,CAAC,GAAI,IAAI,EAAEC,SAASD,EAAiBE,UAAU,EAExC,KAIF,CACLlB,EACAM,EACAD,GACAW,GAAAA,YAAAA,EAAkBE,aAAc,EAAE,EAGtC,CAACZ,EAAUD,EAAuBE,CAAgB,CACpD,EAEMY,EAAaL,EAAAA,YAChBM,GACCjB,EAAMkB,QAAQC,SAAS,CACrBC,eAAgBlB,EAChBmB,OAAQlB,EACRJ,aAAc,CACZuB,WAAYL,GAAAA,YAAAA,EAAW,GACvBM,KAAMxB,GAAAA,YAAAA,EAAcyB,aACpBC,QAAS1B,GAAAA,YAAAA,EAAc2B,OACvBC,QAAS5B,GAAAA,YAAAA,EAAc6B,MAAAA,CACzB,CACD,EACH,CAAC5B,EAAMkB,QAAShB,EAAuBC,EAAUJ,CAAY,CAC/D,EAEM,CAAE8B,KAAAA,EAAMC,MAAAA,EAAOC,UAAAA,EAAWC,aAAAA,EAAcC,QAAAA,EAASC,OAAAA,CAAAA,EACrDC,EAAwCzB,QAAAA,EAAaM,EAAY,CAC/DoB,YAAa,EACbC,kBAAmB,GACnBC,oBAAqB,EAAA,CACtB,EAKHC,EAAAA,UAAU,IAAM,CACRC,MAAAA,EAAgBlC,EAAcmC,UAAYtC,EAC1CuC,EAAiBlC,EAAeiC,UAAYvC,EAI5CyC,EAA0B,EAFXlC,EAAwBgC,UAAY,cACrCrC,IAAqB,aAGrCoC,GAAiBE,GAAkBC,KAErCT,EAAOU,OAAW,CAAEC,WAAY,EAAA,CAAO,EACvCZ,EAAQ,CAAC,GAGX3B,EAAcmC,QAAUtC,EACxBK,EAAeiC,QAAUvC,EACzBO,EAAwBgC,QAAUrC,CAAAA,EACjC,CAACD,EAAUD,EAAuBE,EAAkB8B,EAAQD,CAAO,CAAC,EAEjEa,MAAAA,EAAWjB,GAAAA,YAAAA,EAAMkB,GAAG,IACpBC,EAAcF,IAAaF,QAAa,CAAC,CAACE,EAAS/B,WAEnDkC,EAAQC,EAAAA,QACZ,KACGrB,GAAQ,IACNsB,QAAkBC,GAAAA,GAAAA,YAAAA,EAAMC,cAAc,EACtC3B,UAAiB,CAAC,CAAC4B,CAAI,EAC5B,CAACzB,CAAI,CACP,EAEM0B,GAAWxD,GAAAA,YAAAA,EAAcwD,WAAY3D,EAE3C2C,OAAAA,EAAAA,UAAU,IAAM,CAEZnC,IAAqB,aACrB,CAAC0B,GACDkB,GACA,CAACjB,GACD,CAACC,GACDiB,EAAMO,OAASD,GAINE,EAAAA,GAASA,EAAO,CAAC,CAE9B,EAAG,CACDR,EAAMO,OACNvB,EACAe,EACAjB,EACAC,EACAuB,EACAzB,EACA1B,CAAgB,CACjB,EAEM,CACLyB,KAAMoB,EACNlB,UAAWA,GAAaC,EACxB0B,QAASA,IAAMxB,EAAO,CACxB,CACF"}
@@ -1,2 +1,2 @@
1
- "use strict";const P=require("../context/KnockSlackProvider.js"),d=require("react");require("../../i18n/context/KnockI18nProvider.js");const m=require("swr/infinite"),x=require("../../core/context/KnockProvider.js");require("@knocklabs/client");require("fast-deep-equal");require("date-fns");require("swr");const I=n=>n&&typeof n=="object"&&"default"in n?n:{default:n},L=I(m),N=1e3,g=200,A="private_channel,public_channel",C="SLACK_CHANNELS";function R(n,e){return n===0?[C,""]:e&&["",null].includes(e.next_cursor)?null:[C,e.next_cursor??""]}function M({queryOptions:n}){const e=x.useKnockClient(),{knockSlackChannelId:_,tenantId:h,connectionStatus:i}=P.useKnockSlackClient(),S=t=>e.slack.getChannels({tenant:h,knockChannelId:_,queryOptions:{...n,cursor:t==null?void 0:t[1],limit:(n==null?void 0:n.limitPerPage)||g,types:(n==null?void 0:n.types)||A}}),{data:c,error:r,isLoading:l,isValidating:a,setSize:o,mutate:E}=L.default(R,S,{initialSize:0,revalidateFirstPage:!1}),u=c==null?void 0:c.at(-1),f=u===void 0||!!u.next_cursor,s=d.useMemo(()=>(c??[]).flatMap(t=>t==null?void 0:t.slack_channels).filter(t=>!!t),[c]),k=(n==null?void 0:n.maxCount)||N;return d.useEffect(()=>{i==="connected"&&!r&&f&&!l&&!a&&s.length<k&&o(t=>t+1)},[s.length,o,f,l,a,k,r,i]),{data:s,isLoading:l||a,refetch:()=>E()}}module.exports=M;
1
+ "use strict";const b=require("../context/KnockSlackProvider.js"),l=require("react");require("../../i18n/context/KnockI18nProvider.js");const A=require("swr/infinite"),v=require("../../core/context/KnockProvider.js");require("@knocklabs/client");require("fast-deep-equal");require("date-fns");require("swr");const M=n=>n&&typeof n=="object"&&"default"in n?n:{default:n},P=M(A),T=1e3,g=200,K="private_channel,public_channel",x="SLACK_CHANNELS";function Y({queryOptions:n}){const C=v.useKnockClient(),{knockSlackChannelId:t,tenantId:c,connectionStatus:s}=b.useKnockSlackClient(),k=l.useRef(c),h=l.useRef(t),_=l.useRef(s),I=l.useCallback((e,o)=>s!=="connected"?null:e===0?[x,c,t,""]:o&&["",null].includes(o.next_cursor)?null:[x,c,t,(o==null?void 0:o.next_cursor)??""],[c,t,s]),L=l.useCallback(e=>C.slack.getChannels({tenant:c,knockChannelId:t,queryOptions:{...n,cursor:e==null?void 0:e[3],limit:(n==null?void 0:n.limitPerPage)||g,types:(n==null?void 0:n.types)||K}}),[C.slack,c,t,n]),{data:r,error:S,isLoading:i,isValidating:u,setSize:a,mutate:f}=P.default(I,L,{initialSize:0,revalidateFirstPage:!1});l.useEffect(()=>{const e=k.current!==c,o=h.current!==t,N=!(_.current==="connected")&&s==="connected";(e||o||N)&&(f(void 0,{revalidate:!1}),a(0)),k.current=c,h.current=t,_.current=s},[c,t,s,f,a]);const R=r==null?void 0:r.at(-1),E=R===void 0||!!R.next_cursor,d=l.useMemo(()=>(r??[]).flatMap(e=>e==null?void 0:e.slack_channels).filter(e=>!!e),[r]),m=(n==null?void 0:n.maxCount)||T;return l.useEffect(()=>{s==="connected"&&!S&&E&&!i&&!u&&d.length<m&&a(e=>e+1)},[d.length,a,E,i,u,m,S,s]),{data:d,isLoading:i||u,refetch:()=>f()}}module.exports=Y;
2
2
  //# sourceMappingURL=useSlackChannels.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSlackChannels.js","sources":["../../../../../src/modules/slack/hooks/useSlackChannels.ts"],"sourcesContent":["import { SlackChannelQueryOptions, useKnockSlackClient } from \"..\";\nimport { GetSlackChannelsResponse, SlackChannel } from \"@knocklabs/client\";\nimport { useEffect, useMemo } from \"react\";\nimport useSWRInfinite from \"swr/infinite\";\n\nimport { useKnockClient } from \"../../core\";\n\nconst MAX_COUNT = 1000;\nconst LIMIT_PER_PAGE = 200;\nconst CHANNEL_TYPES = \"private_channel,public_channel\";\n\nconst QUERY_KEY = \"SLACK_CHANNELS\";\n\ntype UseSlackChannelsOptions = {\n queryOptions?: SlackChannelQueryOptions;\n};\n\ntype UseSlackChannelOutput = {\n data: SlackChannel[];\n isLoading: boolean;\n refetch: () => void;\n};\n\ntype QueryKey = [key: string, cursor: string] | null;\n\nfunction getQueryKey(\n pageIndex: number,\n previousPageData: GetSlackChannelsResponse,\n): QueryKey {\n // First page so just pass empty\n if (pageIndex === 0) {\n return [QUERY_KEY, \"\"];\n }\n\n // If there's no more data then return an empty next cursor\n if (previousPageData && [\"\", null].includes(previousPageData.next_cursor)) {\n return null;\n }\n\n // Next cursor exists so pass it\n return [QUERY_KEY, previousPageData.next_cursor ?? \"\"];\n}\n\nfunction useSlackChannels({\n queryOptions,\n}: UseSlackChannelsOptions): UseSlackChannelOutput {\n const knock = useKnockClient();\n const { knockSlackChannelId, tenantId, connectionStatus } =\n useKnockSlackClient();\n\n const fetchChannels = (queryKey: QueryKey) => {\n return knock.slack.getChannels({\n tenant: tenantId,\n knockChannelId: knockSlackChannelId,\n queryOptions: {\n ...queryOptions,\n cursor: queryKey?.[1],\n limit: queryOptions?.limitPerPage || LIMIT_PER_PAGE,\n types: queryOptions?.types || CHANNEL_TYPES,\n },\n });\n };\n\n const { data, error, isLoading, isValidating, setSize, mutate } =\n useSWRInfinite<GetSlackChannelsResponse>(getQueryKey, fetchChannels, {\n initialSize: 0,\n revalidateFirstPage: false,\n });\n\n const lastPage = data?.at(-1);\n const hasNextPage = lastPage === undefined || !!lastPage.next_cursor;\n\n const slackChannels: SlackChannel[] = useMemo(\n () =>\n (data ?? [])\n .flatMap((page) => page?.slack_channels)\n .filter((channel) => !!channel),\n [data],\n );\n\n const maxCount = queryOptions?.maxCount || MAX_COUNT;\n\n useEffect(() => {\n if (\n connectionStatus === \"connected\" &&\n !error &&\n hasNextPage &&\n !isLoading &&\n !isValidating &&\n slackChannels.length < maxCount\n ) {\n // Fetch a page at a time until we have nothing else left to fetch\n // or we've already hit the max amount of channels to fetch\n setSize((size) => size + 1);\n }\n }, [\n slackChannels.length,\n setSize,\n hasNextPage,\n isLoading,\n isValidating,\n maxCount,\n error,\n connectionStatus,\n ]);\n\n return {\n data: slackChannels,\n isLoading: isLoading || isValidating,\n refetch: () => mutate(),\n };\n}\n\nexport default useSlackChannels;\n"],"names":["MAX_COUNT","LIMIT_PER_PAGE","CHANNEL_TYPES","QUERY_KEY","getQueryKey","pageIndex","previousPageData","includes","next_cursor","useSlackChannels","queryOptions","knock","useKnockClient","knockSlackChannelId","tenantId","connectionStatus","useKnockSlackClient","fetchChannels","queryKey","slack","getChannels","tenant","knockChannelId","cursor","limit","limitPerPage","types","data","error","isLoading","isValidating","setSize","mutate","useSWRInfinite","initialSize","revalidateFirstPage","lastPage","at","hasNextPage","undefined","slackChannels","useMemo","flatMap","page","slack_channels","filter","channel","maxCount","useEffect","length","size","refetch"],"mappings":"wXAOMA,EAAY,IACZC,EAAiB,IACjBC,EAAgB,iCAEhBC,EAAY,iBAclB,SAASC,EACPC,EACAC,EACU,CAEV,OAAID,IAAc,EACT,CAACF,EAAW,EAAE,EAInBG,GAAoB,CAAC,GAAI,IAAI,EAAEC,SAASD,EAAiBE,WAAW,EAC/D,KAIF,CAACL,EAAWG,EAAiBE,aAAe,EAAE,CACvD,CAEA,SAASC,EAAiB,CACxBC,aAAAA,CACuB,EAA0B,CACjD,MAAMC,EAAQC,EAAAA,eAAe,EACvB,CAAEC,oBAAAA,EAAqBC,SAAAA,EAAUC,iBAAAA,GACrCC,sBAAoB,EAEhBC,EAAiBC,GACdP,EAAMQ,MAAMC,YAAY,CAC7BC,OAAQP,EACRQ,eAAgBT,EAChBH,aAAc,CACZ,GAAGA,EACHa,OAAQL,GAAAA,YAAAA,EAAW,GACnBM,OAAOd,GAAAA,YAAAA,EAAce,eAAgBxB,EACrCyB,OAAOhB,GAAAA,YAAAA,EAAcgB,QAASxB,CAAAA,CAChC,CACD,EAGG,CAAEyB,KAAAA,EAAMC,MAAAA,EAAOC,UAAAA,EAAWC,aAAAA,EAAcC,QAAAA,EAASC,OAAAA,CAAAA,EACrDC,EAAyC7B,QAAAA,EAAaa,EAAe,CACnEiB,YAAa,EACbC,oBAAqB,EAAA,CACtB,EAEGC,EAAWT,GAAAA,YAAAA,EAAMU,GAAG,IACpBC,EAAcF,IAAaG,QAAa,CAAC,CAACH,EAAS5B,YAEnDgC,EAAgCC,EAAAA,QACpC,KACGd,GAAQ,IACNe,QAAkBC,GAAAA,GAAAA,YAAAA,EAAMC,cAAc,EACtCC,UAAoB,CAAC,CAACC,CAAO,EAClC,CAACnB,CAAI,CACP,EAEMoB,GAAWrC,GAAAA,YAAAA,EAAcqC,WAAY/C,EAE3CgD,OAAAA,EAAAA,UAAU,IAAM,CAEZjC,IAAqB,aACrB,CAACa,GACDU,GACA,CAACT,GACD,CAACC,GACDU,EAAcS,OAASF,GAIdG,EAAAA,GAASA,EAAO,CAAC,CAE9B,EAAG,CACDV,EAAcS,OACdlB,EACAO,EACAT,EACAC,EACAiB,EACAnB,EACAb,CAAgB,CACjB,EAEM,CACLY,KAAMa,EACNX,UAAWA,GAAaC,EACxBqB,QAASA,IAAMnB,EAAO,CACxB,CACF"}
1
+ {"version":3,"file":"useSlackChannels.js","sources":["../../../../../src/modules/slack/hooks/useSlackChannels.ts"],"sourcesContent":["import { SlackChannelQueryOptions, useKnockSlackClient } from \"..\";\nimport { GetSlackChannelsResponse, SlackChannel } from \"@knocklabs/client\";\nimport { useCallback, useEffect, useMemo, useRef } from \"react\";\nimport useSWRInfinite from \"swr/infinite\";\n\nimport { useKnockClient } from \"../../core\";\n\nconst MAX_COUNT = 1000;\nconst LIMIT_PER_PAGE = 200;\nconst CHANNEL_TYPES = \"private_channel,public_channel\";\n\nconst QUERY_KEY = \"SLACK_CHANNELS\";\n\ntype UseSlackChannelsOptions = {\n queryOptions?: SlackChannelQueryOptions;\n};\n\ntype UseSlackChannelOutput = {\n data: SlackChannel[];\n isLoading: boolean;\n refetch: () => void;\n};\n\ntype QueryKey =\n | [key: string, tenantId: string, channelId: string, cursor: string]\n | null;\n\nfunction useSlackChannels({\n queryOptions,\n}: UseSlackChannelsOptions): UseSlackChannelOutput {\n const knock = useKnockClient();\n const { knockSlackChannelId, tenantId, connectionStatus } =\n useKnockSlackClient();\n\n // Track previous tenant/channel/connectionStatus to detect changes and clear cache\n const prevTenantRef = useRef(tenantId);\n const prevChannelRef = useRef(knockSlackChannelId);\n const prevConnectionStatusRef = useRef(connectionStatus);\n\n // Create a getQueryKey function that includes tenantId and knockSlackChannelId\n // so that SWR treats different tenants as different cache entries\n const getQueryKey = useCallback(\n (\n pageIndex: number,\n previousPageData: GetSlackChannelsResponse | null,\n ): QueryKey => {\n // Don't fetch if not connected\n if (connectionStatus !== \"connected\") {\n return null;\n }\n\n // First page so just pass empty\n if (pageIndex === 0) {\n return [QUERY_KEY, tenantId, knockSlackChannelId, \"\"];\n }\n\n // If there's no more data then return an empty next cursor\n if (\n previousPageData &&\n [\"\", null].includes(previousPageData.next_cursor)\n ) {\n return null;\n }\n\n // Next cursor exists so pass it\n return [\n QUERY_KEY,\n tenantId,\n knockSlackChannelId,\n previousPageData?.next_cursor ?? \"\",\n ];\n },\n [tenantId, knockSlackChannelId, connectionStatus],\n );\n\n const fetchChannels = useCallback(\n (queryKey: QueryKey) => {\n return knock.slack.getChannels({\n tenant: tenantId,\n knockChannelId: knockSlackChannelId,\n queryOptions: {\n ...queryOptions,\n cursor: queryKey?.[3],\n limit: queryOptions?.limitPerPage || LIMIT_PER_PAGE,\n types: queryOptions?.types || CHANNEL_TYPES,\n },\n });\n },\n [knock.slack, tenantId, knockSlackChannelId, queryOptions],\n );\n\n const { data, error, isLoading, isValidating, setSize, mutate } =\n useSWRInfinite<GetSlackChannelsResponse>(getQueryKey, fetchChannels, {\n initialSize: 0,\n revalidateFirstPage: false,\n });\n\n // Clear cache when tenant, channel, or connection status changes\n // This ensures that when the user disconnects and reconnects (possibly to a different\n // Slack workspace), or when the access token is revoked, the cached channels are cleared\n useEffect(() => {\n const tenantChanged = prevTenantRef.current !== tenantId;\n const channelChanged = prevChannelRef.current !== knockSlackChannelId;\n // Detect when connection is re-established (was not connected, now is connected)\n const wasConnected = prevConnectionStatusRef.current === \"connected\";\n const isConnected = connectionStatus === \"connected\";\n const connectionReestablished = !wasConnected && isConnected;\n\n if (tenantChanged || channelChanged || connectionReestablished) {\n // Reset the SWR state to clear cached data\n mutate(undefined, { revalidate: false });\n setSize(0);\n }\n\n prevTenantRef.current = tenantId;\n prevChannelRef.current = knockSlackChannelId;\n prevConnectionStatusRef.current = connectionStatus;\n }, [tenantId, knockSlackChannelId, connectionStatus, mutate, setSize]);\n\n const lastPage = data?.at(-1);\n const hasNextPage = lastPage === undefined || !!lastPage.next_cursor;\n\n const slackChannels: SlackChannel[] = useMemo(\n () =>\n (data ?? [])\n .flatMap((page) => page?.slack_channels)\n .filter((channel) => !!channel),\n [data],\n );\n\n const maxCount = queryOptions?.maxCount || MAX_COUNT;\n\n useEffect(() => {\n if (\n connectionStatus === \"connected\" &&\n !error &&\n hasNextPage &&\n !isLoading &&\n !isValidating &&\n slackChannels.length < maxCount\n ) {\n // Fetch a page at a time until we have nothing else left to fetch\n // or we've already hit the max amount of channels to fetch\n setSize((size) => size + 1);\n }\n }, [\n slackChannels.length,\n setSize,\n hasNextPage,\n isLoading,\n isValidating,\n maxCount,\n error,\n connectionStatus,\n ]);\n\n return {\n data: slackChannels,\n isLoading: isLoading || isValidating,\n refetch: () => mutate(),\n };\n}\n\nexport default useSlackChannels;\n"],"names":["MAX_COUNT","LIMIT_PER_PAGE","CHANNEL_TYPES","QUERY_KEY","useSlackChannels","queryOptions","knock","useKnockClient","knockSlackChannelId","tenantId","connectionStatus","useKnockSlackClient","prevTenantRef","useRef","prevChannelRef","prevConnectionStatusRef","getQueryKey","useCallback","pageIndex","previousPageData","includes","next_cursor","fetchChannels","queryKey","slack","getChannels","tenant","knockChannelId","cursor","limit","limitPerPage","types","data","error","isLoading","isValidating","setSize","mutate","useSWRInfinite","initialSize","revalidateFirstPage","useEffect","tenantChanged","current","channelChanged","connectionReestablished","undefined","revalidate","lastPage","at","hasNextPage","slackChannels","useMemo","flatMap","page","slack_channels","filter","channel","maxCount","length","size","refetch"],"mappings":"wXAOMA,EAAY,IACZC,EAAiB,IACjBC,EAAgB,iCAEhBC,EAAY,iBAgBlB,SAASC,EAAiB,CACxBC,aAAAA,CACuB,EAA0B,CACjD,MAAMC,EAAQC,EAAAA,eAAe,EACvB,CAAEC,oBAAAA,EAAqBC,SAAAA,EAAUC,iBAAAA,GACrCC,sBAAoB,EAGhBC,EAAgBC,SAAOJ,CAAQ,EAC/BK,EAAiBD,SAAOL,CAAmB,EAC3CO,EAA0BF,SAAOH,CAAgB,EAIjDM,EAAcC,EAAAA,YAClB,CACEC,EACAC,IAGIT,IAAqB,YAChB,KAILQ,IAAc,EACT,CAACf,EAAWM,EAAUD,EAAqB,EAAE,EAKpDW,GACA,CAAC,GAAI,IAAI,EAAEC,SAASD,EAAiBE,WAAW,EAEzC,KAIF,CACLlB,EACAM,EACAD,GACAW,GAAAA,YAAAA,EAAkBE,cAAe,EAAE,EAGvC,CAACZ,EAAUD,EAAqBE,CAAgB,CAClD,EAEMY,EAAgBL,cACnBM,GACQjB,EAAMkB,MAAMC,YAAY,CAC7BC,OAAQjB,EACRkB,eAAgBnB,EAChBH,aAAc,CACZ,GAAGA,EACHuB,OAAQL,GAAAA,YAAAA,EAAW,GACnBM,OAAOxB,GAAAA,YAAAA,EAAcyB,eAAgB7B,EACrC8B,OAAO1B,GAAAA,YAAAA,EAAc0B,QAAS7B,CAAAA,CAChC,CACD,EAEH,CAACI,EAAMkB,MAAOf,EAAUD,EAAqBH,CAAY,CAC3D,EAEM,CAAE2B,KAAAA,EAAMC,MAAAA,EAAOC,UAAAA,EAAWC,aAAAA,EAAcC,QAAAA,EAASC,OAAAA,CAAAA,EACrDC,EAAyCtB,QAAAA,EAAaM,EAAe,CACnEiB,YAAa,EACbC,oBAAqB,EAAA,CACtB,EAKHC,EAAAA,UAAU,IAAM,CACRC,MAAAA,EAAgB9B,EAAc+B,UAAYlC,EAC1CmC,EAAiB9B,EAAe6B,UAAYnC,EAI5CqC,EAA0B,EAFX9B,EAAwB4B,UAAY,cACrCjC,IAAqB,aAGrCgC,GAAiBE,GAAkBC,KAErCR,EAAOS,OAAW,CAAEC,WAAY,EAAA,CAAO,EACvCX,EAAQ,CAAC,GAGXxB,EAAc+B,QAAUlC,EACxBK,EAAe6B,QAAUnC,EACzBO,EAAwB4B,QAAUjC,CAAAA,EACjC,CAACD,EAAUD,EAAqBE,EAAkB2B,EAAQD,CAAO,CAAC,EAE/DY,MAAAA,EAAWhB,GAAAA,YAAAA,EAAMiB,GAAG,IACpBC,EAAcF,IAAaF,QAAa,CAAC,CAACE,EAAS3B,YAEnD8B,EAAgCC,EAAAA,QACpC,KACGpB,GAAQ,IACNqB,QAAkBC,GAAAA,GAAAA,YAAAA,EAAMC,cAAc,EACtCC,UAAoB,CAAC,CAACC,CAAO,EAClC,CAACzB,CAAI,CACP,EAEM0B,GAAWrD,GAAAA,YAAAA,EAAcqD,WAAY1D,EAE3CyC,OAAAA,EAAAA,UAAU,IAAM,CAEZ/B,IAAqB,aACrB,CAACuB,GACDiB,GACA,CAAChB,GACD,CAACC,GACDgB,EAAcQ,OAASD,GAIdE,EAAAA,GAASA,EAAO,CAAC,CAE9B,EAAG,CACDT,EAAcQ,OACdvB,EACAc,EACAhB,EACAC,EACAuB,EACAzB,EACAvB,CAAgB,CACjB,EAEM,CACLsB,KAAMmB,EACNjB,UAAWA,GAAaC,EACxB0B,QAASA,IAAMxB,EAAO,CACxB,CACF"}
@@ -1,21 +1,21 @@
1
- import { useStore as u } from "@tanstack/react-store";
2
- import { useGuideContext as c } from "./useGuideContext.mjs";
3
- const g = (e) => {
4
- const r = c();
1
+ import { useStore as c } from "@tanstack/react-store";
2
+ import { useGuideContext as p } from "./useGuideContext.mjs";
3
+ const m = (e, r) => {
4
+ const n = p();
5
5
  if (!e.key && !e.type)
6
6
  throw new Error("useGuide must be given at least one filter: { key?: string; type?: string; }");
7
7
  const {
8
8
  client: t,
9
- colorMode: n
10
- } = r, o = u(t.store, (i) => t.selectGuide(i, e)), s = o && o.getStep();
9
+ colorMode: s
10
+ } = n, o = c(t.store, (u) => t.selectGuide(u, e, r)), i = o && o.getStep();
11
11
  return {
12
12
  client: t,
13
- colorMode: n,
13
+ colorMode: s,
14
14
  guide: o,
15
- step: s
15
+ step: i
16
16
  };
17
17
  };
18
18
  export {
19
- g as useGuide
19
+ m as useGuide
20
20
  };
21
21
  //# sourceMappingURL=useGuide.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useGuide.mjs","sources":["../../../../../src/modules/guide/hooks/useGuide.ts"],"sourcesContent":["import {\n KnockGuide,\n KnockGuideFilterParams,\n KnockGuideStep,\n} from \"@knocklabs/client\";\nimport { useStore } from \"@tanstack/react-store\";\n\nimport { UseGuideContextReturn, useGuideContext } from \"./useGuideContext\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\ninterface UseGuideReturn<C = Any> extends UseGuideContextReturn {\n guide: KnockGuide<C> | undefined;\n step: KnockGuideStep<C> | undefined;\n}\n\nexport const useGuide = <C = Any>(\n filters: KnockGuideFilterParams,\n): UseGuideReturn<C> => {\n const context = useGuideContext();\n\n if (!filters.key && !filters.type) {\n throw new Error(\n \"useGuide must be given at least one filter: { key?: string; type?: string; }\",\n );\n }\n\n const { client, colorMode } = context;\n\n const guide = useStore(client.store, (state) =>\n client.selectGuide<C>(state, filters),\n );\n\n const step = guide && guide.getStep();\n\n return { client, colorMode, guide, step };\n};\n"],"names":["useGuide","filters","context","useGuideContext","key","type","Error","client","colorMode","guide","useStore","store","state","selectGuide","step","getStep"],"mappings":";;AAiBaA,MAAAA,IAAW,CACtBC,MACsB;AACtB,QAAMC,IAAUC,EAAgB;AAEhC,MAAI,CAACF,EAAQG,OAAO,CAACH,EAAQI;AACrB,UAAA,IAAIC,MACR,8EACF;AAGI,QAAA;AAAA,IAAEC,QAAAA;AAAAA,IAAQC,WAAAA;AAAAA,EAAAA,IAAcN,GAExBO,IAAQC,EAASH,EAAOI,OAAQC,OACpCL,EAAOM,YAAeD,GAAOX,CAAO,CACtC,GAEMa,IAAOL,KAASA,EAAMM,QAAQ;AAE7B,SAAA;AAAA,IAAER,QAAAA;AAAAA,IAAQC,WAAAA;AAAAA,IAAWC,OAAAA;AAAAA,IAAOK,MAAAA;AAAAA,EAAK;AAC1C;"}
1
+ {"version":3,"file":"useGuide.mjs","sources":["../../../../../src/modules/guide/hooks/useGuide.ts"],"sourcesContent":["import {\n KnockGuide,\n KnockGuideFilterParams,\n KnockGuideStep,\n KnockSelectGuideOpts,\n} from \"@knocklabs/client\";\nimport { useStore } from \"@tanstack/react-store\";\n\nimport { UseGuideContextReturn, useGuideContext } from \"./useGuideContext\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\ninterface UseGuideReturn<C = Any> extends UseGuideContextReturn {\n guide: KnockGuide<C> | undefined;\n step: KnockGuideStep<C> | undefined;\n}\n\nexport const useGuide = <C = Any>(\n filters: KnockGuideFilterParams,\n opts?: KnockSelectGuideOpts,\n): UseGuideReturn<C> => {\n const context = useGuideContext();\n\n if (!filters.key && !filters.type) {\n throw new Error(\n \"useGuide must be given at least one filter: { key?: string; type?: string; }\",\n );\n }\n\n const { client, colorMode } = context;\n\n const guide = useStore(client.store, (state) =>\n client.selectGuide<C>(state, filters, opts),\n );\n\n const step = guide && guide.getStep();\n\n return { client, colorMode, guide, step };\n};\n"],"names":["useGuide","filters","opts","context","useGuideContext","key","type","Error","client","colorMode","guide","useStore","store","state","selectGuide","step","getStep"],"mappings":";;AAkBaA,MAAAA,IAAW,CACtBC,GACAC,MACsB;AACtB,QAAMC,IAAUC,EAAgB;AAEhC,MAAI,CAACH,EAAQI,OAAO,CAACJ,EAAQK;AACrB,UAAA,IAAIC,MACR,8EACF;AAGI,QAAA;AAAA,IAAEC,QAAAA;AAAAA,IAAQC,WAAAA;AAAAA,EAAAA,IAAcN,GAExBO,IAAQC,EAASH,EAAOI,OAAQC,CAAAA,MACpCL,EAAOM,YAAeD,GAAOZ,GAASC,CAAI,CAC5C,GAEMa,IAAOL,KAASA,EAAMM,QAAQ;AAE7B,SAAA;AAAA,IAAER,QAAAA;AAAAA,IAAQC,WAAAA;AAAAA,IAAWC,OAAAA;AAAAA,IAAOK,MAAAA;AAAAA,EAAK;AAC1C;"}
@@ -1,17 +1,17 @@
1
- import { useStore as n } from "@tanstack/react-store";
2
- import { useGuideContext as u } from "./useGuideContext.mjs";
3
- const m = (o) => {
4
- const t = u(), {
1
+ import { useStore as u } from "@tanstack/react-store";
2
+ import { useGuideContext as i } from "./useGuideContext.mjs";
3
+ const l = (o, t) => {
4
+ const s = i(), {
5
5
  client: e,
6
- colorMode: s
7
- } = t, r = n(e.store, (c) => e.selectGuides(c, o));
6
+ colorMode: r
7
+ } = s, c = u(e.store, (n) => e.selectGuides(n, o, t));
8
8
  return {
9
9
  client: e,
10
- colorMode: s,
11
- guides: r
10
+ colorMode: r,
11
+ guides: c
12
12
  };
13
13
  };
14
14
  export {
15
- m as useGuides
15
+ l as useGuides
16
16
  };
17
17
  //# sourceMappingURL=useGuides.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useGuides.mjs","sources":["../../../../../src/modules/guide/hooks/useGuides.ts"],"sourcesContent":["import { KnockGuide, KnockGuideFilterParams } from \"@knocklabs/client\";\nimport { useStore } from \"@tanstack/react-store\";\n\nimport { UseGuideContextReturn, useGuideContext } from \"./useGuideContext\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\ninterface UseGuidesReturn<C = Any> extends UseGuideContextReturn {\n guides: KnockGuide<C>[];\n}\n\nexport const useGuides = <C = Any>(\n filters: Pick<KnockGuideFilterParams, \"type\">,\n): UseGuidesReturn<C> => {\n const context = useGuideContext();\n const { client, colorMode } = context;\n\n const guides = useStore(client.store, (state) =>\n client.selectGuides<C>(state, filters),\n );\n\n return { client, colorMode, guides };\n};\n"],"names":["useGuides","filters","context","useGuideContext","client","colorMode","guides","useStore","store","state","selectGuides"],"mappings":";;AAYaA,MAAAA,IAAY,CACvBC,MACuB;AACvB,QAAMC,IAAUC,EAAgB,GAC1B;AAAA,IAAEC,QAAAA;AAAAA,IAAQC,WAAAA;AAAAA,EAAAA,IAAcH,GAExBI,IAASC,EAASH,EAAOI,OAAQC,OACrCL,EAAOM,aAAgBD,GAAOR,CAAO,CACvC;AAEO,SAAA;AAAA,IAAEG,QAAAA;AAAAA,IAAQC,WAAAA;AAAAA,IAAWC,QAAAA;AAAAA,EAAO;AACrC;"}
1
+ {"version":3,"file":"useGuides.mjs","sources":["../../../../../src/modules/guide/hooks/useGuides.ts"],"sourcesContent":["import {\n KnockGuide,\n KnockGuideFilterParams,\n KnockSelectGuidesOpts,\n} from \"@knocklabs/client\";\nimport { useStore } from \"@tanstack/react-store\";\n\nimport { UseGuideContextReturn, useGuideContext } from \"./useGuideContext\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Any = any;\n\ninterface UseGuidesReturn<C = Any> extends UseGuideContextReturn {\n guides: KnockGuide<C>[];\n}\n\nexport const useGuides = <C = Any>(\n filters: Pick<KnockGuideFilterParams, \"type\">,\n opts?: KnockSelectGuidesOpts,\n): UseGuidesReturn<C> => {\n const context = useGuideContext();\n const { client, colorMode } = context;\n\n const guides = useStore(client.store, (state) =>\n client.selectGuides<C>(state, filters, opts),\n );\n\n return { client, colorMode, guides };\n};\n"],"names":["useGuides","filters","opts","context","useGuideContext","client","colorMode","guides","useStore","store","state","selectGuides"],"mappings":";;AAgBaA,MAAAA,IAAY,CACvBC,GACAC,MACuB;AACvB,QAAMC,IAAUC,EAAgB,GAC1B;AAAA,IAAEC,QAAAA;AAAAA,IAAQC,WAAAA;AAAAA,EAAAA,IAAcH,GAExBI,IAASC,EAASH,EAAOI,OAAQC,CAAAA,MACrCL,EAAOM,aAAgBD,GAAOT,GAASC,CAAI,CAC7C;AAEO,SAAA;AAAA,IAAEG,QAAAA;AAAAA,IAAQC,WAAAA;AAAAA,IAAWC,QAAAA;AAAAA,EAAO;AACrC;"}
@@ -1,41 +1,47 @@
1
- import f from "swr";
2
- import { useKnockClient as r } from "../../core/context/KnockProvider.mjs";
1
+ import { useRef as l, useCallback as T, useEffect as g } from "react";
2
+ import v from "swr";
3
+ import { useKnockClient as S } from "../../core/context/KnockProvider.mjs";
3
4
  import "@knocklabs/client";
4
- import "react";
5
5
  import "fast-deep-equal";
6
6
  import "date-fns";
7
- import { useKnockMsTeamsClient as h } from "../context/KnockMsTeamsProvider.mjs";
8
- const k = "MS_TEAMS_CHANNELS";
9
- function S({
10
- teamId: e,
7
+ import { useKnockMsTeamsClient as E } from "../context/KnockMsTeamsProvider.mjs";
8
+ const M = "MS_TEAMS_CHANNELS";
9
+ function $({
10
+ teamId: s,
11
11
  queryOptions: n
12
12
  }) {
13
- const s = r(), {
14
- knockMsTeamsChannelId: a,
15
- tenantId: l
16
- } = h(), c = () => s.msTeams.getChannels({
17
- knockChannelId: a,
18
- tenant: l,
19
- teamId: e,
13
+ const r = S(), {
14
+ knockMsTeamsChannelId: e,
15
+ tenantId: t,
16
+ connectionStatus: c
17
+ } = E(), i = l(t), f = l(e), m = l(c), d = T(() => r.msTeams.getChannels({
18
+ knockChannelId: e,
19
+ tenant: t,
20
+ teamId: s,
20
21
  queryOptions: {
21
22
  $filter: n == null ? void 0 : n.filter,
22
23
  $select: n == null ? void 0 : n.select
23
24
  }
24
- }), {
25
- data: t,
26
- isLoading: o,
27
- isValidating: m,
28
- mutate: i
29
- } = f(e ? [k, e] : null, c, {
25
+ }), [r.msTeams, e, t, s, n]), {
26
+ data: o,
27
+ isLoading: C,
28
+ isValidating: h,
29
+ mutate: a
30
+ } = v(s && c === "connected" ? [M, t, e, s] : null, d, {
30
31
  revalidateOnFocus: !1
31
32
  });
32
- return {
33
- data: (t == null ? void 0 : t.ms_teams_channels) ?? [],
34
- isLoading: o || m,
35
- refetch: () => i()
33
+ return g(() => {
34
+ const u = i.current !== t, k = f.current !== e, R = !(m.current === "connected") && c === "connected";
35
+ (u || k || R) && a(void 0, {
36
+ revalidate: !1
37
+ }), i.current = t, f.current = e, m.current = c;
38
+ }, [t, e, c, a]), {
39
+ data: (o == null ? void 0 : o.ms_teams_channels) ?? [],
40
+ isLoading: C || h,
41
+ refetch: () => a()
36
42
  };
37
43
  }
38
44
  export {
39
- S as default
45
+ $ as default
40
46
  };
41
47
  //# sourceMappingURL=useMsTeamsChannels.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useMsTeamsChannels.mjs","sources":["../../../../../src/modules/ms-teams/hooks/useMsTeamsChannels.ts"],"sourcesContent":["import { GetMsTeamsChannelsResponse, MsTeamsChannel } from \"@knocklabs/client\";\nimport useSWR from \"swr\";\n\nimport { useKnockClient } from \"../../core\";\nimport { useKnockMsTeamsClient } from \"../context\";\nimport { MsTeamsChannelQueryOptions } from \"../interfaces\";\n\nconst QUERY_KEY = \"MS_TEAMS_CHANNELS\";\n\ntype UseMsTeamsChannelsProps = {\n teamId?: string;\n queryOptions?: MsTeamsChannelQueryOptions;\n};\n\ntype UseMsTeamsChannelsOutput = {\n data: MsTeamsChannel[];\n isLoading: boolean;\n refetch: () => void;\n};\n\nfunction useMsTeamsChannels({\n teamId,\n queryOptions,\n}: UseMsTeamsChannelsProps): UseMsTeamsChannelsOutput {\n const knock = useKnockClient();\n const { knockMsTeamsChannelId, tenantId } = useKnockMsTeamsClient();\n\n const fetchChannels = () =>\n knock.msTeams.getChannels({\n knockChannelId: knockMsTeamsChannelId,\n tenant: tenantId,\n teamId: teamId!,\n queryOptions: {\n $filter: queryOptions?.filter,\n $select: queryOptions?.select,\n },\n });\n\n const { data, isLoading, isValidating, mutate } =\n useSWR<GetMsTeamsChannelsResponse>(\n teamId ? [QUERY_KEY, teamId] : null,\n fetchChannels,\n { revalidateOnFocus: false },\n );\n\n return {\n data: data?.ms_teams_channels ?? [],\n isLoading: isLoading || isValidating,\n refetch: () => mutate(),\n };\n}\n\nexport default useMsTeamsChannels;\n"],"names":["QUERY_KEY","useMsTeamsChannels","teamId","queryOptions","knock","useKnockClient","knockMsTeamsChannelId","tenantId","useKnockMsTeamsClient","fetchChannels","msTeams","getChannels","knockChannelId","tenant","$filter","filter","$select","select","data","isLoading","isValidating","mutate","useSWR","revalidateOnFocus","ms_teams_channels","refetch"],"mappings":";;;;;;;AAOA,MAAMA,IAAY;AAalB,SAASC,EAAmB;AAAA,EAC1BC,QAAAA;AAAAA,EACAC,cAAAA;AACuB,GAA6B;AACpD,QAAMC,IAAQC,EAAe,GACvB;AAAA,IAAEC,uBAAAA;AAAAA,IAAuBC,UAAAA;AAAAA,MAAaC,EAAsB,GAE5DC,IAAgBA,MACpBL,EAAMM,QAAQC,YAAY;AAAA,IACxBC,gBAAgBN;AAAAA,IAChBO,QAAQN;AAAAA,IACRL,QAAAA;AAAAA,IACAC,cAAc;AAAA,MACZW,SAASX,KAAAA,gBAAAA,EAAcY;AAAAA,MACvBC,SAASb,KAAAA,gBAAAA,EAAcc;AAAAA,IAAAA;AAAAA,EACzB,CACD,GAEG;AAAA,IAAEC,MAAAA;AAAAA,IAAMC,WAAAA;AAAAA,IAAWC,cAAAA;AAAAA,IAAcC,QAAAA;AAAAA,EAAAA,IACrCC,EACEpB,IAAS,CAACF,GAAWE,CAAM,IAAI,MAC/BO,GACA;AAAA,IAAEc,mBAAmB;AAAA,EAAA,CACvB;AAEK,SAAA;AAAA,IACLL,OAAMA,KAAAA,gBAAAA,EAAMM,sBAAqB,CAAE;AAAA,IACnCL,WAAWA,KAAaC;AAAAA,IACxBK,SAASA,MAAMJ,EAAO;AAAA,EACxB;AACF;"}
1
+ {"version":3,"file":"useMsTeamsChannels.mjs","sources":["../../../../../src/modules/ms-teams/hooks/useMsTeamsChannels.ts"],"sourcesContent":["import { GetMsTeamsChannelsResponse, MsTeamsChannel } from \"@knocklabs/client\";\nimport { useCallback, useEffect, useRef } from \"react\";\nimport useSWR from \"swr\";\n\nimport { useKnockClient } from \"../../core\";\nimport { useKnockMsTeamsClient } from \"../context\";\nimport { MsTeamsChannelQueryOptions } from \"../interfaces\";\n\nconst QUERY_KEY = \"MS_TEAMS_CHANNELS\";\n\ntype UseMsTeamsChannelsProps = {\n teamId?: string;\n queryOptions?: MsTeamsChannelQueryOptions;\n};\n\ntype UseMsTeamsChannelsOutput = {\n data: MsTeamsChannel[];\n isLoading: boolean;\n refetch: () => void;\n};\n\nfunction useMsTeamsChannels({\n teamId,\n queryOptions,\n}: UseMsTeamsChannelsProps): UseMsTeamsChannelsOutput {\n const knock = useKnockClient();\n const { knockMsTeamsChannelId, tenantId, connectionStatus } =\n useKnockMsTeamsClient();\n\n // Track previous tenant/channel/connectionStatus to detect changes and clear cache\n const prevTenantRef = useRef(tenantId);\n const prevChannelRef = useRef(knockMsTeamsChannelId);\n const prevConnectionStatusRef = useRef(connectionStatus);\n\n const fetchChannels = useCallback(\n () =>\n knock.msTeams.getChannels({\n knockChannelId: knockMsTeamsChannelId,\n tenant: tenantId,\n teamId: teamId!,\n queryOptions: {\n $filter: queryOptions?.filter,\n $select: queryOptions?.select,\n },\n }),\n [knock.msTeams, knockMsTeamsChannelId, tenantId, teamId, queryOptions],\n );\n\n // Include tenantId and knockMsTeamsChannelId in the cache key so that\n // SWR treats different tenants as different cache entries\n const { data, isLoading, isValidating, mutate } =\n useSWR<GetMsTeamsChannelsResponse>(\n teamId && connectionStatus === \"connected\"\n ? [QUERY_KEY, tenantId, knockMsTeamsChannelId, teamId]\n : null,\n fetchChannels,\n { revalidateOnFocus: false },\n );\n\n // Clear cache when tenant, channel, or connection status changes\n // This ensures that when the user disconnects and reconnects (possibly to a different\n // MS Teams workspace), or when the access token is revoked, the cached channels are cleared\n useEffect(() => {\n const tenantChanged = prevTenantRef.current !== tenantId;\n const channelChanged = prevChannelRef.current !== knockMsTeamsChannelId;\n // Detect when connection is re-established (was not connected, now is connected)\n const wasConnected = prevConnectionStatusRef.current === \"connected\";\n const isConnected = connectionStatus === \"connected\";\n const connectionReestablished = !wasConnected && isConnected;\n\n if (tenantChanged || channelChanged || connectionReestablished) {\n // Reset the SWR state to clear cached data\n mutate(undefined, { revalidate: false });\n }\n\n prevTenantRef.current = tenantId;\n prevChannelRef.current = knockMsTeamsChannelId;\n prevConnectionStatusRef.current = connectionStatus;\n }, [tenantId, knockMsTeamsChannelId, connectionStatus, mutate]);\n\n return {\n data: data?.ms_teams_channels ?? [],\n isLoading: isLoading || isValidating,\n refetch: () => mutate(),\n };\n}\n\nexport default useMsTeamsChannels;\n"],"names":["QUERY_KEY","useMsTeamsChannels","teamId","queryOptions","knock","useKnockClient","knockMsTeamsChannelId","tenantId","connectionStatus","useKnockMsTeamsClient","prevTenantRef","useRef","prevChannelRef","prevConnectionStatusRef","fetchChannels","useCallback","msTeams","getChannels","knockChannelId","tenant","$filter","filter","$select","select","data","isLoading","isValidating","mutate","useSWR","revalidateOnFocus","useEffect","tenantChanged","current","channelChanged","connectionReestablished","undefined","revalidate","ms_teams_channels","refetch"],"mappings":";;;;;;;AAQA,MAAMA,IAAY;AAalB,SAASC,EAAmB;AAAA,EAC1BC,QAAAA;AAAAA,EACAC,cAAAA;AACuB,GAA6B;AACpD,QAAMC,IAAQC,EAAe,GACvB;AAAA,IAAEC,uBAAAA;AAAAA,IAAuBC,UAAAA;AAAAA,IAAUC,kBAAAA;AAAAA,MACvCC,EAAsB,GAGlBC,IAAgBC,EAAOJ,CAAQ,GAC/BK,IAAiBD,EAAOL,CAAqB,GAC7CO,IAA0BF,EAAOH,CAAgB,GAEjDM,IAAgBC,EACpB,MACEX,EAAMY,QAAQC,YAAY;AAAA,IACxBC,gBAAgBZ;AAAAA,IAChBa,QAAQZ;AAAAA,IACRL,QAAAA;AAAAA,IACAC,cAAc;AAAA,MACZiB,SAASjB,KAAAA,gBAAAA,EAAckB;AAAAA,MACvBC,SAASnB,KAAAA,gBAAAA,EAAcoB;AAAAA,IAAAA;AAAAA,EACzB,CACD,GACH,CAACnB,EAAMY,SAASV,GAAuBC,GAAUL,GAAQC,CAAY,CACvE,GAIM;AAAA,IAAEqB,MAAAA;AAAAA,IAAMC,WAAAA;AAAAA,IAAWC,cAAAA;AAAAA,IAAcC,QAAAA;AAAAA,EACrCC,IAAAA,EACE1B,KAAUM,MAAqB,cAC3B,CAACR,GAAWO,GAAUD,GAAuBJ,CAAM,IACnD,MACJY,GACA;AAAA,IAAEe,mBAAmB;AAAA,EAAA,CACvB;AAKFC,SAAAA,EAAU,MAAM;AACRC,UAAAA,IAAgBrB,EAAcsB,YAAYzB,GAC1C0B,IAAiBrB,EAAeoB,YAAY1B,GAI5C4B,IAA0B,EAFXrB,EAAwBmB,YAAY,gBACrCxB,MAAqB;AAGrCuB,KAAAA,KAAiBE,KAAkBC,MAErCP,EAAOQ,QAAW;AAAA,MAAEC,YAAY;AAAA,IAAA,CAAO,GAGzC1B,EAAcsB,UAAUzB,GACxBK,EAAeoB,UAAU1B,GACzBO,EAAwBmB,UAAUxB;AAAAA,KACjC,CAACD,GAAUD,GAAuBE,GAAkBmB,CAAM,CAAC,GAEvD;AAAA,IACLH,OAAMA,KAAAA,gBAAAA,EAAMa,sBAAqB,CAAE;AAAA,IACnCZ,WAAWA,KAAaC;AAAAA,IACxBY,SAASA,MAAMX,EAAO;AAAA,EACxB;AACF;"}
@@ -1,51 +1,55 @@
1
- import { useMemo as C, useEffect as S } from "react";
2
- import g from "swr/infinite";
3
- import { useKnockClient as E } from "../../core/context/KnockProvider.mjs";
1
+ import { useRef as r, useCallback as E, useEffect as x, useMemo as A } from "react";
2
+ import b from "swr/infinite";
3
+ import { useKnockClient as v } from "../../core/context/KnockProvider.mjs";
4
4
  import "@knocklabs/client";
5
5
  import "fast-deep-equal";
6
6
  import "date-fns";
7
- import { useKnockMsTeamsClient as x } from "../context/KnockMsTeamsProvider.mjs";
8
- const I = 1e3, T = "MS_TEAMS_TEAMS";
9
- function $(t, n) {
10
- return t === 0 ? [T, ""] : n && ["", null].includes(n.skip_token) ? null : [T, n.skip_token ?? ""];
11
- }
12
- function R({
7
+ import { useKnockMsTeamsClient as w } from "../context/KnockMsTeamsProvider.mjs";
8
+ const z = 1e3, I = "MS_TEAMS_TEAMS";
9
+ function j({
13
10
  queryOptions: t = {}
14
11
  }) {
15
- const n = E(), {
16
- knockMsTeamsChannelId: M,
17
- tenantId: _,
18
- connectionStatus: l
19
- } = x(), r = (e) => n.msTeams.getTeams({
20
- knockChannelId: M,
21
- tenant: _,
12
+ const k = v(), {
13
+ knockMsTeamsChannelId: e,
14
+ tenantId: c,
15
+ connectionStatus: s
16
+ } = w(), C = r(c), h = r(e), T = r(s), $ = E((n, o) => s !== "connected" ? null : n === 0 ? [I, c, e, ""] : o && ["", null].includes(o.skip_token) ? null : [I, c, e, (o == null ? void 0 : o.skip_token) ?? ""], [c, e, s]), g = E((n) => k.msTeams.getTeams({
17
+ knockChannelId: e,
18
+ tenant: c,
22
19
  queryOptions: {
23
- $skiptoken: e == null ? void 0 : e[1],
20
+ $skiptoken: n == null ? void 0 : n[3],
24
21
  $top: t == null ? void 0 : t.limitPerPage,
25
22
  $filter: t == null ? void 0 : t.filter,
26
23
  $select: t == null ? void 0 : t.select
27
24
  }
28
- }), {
29
- data: a,
30
- error: o,
31
- isLoading: s,
32
- isValidating: c,
33
- setSize: i,
34
- mutate: h
35
- } = g($, r, {
25
+ }), [k.msTeams, e, c, t]), {
26
+ data: l,
27
+ error: M,
28
+ isLoading: m,
29
+ isValidating: f,
30
+ setSize: a,
31
+ mutate: i
32
+ } = b($, g, {
36
33
  initialSize: 0,
37
34
  revalidateOnFocus: !1,
38
35
  revalidateFirstPage: !1
39
- }), f = a == null ? void 0 : a.at(-1), k = f === void 0 || !!f.skip_token, m = C(() => (a ?? []).flatMap((e) => e == null ? void 0 : e.ms_teams_teams).filter((e) => !!e), [a]), d = (t == null ? void 0 : t.maxCount) || I;
40
- return S(() => {
41
- l === "connected" && !o && k && !s && !c && m.length < d && i((e) => e + 1);
42
- }, [m.length, i, k, s, c, d, o, l]), {
43
- data: m,
44
- isLoading: s || c,
45
- refetch: () => h()
36
+ });
37
+ x(() => {
38
+ const n = C.current !== c, o = h.current !== e, u = !(T.current === "connected") && s === "connected";
39
+ (n || o || u) && (i(void 0, {
40
+ revalidate: !1
41
+ }), a(0)), C.current = c, h.current = e, T.current = s;
42
+ }, [c, e, s, i, a]);
43
+ const _ = l == null ? void 0 : l.at(-1), S = _ === void 0 || !!_.skip_token, d = A(() => (l ?? []).flatMap((n) => n == null ? void 0 : n.ms_teams_teams).filter((n) => !!n), [l]), R = (t == null ? void 0 : t.maxCount) || z;
44
+ return x(() => {
45
+ s === "connected" && !M && S && !m && !f && d.length < R && a((n) => n + 1);
46
+ }, [d.length, a, S, m, f, R, M, s]), {
47
+ data: d,
48
+ isLoading: m || f,
49
+ refetch: () => i()
46
50
  };
47
51
  }
48
52
  export {
49
- R as default
53
+ j as default
50
54
  };
51
55
  //# sourceMappingURL=useMsTeamsTeams.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useMsTeamsTeams.mjs","sources":["../../../../../src/modules/ms-teams/hooks/useMsTeamsTeams.ts"],"sourcesContent":["import { GetMsTeamsTeamsResponse, MsTeamsTeam } from \"@knocklabs/client\";\nimport { useEffect, useMemo } from \"react\";\nimport useSWRInfinite from \"swr/infinite\";\n\nimport { useKnockClient } from \"../../core\";\nimport { useKnockMsTeamsClient } from \"../context\";\nimport { MsTeamsTeamQueryOptions } from \"../interfaces\";\n\nconst MAX_COUNT = 1000;\n\nconst QUERY_KEY = \"MS_TEAMS_TEAMS\";\n\ntype UseMsTeamsTeamsOptions = {\n queryOptions?: MsTeamsTeamQueryOptions;\n};\n\ntype UseMsTeamsTeamsOutput = {\n data: MsTeamsTeam[];\n isLoading: boolean;\n refetch: () => void;\n};\n\ntype QueryKey = [key: string, skiptoken: string] | null;\n\nfunction getQueryKey(\n pageIndex: number,\n previousPageData: GetMsTeamsTeamsResponse,\n): QueryKey {\n // First page so just pass empty\n if (pageIndex === 0) {\n return [QUERY_KEY, \"\"];\n }\n\n // If there's no more data then return an empty next skiptoken\n if (previousPageData && [\"\", null].includes(previousPageData.skip_token)) {\n return null;\n }\n\n // Next skiptoken exists so pass it\n return [QUERY_KEY, previousPageData.skip_token ?? \"\"];\n}\n\nfunction useMsTeamsTeams({\n queryOptions = {},\n}: UseMsTeamsTeamsOptions): UseMsTeamsTeamsOutput {\n const knock = useKnockClient();\n const { knockMsTeamsChannelId, tenantId, connectionStatus } =\n useKnockMsTeamsClient();\n\n const fetchTeams = (queryKey: QueryKey) =>\n knock.msTeams.getTeams({\n knockChannelId: knockMsTeamsChannelId,\n tenant: tenantId,\n queryOptions: {\n $skiptoken: queryKey?.[1],\n $top: queryOptions?.limitPerPage,\n $filter: queryOptions?.filter,\n $select: queryOptions?.select,\n },\n });\n\n const { data, error, isLoading, isValidating, setSize, mutate } =\n useSWRInfinite<GetMsTeamsTeamsResponse>(getQueryKey, fetchTeams, {\n initialSize: 0,\n revalidateOnFocus: false,\n revalidateFirstPage: false,\n });\n\n const lastPage = data?.at(-1);\n const hasNextPage = lastPage === undefined || !!lastPage.skip_token;\n\n const teams = useMemo(\n () =>\n (data ?? [])\n .flatMap((page) => page?.ms_teams_teams)\n .filter((team) => !!team),\n [data],\n );\n\n const maxCount = queryOptions?.maxCount || MAX_COUNT;\n\n useEffect(() => {\n if (\n connectionStatus === \"connected\" &&\n !error &&\n hasNextPage &&\n !isLoading &&\n !isValidating &&\n teams.length < maxCount\n ) {\n // Fetch a page at a time until we have nothing else left to fetch\n // or we've already hit the max amount of teams to fetch\n setSize((size) => size + 1);\n }\n }, [\n teams.length,\n setSize,\n hasNextPage,\n isLoading,\n isValidating,\n maxCount,\n error,\n connectionStatus,\n ]);\n\n return {\n data: teams,\n isLoading: isLoading || isValidating,\n refetch: () => mutate(),\n };\n}\n\nexport default useMsTeamsTeams;\n"],"names":["MAX_COUNT","QUERY_KEY","getQueryKey","pageIndex","previousPageData","includes","skip_token","useMsTeamsTeams","queryOptions","knock","useKnockClient","knockMsTeamsChannelId","tenantId","connectionStatus","useKnockMsTeamsClient","fetchTeams","queryKey","msTeams","getTeams","knockChannelId","tenant","$skiptoken","$top","limitPerPage","$filter","filter","$select","select","data","error","isLoading","isValidating","setSize","mutate","useSWRInfinite","initialSize","revalidateOnFocus","revalidateFirstPage","lastPage","at","hasNextPage","undefined","teams","useMemo","flatMap","page","ms_teams_teams","team","maxCount","useEffect","length","size","refetch"],"mappings":";;;;;;;AAQA,MAAMA,IAAY,KAEZC,IAAY;AAclB,SAASC,EACPC,GACAC,GACU;AAEV,SAAID,MAAc,IACT,CAACF,GAAW,EAAE,IAInBG,KAAoB,CAAC,IAAI,IAAI,EAAEC,SAASD,EAAiBE,UAAU,IAC9D,OAIF,CAACL,GAAWG,EAAiBE,cAAc,EAAE;AACtD;AAEA,SAASC,EAAgB;AAAA,EACvBC,cAAAA,IAAe,CAAA;AACO,GAA0B;AAChD,QAAMC,IAAQC,EAAe,GACvB;AAAA,IAAEC,uBAAAA;AAAAA,IAAuBC,UAAAA;AAAAA,IAAUC,kBAAAA;AAAAA,MACvCC,EAAsB,GAElBC,IAAaA,CAACC,MAClBP,EAAMQ,QAAQC,SAAS;AAAA,IACrBC,gBAAgBR;AAAAA,IAChBS,QAAQR;AAAAA,IACRJ,cAAc;AAAA,MACZa,YAAYL,KAAAA,gBAAAA,EAAW;AAAA,MACvBM,MAAMd,KAAAA,gBAAAA,EAAce;AAAAA,MACpBC,SAAShB,KAAAA,gBAAAA,EAAciB;AAAAA,MACvBC,SAASlB,KAAAA,gBAAAA,EAAcmB;AAAAA,IAAAA;AAAAA,EACzB,CACD,GAEG;AAAA,IAAEC,MAAAA;AAAAA,IAAMC,OAAAA;AAAAA,IAAOC,WAAAA;AAAAA,IAAWC,cAAAA;AAAAA,IAAcC,SAAAA;AAAAA,IAASC,QAAAA;AAAAA,EAAAA,IACrDC,EAAwChC,GAAaa,GAAY;AAAA,IAC/DoB,aAAa;AAAA,IACbC,mBAAmB;AAAA,IACnBC,qBAAqB;AAAA,EAAA,CACtB,GAEGC,IAAWV,KAAAA,gBAAAA,EAAMW,GAAG,KACpBC,IAAcF,MAAaG,UAAa,CAAC,CAACH,EAAShC,YAEnDoC,IAAQC,EACZ,OACGf,KAAQ,IACNgB,QAASC,CAASA,MAAAA,KAAAA,gBAAAA,EAAMC,cAAc,EACtCrB,OAAQsB,OAAS,CAAC,CAACA,CAAI,GAC5B,CAACnB,CAAI,CACP,GAEMoB,KAAWxC,KAAAA,gBAAAA,EAAcwC,aAAYhD;AAE3CiD,SAAAA,EAAU,MAAM;AAEZpC,IAAAA,MAAqB,eACrB,CAACgB,KACDW,KACA,CAACV,KACD,CAACC,KACDW,EAAMQ,SAASF,KAING,EAAAA,CAAAA,MAASA,IAAO,CAAC;AAAA,EAE9B,GAAG,CACDT,EAAMQ,QACNlB,GACAQ,GACAV,GACAC,GACAiB,GACAnB,GACAhB,CAAgB,CACjB,GAEM;AAAA,IACLe,MAAMc;AAAAA,IACNZ,WAAWA,KAAaC;AAAAA,IACxBqB,SAASA,MAAMnB,EAAO;AAAA,EACxB;AACF;"}
1
+ {"version":3,"file":"useMsTeamsTeams.mjs","sources":["../../../../../src/modules/ms-teams/hooks/useMsTeamsTeams.ts"],"sourcesContent":["import { GetMsTeamsTeamsResponse, MsTeamsTeam } from \"@knocklabs/client\";\nimport { useCallback, useEffect, useMemo, useRef } from \"react\";\nimport useSWRInfinite from \"swr/infinite\";\n\nimport { useKnockClient } from \"../../core\";\nimport { useKnockMsTeamsClient } from \"../context\";\nimport { MsTeamsTeamQueryOptions } from \"../interfaces\";\n\nconst MAX_COUNT = 1000;\n\nconst QUERY_KEY = \"MS_TEAMS_TEAMS\";\n\ntype UseMsTeamsTeamsOptions = {\n queryOptions?: MsTeamsTeamQueryOptions;\n};\n\ntype UseMsTeamsTeamsOutput = {\n data: MsTeamsTeam[];\n isLoading: boolean;\n refetch: () => void;\n};\n\ntype QueryKey =\n | [key: string, tenantId: string, channelId: string, skiptoken: string]\n | null;\n\nfunction useMsTeamsTeams({\n queryOptions = {},\n}: UseMsTeamsTeamsOptions): UseMsTeamsTeamsOutput {\n const knock = useKnockClient();\n const { knockMsTeamsChannelId, tenantId, connectionStatus } =\n useKnockMsTeamsClient();\n\n // Track previous tenant/channel/connectionStatus to detect changes and clear cache\n const prevTenantRef = useRef(tenantId);\n const prevChannelRef = useRef(knockMsTeamsChannelId);\n const prevConnectionStatusRef = useRef(connectionStatus);\n\n // Create a getQueryKey function that includes tenantId and knockMsTeamsChannelId\n // so that SWR treats different tenants as different cache entries\n const getQueryKey = useCallback(\n (\n pageIndex: number,\n previousPageData: GetMsTeamsTeamsResponse | null,\n ): QueryKey => {\n // Don't fetch if not connected\n if (connectionStatus !== \"connected\") {\n return null;\n }\n\n // First page so just pass empty\n if (pageIndex === 0) {\n return [QUERY_KEY, tenantId, knockMsTeamsChannelId, \"\"];\n }\n\n // If there's no more data then return an empty next skiptoken\n if (\n previousPageData &&\n [\"\", null].includes(previousPageData.skip_token)\n ) {\n return null;\n }\n\n // Next skiptoken exists so pass it\n return [\n QUERY_KEY,\n tenantId,\n knockMsTeamsChannelId,\n previousPageData?.skip_token ?? \"\",\n ];\n },\n [tenantId, knockMsTeamsChannelId, connectionStatus],\n );\n\n const fetchTeams = useCallback(\n (queryKey: QueryKey) =>\n knock.msTeams.getTeams({\n knockChannelId: knockMsTeamsChannelId,\n tenant: tenantId,\n queryOptions: {\n $skiptoken: queryKey?.[3],\n $top: queryOptions?.limitPerPage,\n $filter: queryOptions?.filter,\n $select: queryOptions?.select,\n },\n }),\n [knock.msTeams, knockMsTeamsChannelId, tenantId, queryOptions],\n );\n\n const { data, error, isLoading, isValidating, setSize, mutate } =\n useSWRInfinite<GetMsTeamsTeamsResponse>(getQueryKey, fetchTeams, {\n initialSize: 0,\n revalidateOnFocus: false,\n revalidateFirstPage: false,\n });\n\n // Clear cache when tenant, channel, or connection status changes\n // This ensures that when the user disconnects and reconnects (possibly to a different\n // MS Teams workspace), or when the access token is revoked, the cached teams are cleared\n useEffect(() => {\n const tenantChanged = prevTenantRef.current !== tenantId;\n const channelChanged = prevChannelRef.current !== knockMsTeamsChannelId;\n // Detect when connection is re-established (was not connected, now is connected)\n const wasConnected = prevConnectionStatusRef.current === \"connected\";\n const isConnected = connectionStatus === \"connected\";\n const connectionReestablished = !wasConnected && isConnected;\n\n if (tenantChanged || channelChanged || connectionReestablished) {\n // Reset the SWR state to clear cached data\n mutate(undefined, { revalidate: false });\n setSize(0);\n }\n\n prevTenantRef.current = tenantId;\n prevChannelRef.current = knockMsTeamsChannelId;\n prevConnectionStatusRef.current = connectionStatus;\n }, [tenantId, knockMsTeamsChannelId, connectionStatus, mutate, setSize]);\n\n const lastPage = data?.at(-1);\n const hasNextPage = lastPage === undefined || !!lastPage.skip_token;\n\n const teams = useMemo(\n () =>\n (data ?? [])\n .flatMap((page) => page?.ms_teams_teams)\n .filter((team) => !!team),\n [data],\n );\n\n const maxCount = queryOptions?.maxCount || MAX_COUNT;\n\n useEffect(() => {\n if (\n connectionStatus === \"connected\" &&\n !error &&\n hasNextPage &&\n !isLoading &&\n !isValidating &&\n teams.length < maxCount\n ) {\n // Fetch a page at a time until we have nothing else left to fetch\n // or we've already hit the max amount of teams to fetch\n setSize((size) => size + 1);\n }\n }, [\n teams.length,\n setSize,\n hasNextPage,\n isLoading,\n isValidating,\n maxCount,\n error,\n connectionStatus,\n ]);\n\n return {\n data: teams,\n isLoading: isLoading || isValidating,\n refetch: () => mutate(),\n };\n}\n\nexport default useMsTeamsTeams;\n"],"names":["MAX_COUNT","QUERY_KEY","useMsTeamsTeams","queryOptions","knock","useKnockClient","knockMsTeamsChannelId","tenantId","connectionStatus","useKnockMsTeamsClient","prevTenantRef","useRef","prevChannelRef","prevConnectionStatusRef","getQueryKey","useCallback","pageIndex","previousPageData","includes","skip_token","fetchTeams","queryKey","msTeams","getTeams","knockChannelId","tenant","$skiptoken","$top","limitPerPage","$filter","filter","$select","select","data","error","isLoading","isValidating","setSize","mutate","useSWRInfinite","initialSize","revalidateOnFocus","revalidateFirstPage","useEffect","tenantChanged","current","channelChanged","connectionReestablished","undefined","revalidate","lastPage","at","hasNextPage","teams","useMemo","flatMap","page","ms_teams_teams","team","maxCount","length","size","refetch"],"mappings":";;;;;;;AAQA,MAAMA,IAAY,KAEZC,IAAY;AAgBlB,SAASC,EAAgB;AAAA,EACvBC,cAAAA,IAAe,CAAA;AACO,GAA0B;AAChD,QAAMC,IAAQC,EAAe,GACvB;AAAA,IAAEC,uBAAAA;AAAAA,IAAuBC,UAAAA;AAAAA,IAAUC,kBAAAA;AAAAA,MACvCC,EAAsB,GAGlBC,IAAgBC,EAAOJ,CAAQ,GAC/BK,IAAiBD,EAAOL,CAAqB,GAC7CO,IAA0BF,EAAOH,CAAgB,GAIjDM,IAAcC,EAClB,CACEC,GACAC,MAGIT,MAAqB,cAChB,OAILQ,MAAc,IACT,CAACf,GAAWM,GAAUD,GAAuB,EAAE,IAKtDW,KACA,CAAC,IAAI,IAAI,EAAEC,SAASD,EAAiBE,UAAU,IAExC,OAIF,CACLlB,GACAM,GACAD,IACAW,KAAAA,gBAAAA,EAAkBE,eAAc,EAAE,GAGtC,CAACZ,GAAUD,GAAuBE,CAAgB,CACpD,GAEMY,IAAaL,EACjB,CAACM,MACCjB,EAAMkB,QAAQC,SAAS;AAAA,IACrBC,gBAAgBlB;AAAAA,IAChBmB,QAAQlB;AAAAA,IACRJ,cAAc;AAAA,MACZuB,YAAYL,KAAAA,gBAAAA,EAAW;AAAA,MACvBM,MAAMxB,KAAAA,gBAAAA,EAAcyB;AAAAA,MACpBC,SAAS1B,KAAAA,gBAAAA,EAAc2B;AAAAA,MACvBC,SAAS5B,KAAAA,gBAAAA,EAAc6B;AAAAA,IAAAA;AAAAA,EACzB,CACD,GACH,CAAC5B,EAAMkB,SAAShB,GAAuBC,GAAUJ,CAAY,CAC/D,GAEM;AAAA,IAAE8B,MAAAA;AAAAA,IAAMC,OAAAA;AAAAA,IAAOC,WAAAA;AAAAA,IAAWC,cAAAA;AAAAA,IAAcC,SAAAA;AAAAA,IAASC,QAAAA;AAAAA,EAAAA,IACrDC,EAAwCzB,GAAaM,GAAY;AAAA,IAC/DoB,aAAa;AAAA,IACbC,mBAAmB;AAAA,IACnBC,qBAAqB;AAAA,EAAA,CACtB;AAKHC,EAAAA,EAAU,MAAM;AACRC,UAAAA,IAAgBlC,EAAcmC,YAAYtC,GAC1CuC,IAAiBlC,EAAeiC,YAAYvC,GAI5CyC,IAA0B,EAFXlC,EAAwBgC,YAAY,gBACrCrC,MAAqB;AAGrCoC,KAAAA,KAAiBE,KAAkBC,OAErCT,EAAOU,QAAW;AAAA,MAAEC,YAAY;AAAA,IAAA,CAAO,GACvCZ,EAAQ,CAAC,IAGX3B,EAAcmC,UAAUtC,GACxBK,EAAeiC,UAAUvC,GACzBO,EAAwBgC,UAAUrC;AAAAA,EAAAA,GACjC,CAACD,GAAUD,GAAuBE,GAAkB8B,GAAQD,CAAO,CAAC;AAEjEa,QAAAA,IAAWjB,KAAAA,gBAAAA,EAAMkB,GAAG,KACpBC,IAAcF,MAAaF,UAAa,CAAC,CAACE,EAAS/B,YAEnDkC,IAAQC,EACZ,OACGrB,KAAQ,IACNsB,QAASC,CAASA,MAAAA,KAAAA,gBAAAA,EAAMC,cAAc,EACtC3B,OAAQ4B,OAAS,CAAC,CAACA,CAAI,GAC5B,CAACzB,CAAI,CACP,GAEM0B,KAAWxD,KAAAA,gBAAAA,EAAcwD,aAAY3D;AAE3C2C,SAAAA,EAAU,MAAM;AAEZnC,IAAAA,MAAqB,eACrB,CAAC0B,KACDkB,KACA,CAACjB,KACD,CAACC,KACDiB,EAAMO,SAASD,KAINE,EAAAA,CAAAA,MAASA,IAAO,CAAC;AAAA,EAE9B,GAAG,CACDR,EAAMO,QACNvB,GACAe,GACAjB,GACAC,GACAuB,GACAzB,GACA1B,CAAgB,CACjB,GAEM;AAAA,IACLyB,MAAMoB;AAAAA,IACNlB,WAAWA,KAAaC;AAAAA,IACxB0B,SAASA,MAAMxB,EAAO;AAAA,EACxB;AACF;"}
@@ -1,52 +1,56 @@
1
- import { useKnockSlackClient as E } from "../context/KnockSlackProvider.mjs";
2
- import { useMemo as u, useEffect as x } from "react";
1
+ import { useKnockSlackClient as T } from "../context/KnockSlackProvider.mjs";
2
+ import { useRef as C, useCallback as x, useEffect as I, useMemo as b } from "react";
3
3
  import "../../i18n/context/KnockI18nProvider.mjs";
4
- import I from "swr/infinite";
5
- import { useKnockClient as L } from "../../core/context/KnockProvider.mjs";
4
+ import g from "swr/infinite";
5
+ import { useKnockClient as Y } from "../../core/context/KnockProvider.mjs";
6
6
  import "@knocklabs/client";
7
7
  import "fast-deep-equal";
8
8
  import "date-fns";
9
9
  import "swr";
10
- const N = 1e3, P = 200, g = "private_channel,public_channel", k = "SLACK_CHANNELS";
11
- function A(n, c) {
12
- return n === 0 ? [k, ""] : c && ["", null].includes(c.next_cursor) ? null : [k, c.next_cursor ?? ""];
13
- }
14
- function v({
15
- queryOptions: n
10
+ const v = 1e3, w = 200, z = "private_channel,public_channel", L = "SLACK_CHANNELS";
11
+ function B({
12
+ queryOptions: t
16
13
  }) {
17
- const c = L(), {
18
- knockSlackChannelId: C,
19
- tenantId: _,
20
- connectionStatus: s
21
- } = E(), d = (t) => c.slack.getChannels({
22
- tenant: _,
23
- knockChannelId: C,
14
+ const h = Y(), {
15
+ knockSlackChannelId: c,
16
+ tenantId: e,
17
+ connectionStatus: o
18
+ } = T(), m = C(e), k = C(c), u = C(o), N = x((n, l) => o !== "connected" ? null : n === 0 ? [L, e, c, ""] : l && ["", null].includes(l.next_cursor) ? null : [L, e, c, (l == null ? void 0 : l.next_cursor) ?? ""], [e, c, o]), A = x((n) => h.slack.getChannels({
19
+ tenant: e,
20
+ knockChannelId: c,
24
21
  queryOptions: {
25
- ...n,
26
- cursor: t == null ? void 0 : t[1],
27
- limit: (n == null ? void 0 : n.limitPerPage) || P,
28
- types: (n == null ? void 0 : n.types) || g
22
+ ...t,
23
+ cursor: n == null ? void 0 : n[3],
24
+ limit: (t == null ? void 0 : t.limitPerPage) || w,
25
+ types: (t == null ? void 0 : t.types) || z
29
26
  }
30
- }), {
31
- data: l,
32
- error: i,
33
- isLoading: e,
34
- isValidating: a,
35
- setSize: r,
36
- mutate: S
37
- } = I(A, d, {
27
+ }), [h.slack, e, c, t]), {
28
+ data: s,
29
+ error: _,
30
+ isLoading: r,
31
+ isValidating: i,
32
+ setSize: a,
33
+ mutate: f
34
+ } = g(N, A, {
38
35
  initialSize: 0,
39
36
  revalidateFirstPage: !1
40
- }), m = l == null ? void 0 : l.at(-1), f = m === void 0 || !!m.next_cursor, o = u(() => (l ?? []).flatMap((t) => t == null ? void 0 : t.slack_channels).filter((t) => !!t), [l]), h = (n == null ? void 0 : n.maxCount) || N;
41
- return x(() => {
42
- s === "connected" && !i && f && !e && !a && o.length < h && r((t) => t + 1);
43
- }, [o.length, r, f, e, a, h, i, s]), {
44
- data: o,
45
- isLoading: e || a,
46
- refetch: () => S()
37
+ });
38
+ I(() => {
39
+ const n = m.current !== e, l = k.current !== c, M = !(u.current === "connected") && o === "connected";
40
+ (n || l || M) && (f(void 0, {
41
+ revalidate: !1
42
+ }), a(0)), m.current = e, k.current = c, u.current = o;
43
+ }, [e, c, o, f, a]);
44
+ const S = s == null ? void 0 : s.at(-1), E = S === void 0 || !!S.next_cursor, d = b(() => (s ?? []).flatMap((n) => n == null ? void 0 : n.slack_channels).filter((n) => !!n), [s]), R = (t == null ? void 0 : t.maxCount) || v;
45
+ return I(() => {
46
+ o === "connected" && !_ && E && !r && !i && d.length < R && a((n) => n + 1);
47
+ }, [d.length, a, E, r, i, R, _, o]), {
48
+ data: d,
49
+ isLoading: r || i,
50
+ refetch: () => f()
47
51
  };
48
52
  }
49
53
  export {
50
- v as default
54
+ B as default
51
55
  };
52
56
  //# sourceMappingURL=useSlackChannels.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useSlackChannels.mjs","sources":["../../../../../src/modules/slack/hooks/useSlackChannels.ts"],"sourcesContent":["import { SlackChannelQueryOptions, useKnockSlackClient } from \"..\";\nimport { GetSlackChannelsResponse, SlackChannel } from \"@knocklabs/client\";\nimport { useEffect, useMemo } from \"react\";\nimport useSWRInfinite from \"swr/infinite\";\n\nimport { useKnockClient } from \"../../core\";\n\nconst MAX_COUNT = 1000;\nconst LIMIT_PER_PAGE = 200;\nconst CHANNEL_TYPES = \"private_channel,public_channel\";\n\nconst QUERY_KEY = \"SLACK_CHANNELS\";\n\ntype UseSlackChannelsOptions = {\n queryOptions?: SlackChannelQueryOptions;\n};\n\ntype UseSlackChannelOutput = {\n data: SlackChannel[];\n isLoading: boolean;\n refetch: () => void;\n};\n\ntype QueryKey = [key: string, cursor: string] | null;\n\nfunction getQueryKey(\n pageIndex: number,\n previousPageData: GetSlackChannelsResponse,\n): QueryKey {\n // First page so just pass empty\n if (pageIndex === 0) {\n return [QUERY_KEY, \"\"];\n }\n\n // If there's no more data then return an empty next cursor\n if (previousPageData && [\"\", null].includes(previousPageData.next_cursor)) {\n return null;\n }\n\n // Next cursor exists so pass it\n return [QUERY_KEY, previousPageData.next_cursor ?? \"\"];\n}\n\nfunction useSlackChannels({\n queryOptions,\n}: UseSlackChannelsOptions): UseSlackChannelOutput {\n const knock = useKnockClient();\n const { knockSlackChannelId, tenantId, connectionStatus } =\n useKnockSlackClient();\n\n const fetchChannels = (queryKey: QueryKey) => {\n return knock.slack.getChannels({\n tenant: tenantId,\n knockChannelId: knockSlackChannelId,\n queryOptions: {\n ...queryOptions,\n cursor: queryKey?.[1],\n limit: queryOptions?.limitPerPage || LIMIT_PER_PAGE,\n types: queryOptions?.types || CHANNEL_TYPES,\n },\n });\n };\n\n const { data, error, isLoading, isValidating, setSize, mutate } =\n useSWRInfinite<GetSlackChannelsResponse>(getQueryKey, fetchChannels, {\n initialSize: 0,\n revalidateFirstPage: false,\n });\n\n const lastPage = data?.at(-1);\n const hasNextPage = lastPage === undefined || !!lastPage.next_cursor;\n\n const slackChannels: SlackChannel[] = useMemo(\n () =>\n (data ?? [])\n .flatMap((page) => page?.slack_channels)\n .filter((channel) => !!channel),\n [data],\n );\n\n const maxCount = queryOptions?.maxCount || MAX_COUNT;\n\n useEffect(() => {\n if (\n connectionStatus === \"connected\" &&\n !error &&\n hasNextPage &&\n !isLoading &&\n !isValidating &&\n slackChannels.length < maxCount\n ) {\n // Fetch a page at a time until we have nothing else left to fetch\n // or we've already hit the max amount of channels to fetch\n setSize((size) => size + 1);\n }\n }, [\n slackChannels.length,\n setSize,\n hasNextPage,\n isLoading,\n isValidating,\n maxCount,\n error,\n connectionStatus,\n ]);\n\n return {\n data: slackChannels,\n isLoading: isLoading || isValidating,\n refetch: () => mutate(),\n };\n}\n\nexport default useSlackChannels;\n"],"names":["MAX_COUNT","LIMIT_PER_PAGE","CHANNEL_TYPES","QUERY_KEY","getQueryKey","pageIndex","previousPageData","includes","next_cursor","useSlackChannels","queryOptions","knock","useKnockClient","knockSlackChannelId","tenantId","connectionStatus","useKnockSlackClient","fetchChannels","queryKey","slack","getChannels","tenant","knockChannelId","cursor","limit","limitPerPage","types","data","error","isLoading","isValidating","setSize","mutate","useSWRInfinite","initialSize","revalidateFirstPage","lastPage","at","hasNextPage","undefined","slackChannels","useMemo","flatMap","page","slack_channels","filter","channel","maxCount","useEffect","length","size","refetch"],"mappings":";;;;;;;;;AAOA,MAAMA,IAAY,KACZC,IAAiB,KACjBC,IAAgB,kCAEhBC,IAAY;AAclB,SAASC,EACPC,GACAC,GACU;AAEV,SAAID,MAAc,IACT,CAACF,GAAW,EAAE,IAInBG,KAAoB,CAAC,IAAI,IAAI,EAAEC,SAASD,EAAiBE,WAAW,IAC/D,OAIF,CAACL,GAAWG,EAAiBE,eAAe,EAAE;AACvD;AAEA,SAASC,EAAiB;AAAA,EACxBC,cAAAA;AACuB,GAA0B;AACjD,QAAMC,IAAQC,EAAe,GACvB;AAAA,IAAEC,qBAAAA;AAAAA,IAAqBC,UAAAA;AAAAA,IAAUC,kBAAAA;AAAAA,MACrCC,EAAoB,GAEhBC,IAAgBA,CAACC,MACdP,EAAMQ,MAAMC,YAAY;AAAA,IAC7BC,QAAQP;AAAAA,IACRQ,gBAAgBT;AAAAA,IAChBH,cAAc;AAAA,MACZ,GAAGA;AAAAA,MACHa,QAAQL,KAAAA,gBAAAA,EAAW;AAAA,MACnBM,QAAOd,KAAAA,gBAAAA,EAAce,iBAAgBxB;AAAAA,MACrCyB,QAAOhB,KAAAA,gBAAAA,EAAcgB,UAASxB;AAAAA,IAAAA;AAAAA,EAChC,CACD,GAGG;AAAA,IAAEyB,MAAAA;AAAAA,IAAMC,OAAAA;AAAAA,IAAOC,WAAAA;AAAAA,IAAWC,cAAAA;AAAAA,IAAcC,SAAAA;AAAAA,IAASC,QAAAA;AAAAA,EAAAA,IACrDC,EAAyC7B,GAAaa,GAAe;AAAA,IACnEiB,aAAa;AAAA,IACbC,qBAAqB;AAAA,EAAA,CACtB,GAEGC,IAAWT,KAAAA,gBAAAA,EAAMU,GAAG,KACpBC,IAAcF,MAAaG,UAAa,CAAC,CAACH,EAAS5B,aAEnDgC,IAAgCC,EACpC,OACGd,KAAQ,IACNe,QAASC,CAASA,MAAAA,KAAAA,gBAAAA,EAAMC,cAAc,EACtCC,OAAQC,OAAY,CAAC,CAACA,CAAO,GAClC,CAACnB,CAAI,CACP,GAEMoB,KAAWrC,KAAAA,gBAAAA,EAAcqC,aAAY/C;AAE3CgD,SAAAA,EAAU,MAAM;AAEZjC,IAAAA,MAAqB,eACrB,CAACa,KACDU,KACA,CAACT,KACD,CAACC,KACDU,EAAcS,SAASF,KAIdG,EAAAA,CAAAA,MAASA,IAAO,CAAC;AAAA,EAE9B,GAAG,CACDV,EAAcS,QACdlB,GACAO,GACAT,GACAC,GACAiB,GACAnB,GACAb,CAAgB,CACjB,GAEM;AAAA,IACLY,MAAMa;AAAAA,IACNX,WAAWA,KAAaC;AAAAA,IACxBqB,SAASA,MAAMnB,EAAO;AAAA,EACxB;AACF;"}
1
+ {"version":3,"file":"useSlackChannels.mjs","sources":["../../../../../src/modules/slack/hooks/useSlackChannels.ts"],"sourcesContent":["import { SlackChannelQueryOptions, useKnockSlackClient } from \"..\";\nimport { GetSlackChannelsResponse, SlackChannel } from \"@knocklabs/client\";\nimport { useCallback, useEffect, useMemo, useRef } from \"react\";\nimport useSWRInfinite from \"swr/infinite\";\n\nimport { useKnockClient } from \"../../core\";\n\nconst MAX_COUNT = 1000;\nconst LIMIT_PER_PAGE = 200;\nconst CHANNEL_TYPES = \"private_channel,public_channel\";\n\nconst QUERY_KEY = \"SLACK_CHANNELS\";\n\ntype UseSlackChannelsOptions = {\n queryOptions?: SlackChannelQueryOptions;\n};\n\ntype UseSlackChannelOutput = {\n data: SlackChannel[];\n isLoading: boolean;\n refetch: () => void;\n};\n\ntype QueryKey =\n | [key: string, tenantId: string, channelId: string, cursor: string]\n | null;\n\nfunction useSlackChannels({\n queryOptions,\n}: UseSlackChannelsOptions): UseSlackChannelOutput {\n const knock = useKnockClient();\n const { knockSlackChannelId, tenantId, connectionStatus } =\n useKnockSlackClient();\n\n // Track previous tenant/channel/connectionStatus to detect changes and clear cache\n const prevTenantRef = useRef(tenantId);\n const prevChannelRef = useRef(knockSlackChannelId);\n const prevConnectionStatusRef = useRef(connectionStatus);\n\n // Create a getQueryKey function that includes tenantId and knockSlackChannelId\n // so that SWR treats different tenants as different cache entries\n const getQueryKey = useCallback(\n (\n pageIndex: number,\n previousPageData: GetSlackChannelsResponse | null,\n ): QueryKey => {\n // Don't fetch if not connected\n if (connectionStatus !== \"connected\") {\n return null;\n }\n\n // First page so just pass empty\n if (pageIndex === 0) {\n return [QUERY_KEY, tenantId, knockSlackChannelId, \"\"];\n }\n\n // If there's no more data then return an empty next cursor\n if (\n previousPageData &&\n [\"\", null].includes(previousPageData.next_cursor)\n ) {\n return null;\n }\n\n // Next cursor exists so pass it\n return [\n QUERY_KEY,\n tenantId,\n knockSlackChannelId,\n previousPageData?.next_cursor ?? \"\",\n ];\n },\n [tenantId, knockSlackChannelId, connectionStatus],\n );\n\n const fetchChannels = useCallback(\n (queryKey: QueryKey) => {\n return knock.slack.getChannels({\n tenant: tenantId,\n knockChannelId: knockSlackChannelId,\n queryOptions: {\n ...queryOptions,\n cursor: queryKey?.[3],\n limit: queryOptions?.limitPerPage || LIMIT_PER_PAGE,\n types: queryOptions?.types || CHANNEL_TYPES,\n },\n });\n },\n [knock.slack, tenantId, knockSlackChannelId, queryOptions],\n );\n\n const { data, error, isLoading, isValidating, setSize, mutate } =\n useSWRInfinite<GetSlackChannelsResponse>(getQueryKey, fetchChannels, {\n initialSize: 0,\n revalidateFirstPage: false,\n });\n\n // Clear cache when tenant, channel, or connection status changes\n // This ensures that when the user disconnects and reconnects (possibly to a different\n // Slack workspace), or when the access token is revoked, the cached channels are cleared\n useEffect(() => {\n const tenantChanged = prevTenantRef.current !== tenantId;\n const channelChanged = prevChannelRef.current !== knockSlackChannelId;\n // Detect when connection is re-established (was not connected, now is connected)\n const wasConnected = prevConnectionStatusRef.current === \"connected\";\n const isConnected = connectionStatus === \"connected\";\n const connectionReestablished = !wasConnected && isConnected;\n\n if (tenantChanged || channelChanged || connectionReestablished) {\n // Reset the SWR state to clear cached data\n mutate(undefined, { revalidate: false });\n setSize(0);\n }\n\n prevTenantRef.current = tenantId;\n prevChannelRef.current = knockSlackChannelId;\n prevConnectionStatusRef.current = connectionStatus;\n }, [tenantId, knockSlackChannelId, connectionStatus, mutate, setSize]);\n\n const lastPage = data?.at(-1);\n const hasNextPage = lastPage === undefined || !!lastPage.next_cursor;\n\n const slackChannels: SlackChannel[] = useMemo(\n () =>\n (data ?? [])\n .flatMap((page) => page?.slack_channels)\n .filter((channel) => !!channel),\n [data],\n );\n\n const maxCount = queryOptions?.maxCount || MAX_COUNT;\n\n useEffect(() => {\n if (\n connectionStatus === \"connected\" &&\n !error &&\n hasNextPage &&\n !isLoading &&\n !isValidating &&\n slackChannels.length < maxCount\n ) {\n // Fetch a page at a time until we have nothing else left to fetch\n // or we've already hit the max amount of channels to fetch\n setSize((size) => size + 1);\n }\n }, [\n slackChannels.length,\n setSize,\n hasNextPage,\n isLoading,\n isValidating,\n maxCount,\n error,\n connectionStatus,\n ]);\n\n return {\n data: slackChannels,\n isLoading: isLoading || isValidating,\n refetch: () => mutate(),\n };\n}\n\nexport default useSlackChannels;\n"],"names":["MAX_COUNT","LIMIT_PER_PAGE","CHANNEL_TYPES","QUERY_KEY","useSlackChannels","queryOptions","knock","useKnockClient","knockSlackChannelId","tenantId","connectionStatus","useKnockSlackClient","prevTenantRef","useRef","prevChannelRef","prevConnectionStatusRef","getQueryKey","useCallback","pageIndex","previousPageData","includes","next_cursor","fetchChannels","queryKey","slack","getChannels","tenant","knockChannelId","cursor","limit","limitPerPage","types","data","error","isLoading","isValidating","setSize","mutate","useSWRInfinite","initialSize","revalidateFirstPage","useEffect","tenantChanged","current","channelChanged","connectionReestablished","undefined","revalidate","lastPage","at","hasNextPage","slackChannels","useMemo","flatMap","page","slack_channels","filter","channel","maxCount","length","size","refetch"],"mappings":";;;;;;;;;AAOA,MAAMA,IAAY,KACZC,IAAiB,KACjBC,IAAgB,kCAEhBC,IAAY;AAgBlB,SAASC,EAAiB;AAAA,EACxBC,cAAAA;AACuB,GAA0B;AACjD,QAAMC,IAAQC,EAAe,GACvB;AAAA,IAAEC,qBAAAA;AAAAA,IAAqBC,UAAAA;AAAAA,IAAUC,kBAAAA;AAAAA,MACrCC,EAAoB,GAGhBC,IAAgBC,EAAOJ,CAAQ,GAC/BK,IAAiBD,EAAOL,CAAmB,GAC3CO,IAA0BF,EAAOH,CAAgB,GAIjDM,IAAcC,EAClB,CACEC,GACAC,MAGIT,MAAqB,cAChB,OAILQ,MAAc,IACT,CAACf,GAAWM,GAAUD,GAAqB,EAAE,IAKpDW,KACA,CAAC,IAAI,IAAI,EAAEC,SAASD,EAAiBE,WAAW,IAEzC,OAIF,CACLlB,GACAM,GACAD,IACAW,KAAAA,gBAAAA,EAAkBE,gBAAe,EAAE,GAGvC,CAACZ,GAAUD,GAAqBE,CAAgB,CAClD,GAEMY,IAAgBL,EACpB,CAACM,MACQjB,EAAMkB,MAAMC,YAAY;AAAA,IAC7BC,QAAQjB;AAAAA,IACRkB,gBAAgBnB;AAAAA,IAChBH,cAAc;AAAA,MACZ,GAAGA;AAAAA,MACHuB,QAAQL,KAAAA,gBAAAA,EAAW;AAAA,MACnBM,QAAOxB,KAAAA,gBAAAA,EAAcyB,iBAAgB7B;AAAAA,MACrC8B,QAAO1B,KAAAA,gBAAAA,EAAc0B,UAAS7B;AAAAA,IAAAA;AAAAA,EAChC,CACD,GAEH,CAACI,EAAMkB,OAAOf,GAAUD,GAAqBH,CAAY,CAC3D,GAEM;AAAA,IAAE2B,MAAAA;AAAAA,IAAMC,OAAAA;AAAAA,IAAOC,WAAAA;AAAAA,IAAWC,cAAAA;AAAAA,IAAcC,SAAAA;AAAAA,IAASC,QAAAA;AAAAA,EAAAA,IACrDC,EAAyCtB,GAAaM,GAAe;AAAA,IACnEiB,aAAa;AAAA,IACbC,qBAAqB;AAAA,EAAA,CACtB;AAKHC,EAAAA,EAAU,MAAM;AACRC,UAAAA,IAAgB9B,EAAc+B,YAAYlC,GAC1CmC,IAAiB9B,EAAe6B,YAAYnC,GAI5CqC,IAA0B,EAFX9B,EAAwB4B,YAAY,gBACrCjC,MAAqB;AAGrCgC,KAAAA,KAAiBE,KAAkBC,OAErCR,EAAOS,QAAW;AAAA,MAAEC,YAAY;AAAA,IAAA,CAAO,GACvCX,EAAQ,CAAC,IAGXxB,EAAc+B,UAAUlC,GACxBK,EAAe6B,UAAUnC,GACzBO,EAAwB4B,UAAUjC;AAAAA,EAAAA,GACjC,CAACD,GAAUD,GAAqBE,GAAkB2B,GAAQD,CAAO,CAAC;AAE/DY,QAAAA,IAAWhB,KAAAA,gBAAAA,EAAMiB,GAAG,KACpBC,IAAcF,MAAaF,UAAa,CAAC,CAACE,EAAS3B,aAEnD8B,IAAgCC,EACpC,OACGpB,KAAQ,IACNqB,QAASC,CAASA,MAAAA,KAAAA,gBAAAA,EAAMC,cAAc,EACtCC,OAAQC,OAAY,CAAC,CAACA,CAAO,GAClC,CAACzB,CAAI,CACP,GAEM0B,KAAWrD,KAAAA,gBAAAA,EAAcqD,aAAY1D;AAE3CyC,SAAAA,EAAU,MAAM;AAEZ/B,IAAAA,MAAqB,eACrB,CAACuB,KACDiB,KACA,CAAChB,KACD,CAACC,KACDgB,EAAcQ,SAASD,KAIdE,EAAAA,CAAAA,MAASA,IAAO,CAAC;AAAA,EAE9B,GAAG,CACDT,EAAcQ,QACdvB,GACAc,GACAhB,GACAC,GACAuB,GACAzB,GACAvB,CAAgB,CACjB,GAEM;AAAA,IACLsB,MAAMmB;AAAAA,IACNjB,WAAWA,KAAaC;AAAAA,IACxB0B,SAASA,MAAMxB,EAAO;AAAA,EACxB;AACF;"}
@@ -1,10 +1,10 @@
1
- import { KnockGuide, KnockGuideFilterParams, KnockGuideStep } from '@knocklabs/client';
1
+ import { KnockGuide, KnockGuideFilterParams, KnockGuideStep, KnockSelectGuideOpts } from '@knocklabs/client';
2
2
  import { UseGuideContextReturn } from './useGuideContext';
3
3
  type Any = any;
4
4
  interface UseGuideReturn<C = Any> extends UseGuideContextReturn {
5
5
  guide: KnockGuide<C> | undefined;
6
6
  step: KnockGuideStep<C> | undefined;
7
7
  }
8
- export declare const useGuide: <C = Any>(filters: KnockGuideFilterParams) => UseGuideReturn<C>;
8
+ export declare const useGuide: <C = Any>(filters: KnockGuideFilterParams, opts?: KnockSelectGuideOpts) => UseGuideReturn<C>;
9
9
  export {};
10
10
  //# sourceMappingURL=useGuide.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useGuide.d.ts","sourceRoot":"","sources":["../../../../../src/modules/guide/hooks/useGuide.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,sBAAsB,EACtB,cAAc,EACf,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,qBAAqB,EAAmB,MAAM,mBAAmB,CAAC;AAG3E,KAAK,GAAG,GAAG,GAAG,CAAC;AAEf,UAAU,cAAc,CAAC,CAAC,GAAG,GAAG,CAAE,SAAQ,qBAAqB;IAC7D,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IACjC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;CACrC;AAED,eAAO,MAAM,QAAQ,GAAI,CAAC,GAAG,GAAG,EAC9B,SAAS,sBAAsB,KAC9B,cAAc,CAAC,CAAC,CAkBlB,CAAC"}
1
+ {"version":3,"file":"useGuide.d.ts","sourceRoot":"","sources":["../../../../../src/modules/guide/hooks/useGuide.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,sBAAsB,EACtB,cAAc,EACd,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,qBAAqB,EAAmB,MAAM,mBAAmB,CAAC;AAG3E,KAAK,GAAG,GAAG,GAAG,CAAC;AAEf,UAAU,cAAc,CAAC,CAAC,GAAG,GAAG,CAAE,SAAQ,qBAAqB;IAC7D,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IACjC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;CACrC;AAED,eAAO,MAAM,QAAQ,GAAI,CAAC,GAAG,GAAG,EAC9B,SAAS,sBAAsB,EAC/B,OAAO,oBAAoB,KAC1B,cAAc,CAAC,CAAC,CAkBlB,CAAC"}
@@ -1,9 +1,9 @@
1
- import { KnockGuide, KnockGuideFilterParams } from '@knocklabs/client';
1
+ import { KnockGuide, KnockGuideFilterParams, KnockSelectGuidesOpts } from '@knocklabs/client';
2
2
  import { UseGuideContextReturn } from './useGuideContext';
3
3
  type Any = any;
4
4
  interface UseGuidesReturn<C = Any> extends UseGuideContextReturn {
5
5
  guides: KnockGuide<C>[];
6
6
  }
7
- export declare const useGuides: <C = Any>(filters: Pick<KnockGuideFilterParams, "type">) => UseGuidesReturn<C>;
7
+ export declare const useGuides: <C = Any>(filters: Pick<KnockGuideFilterParams, "type">, opts?: KnockSelectGuidesOpts) => UseGuidesReturn<C>;
8
8
  export {};
9
9
  //# sourceMappingURL=useGuides.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useGuides.d.ts","sourceRoot":"","sources":["../../../../../src/modules/guide/hooks/useGuides.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAGvE,OAAO,EAAE,qBAAqB,EAAmB,MAAM,mBAAmB,CAAC;AAG3E,KAAK,GAAG,GAAG,GAAG,CAAC;AAEf,UAAU,eAAe,CAAC,CAAC,GAAG,GAAG,CAAE,SAAQ,qBAAqB;IAC9D,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;CACzB;AAED,eAAO,MAAM,SAAS,GAAI,CAAC,GAAG,GAAG,EAC/B,SAAS,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,KAC5C,eAAe,CAAC,CAAC,CASnB,CAAC"}
1
+ {"version":3,"file":"useGuides.d.ts","sourceRoot":"","sources":["../../../../../src/modules/guide/hooks/useGuides.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,sBAAsB,EACtB,qBAAqB,EACtB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,qBAAqB,EAAmB,MAAM,mBAAmB,CAAC;AAG3E,KAAK,GAAG,GAAG,GAAG,CAAC;AAEf,UAAU,eAAe,CAAC,CAAC,GAAG,GAAG,CAAE,SAAQ,qBAAqB;IAC9D,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;CACzB;AAED,eAAO,MAAM,SAAS,GAAI,CAAC,GAAG,GAAG,EAC/B,SAAS,IAAI,CAAC,sBAAsB,EAAE,MAAM,CAAC,EAC7C,OAAO,qBAAqB,KAC3B,eAAe,CAAC,CAAC,CASnB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useMsTeamsChannels.d.ts","sourceRoot":"","sources":["../../../../../src/modules/ms-teams/hooks/useMsTeamsChannels.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAK/E,OAAO,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAI3D,KAAK,uBAAuB,GAAG;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,0BAA0B,CAAC;CAC3C,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC9B,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,iBAAS,kBAAkB,CAAC,EAC1B,MAAM,EACN,YAAY,GACb,EAAE,uBAAuB,GAAG,wBAAwB,CA2BpD;AAED,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"useMsTeamsChannels.d.ts","sourceRoot":"","sources":["../../../../../src/modules/ms-teams/hooks/useMsTeamsChannels.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAM/E,OAAO,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAI3D,KAAK,uBAAuB,GAAG;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,0BAA0B,CAAC;CAC3C,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC9B,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF,iBAAS,kBAAkB,CAAC,EAC1B,MAAM,EACN,YAAY,GACb,EAAE,uBAAuB,GAAG,wBAAwB,CA6DpD;AAED,eAAe,kBAAkB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useMsTeamsTeams.d.ts","sourceRoot":"","sources":["../../../../../src/modules/ms-teams/hooks/useMsTeamsTeams.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAMzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAMxD,KAAK,sBAAsB,GAAG;IAC5B,YAAY,CAAC,EAAE,uBAAuB,CAAC;CACxC,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAsBF,iBAAS,eAAe,CAAC,EACvB,YAAiB,GAClB,EAAE,sBAAsB,GAAG,qBAAqB,CAkEhD;AAED,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"useMsTeamsTeams.d.ts","sourceRoot":"","sources":["../../../../../src/modules/ms-teams/hooks/useMsTeamsTeams.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAMzE,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAMxD,KAAK,sBAAsB,GAAG;IAC5B,YAAY,CAAC,EAAE,uBAAuB,CAAC;CACxC,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,WAAW,EAAE,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAMF,iBAAS,eAAe,CAAC,EACvB,YAAiB,GAClB,EAAE,sBAAsB,GAAG,qBAAqB,CAoIhD;AAED,eAAe,eAAe,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"useSlackChannels.d.ts","sourceRoot":"","sources":["../../../../../src/modules/slack/hooks/useSlackChannels.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAuB,MAAM,IAAI,CAAC;AACnE,OAAO,EAA4B,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAY3E,KAAK,uBAAuB,GAAG;IAC7B,YAAY,CAAC,EAAE,wBAAwB,CAAC;CACzC,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,YAAY,EAAE,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAsBF,iBAAS,gBAAgB,CAAC,EACxB,YAAY,GACb,EAAE,uBAAuB,GAAG,qBAAqB,CAkEjD;AAED,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"useSlackChannels.d.ts","sourceRoot":"","sources":["../../../../../src/modules/slack/hooks/useSlackChannels.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAuB,MAAM,IAAI,CAAC;AACnE,OAAO,EAA4B,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAY3E,KAAK,uBAAuB,GAAG;IAC7B,YAAY,CAAC,EAAE,wBAAwB,CAAC;CACzC,CAAC;AAEF,KAAK,qBAAqB,GAAG;IAC3B,IAAI,EAAE,YAAY,EAAE,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAMF,iBAAS,gBAAgB,CAAC,EACxB,YAAY,GACb,EAAE,uBAAuB,GAAG,qBAAqB,CAoIjD;AAED,eAAe,gBAAgB,CAAC"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@knocklabs/react-core",
3
3
  "description": "A set of React components to build notification experiences powered by Knock",
4
4
  "author": "@knocklabs",
5
- "version": "0.11.3",
5
+ "version": "0.12.0",
6
6
  "license": "MIT",
7
7
  "main": "dist/cjs/index.js",
8
8
  "module": "dist/esm/index.mjs",
@@ -47,7 +47,7 @@
47
47
  "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
48
48
  },
49
49
  "dependencies": {
50
- "@knocklabs/client": "^0.19.3",
50
+ "@knocklabs/client": "^0.20.0",
51
51
  "@tanstack/react-store": "^0.7.3",
52
52
  "date-fns": "^4.0.0",
53
53
  "fast-deep-equal": "^3.1.3",
@@ -2,6 +2,7 @@ import {
2
2
  KnockGuide,
3
3
  KnockGuideFilterParams,
4
4
  KnockGuideStep,
5
+ KnockSelectGuideOpts,
5
6
  } from "@knocklabs/client";
6
7
  import { useStore } from "@tanstack/react-store";
7
8
 
@@ -17,6 +18,7 @@ interface UseGuideReturn<C = Any> extends UseGuideContextReturn {
17
18
 
18
19
  export const useGuide = <C = Any>(
19
20
  filters: KnockGuideFilterParams,
21
+ opts?: KnockSelectGuideOpts,
20
22
  ): UseGuideReturn<C> => {
21
23
  const context = useGuideContext();
22
24
 
@@ -29,7 +31,7 @@ export const useGuide = <C = Any>(
29
31
  const { client, colorMode } = context;
30
32
 
31
33
  const guide = useStore(client.store, (state) =>
32
- client.selectGuide<C>(state, filters),
34
+ client.selectGuide<C>(state, filters, opts),
33
35
  );
34
36
 
35
37
  const step = guide && guide.getStep();
@@ -1,4 +1,8 @@
1
- import { KnockGuide, KnockGuideFilterParams } from "@knocklabs/client";
1
+ import {
2
+ KnockGuide,
3
+ KnockGuideFilterParams,
4
+ KnockSelectGuidesOpts,
5
+ } from "@knocklabs/client";
2
6
  import { useStore } from "@tanstack/react-store";
3
7
 
4
8
  import { UseGuideContextReturn, useGuideContext } from "./useGuideContext";
@@ -12,12 +16,13 @@ interface UseGuidesReturn<C = Any> extends UseGuideContextReturn {
12
16
 
13
17
  export const useGuides = <C = Any>(
14
18
  filters: Pick<KnockGuideFilterParams, "type">,
19
+ opts?: KnockSelectGuidesOpts,
15
20
  ): UseGuidesReturn<C> => {
16
21
  const context = useGuideContext();
17
22
  const { client, colorMode } = context;
18
23
 
19
24
  const guides = useStore(client.store, (state) =>
20
- client.selectGuides<C>(state, filters),
25
+ client.selectGuides<C>(state, filters, opts),
21
26
  );
22
27
 
23
28
  return { client, colorMode, guides };
@@ -1,4 +1,5 @@
1
1
  import { GetMsTeamsChannelsResponse, MsTeamsChannel } from "@knocklabs/client";
2
+ import { useCallback, useEffect, useRef } from "react";
2
3
  import useSWR from "swr";
3
4
 
4
5
  import { useKnockClient } from "../../core";
@@ -23,26 +24,60 @@ function useMsTeamsChannels({
23
24
  queryOptions,
24
25
  }: UseMsTeamsChannelsProps): UseMsTeamsChannelsOutput {
25
26
  const knock = useKnockClient();
26
- const { knockMsTeamsChannelId, tenantId } = useKnockMsTeamsClient();
27
-
28
- const fetchChannels = () =>
29
- knock.msTeams.getChannels({
30
- knockChannelId: knockMsTeamsChannelId,
31
- tenant: tenantId,
32
- teamId: teamId!,
33
- queryOptions: {
34
- $filter: queryOptions?.filter,
35
- $select: queryOptions?.select,
36
- },
37
- });
27
+ const { knockMsTeamsChannelId, tenantId, connectionStatus } =
28
+ useKnockMsTeamsClient();
38
29
 
30
+ // Track previous tenant/channel/connectionStatus to detect changes and clear cache
31
+ const prevTenantRef = useRef(tenantId);
32
+ const prevChannelRef = useRef(knockMsTeamsChannelId);
33
+ const prevConnectionStatusRef = useRef(connectionStatus);
34
+
35
+ const fetchChannels = useCallback(
36
+ () =>
37
+ knock.msTeams.getChannels({
38
+ knockChannelId: knockMsTeamsChannelId,
39
+ tenant: tenantId,
40
+ teamId: teamId!,
41
+ queryOptions: {
42
+ $filter: queryOptions?.filter,
43
+ $select: queryOptions?.select,
44
+ },
45
+ }),
46
+ [knock.msTeams, knockMsTeamsChannelId, tenantId, teamId, queryOptions],
47
+ );
48
+
49
+ // Include tenantId and knockMsTeamsChannelId in the cache key so that
50
+ // SWR treats different tenants as different cache entries
39
51
  const { data, isLoading, isValidating, mutate } =
40
52
  useSWR<GetMsTeamsChannelsResponse>(
41
- teamId ? [QUERY_KEY, teamId] : null,
53
+ teamId && connectionStatus === "connected"
54
+ ? [QUERY_KEY, tenantId, knockMsTeamsChannelId, teamId]
55
+ : null,
42
56
  fetchChannels,
43
57
  { revalidateOnFocus: false },
44
58
  );
45
59
 
60
+ // Clear cache when tenant, channel, or connection status changes
61
+ // This ensures that when the user disconnects and reconnects (possibly to a different
62
+ // MS Teams workspace), or when the access token is revoked, the cached channels are cleared
63
+ useEffect(() => {
64
+ const tenantChanged = prevTenantRef.current !== tenantId;
65
+ const channelChanged = prevChannelRef.current !== knockMsTeamsChannelId;
66
+ // Detect when connection is re-established (was not connected, now is connected)
67
+ const wasConnected = prevConnectionStatusRef.current === "connected";
68
+ const isConnected = connectionStatus === "connected";
69
+ const connectionReestablished = !wasConnected && isConnected;
70
+
71
+ if (tenantChanged || channelChanged || connectionReestablished) {
72
+ // Reset the SWR state to clear cached data
73
+ mutate(undefined, { revalidate: false });
74
+ }
75
+
76
+ prevTenantRef.current = tenantId;
77
+ prevChannelRef.current = knockMsTeamsChannelId;
78
+ prevConnectionStatusRef.current = connectionStatus;
79
+ }, [tenantId, knockMsTeamsChannelId, connectionStatus, mutate]);
80
+
46
81
  return {
47
82
  data: data?.ms_teams_channels ?? [],
48
83
  isLoading: isLoading || isValidating,
@@ -1,5 +1,5 @@
1
1
  import { GetMsTeamsTeamsResponse, MsTeamsTeam } from "@knocklabs/client";
2
- import { useEffect, useMemo } from "react";
2
+ import { useCallback, useEffect, useMemo, useRef } from "react";
3
3
  import useSWRInfinite from "swr/infinite";
4
4
 
5
5
  import { useKnockClient } from "../../core";
@@ -20,25 +20,9 @@ type UseMsTeamsTeamsOutput = {
20
20
  refetch: () => void;
21
21
  };
22
22
 
23
- type QueryKey = [key: string, skiptoken: string] | null;
24
-
25
- function getQueryKey(
26
- pageIndex: number,
27
- previousPageData: GetMsTeamsTeamsResponse,
28
- ): QueryKey {
29
- // First page so just pass empty
30
- if (pageIndex === 0) {
31
- return [QUERY_KEY, ""];
32
- }
33
-
34
- // If there's no more data then return an empty next skiptoken
35
- if (previousPageData && ["", null].includes(previousPageData.skip_token)) {
36
- return null;
37
- }
38
-
39
- // Next skiptoken exists so pass it
40
- return [QUERY_KEY, previousPageData.skip_token ?? ""];
41
- }
23
+ type QueryKey =
24
+ | [key: string, tenantId: string, channelId: string, skiptoken: string]
25
+ | null;
42
26
 
43
27
  function useMsTeamsTeams({
44
28
  queryOptions = {},
@@ -47,17 +31,61 @@ function useMsTeamsTeams({
47
31
  const { knockMsTeamsChannelId, tenantId, connectionStatus } =
48
32
  useKnockMsTeamsClient();
49
33
 
50
- const fetchTeams = (queryKey: QueryKey) =>
51
- knock.msTeams.getTeams({
52
- knockChannelId: knockMsTeamsChannelId,
53
- tenant: tenantId,
54
- queryOptions: {
55
- $skiptoken: queryKey?.[1],
56
- $top: queryOptions?.limitPerPage,
57
- $filter: queryOptions?.filter,
58
- $select: queryOptions?.select,
59
- },
60
- });
34
+ // Track previous tenant/channel/connectionStatus to detect changes and clear cache
35
+ const prevTenantRef = useRef(tenantId);
36
+ const prevChannelRef = useRef(knockMsTeamsChannelId);
37
+ const prevConnectionStatusRef = useRef(connectionStatus);
38
+
39
+ // Create a getQueryKey function that includes tenantId and knockMsTeamsChannelId
40
+ // so that SWR treats different tenants as different cache entries
41
+ const getQueryKey = useCallback(
42
+ (
43
+ pageIndex: number,
44
+ previousPageData: GetMsTeamsTeamsResponse | null,
45
+ ): QueryKey => {
46
+ // Don't fetch if not connected
47
+ if (connectionStatus !== "connected") {
48
+ return null;
49
+ }
50
+
51
+ // First page so just pass empty
52
+ if (pageIndex === 0) {
53
+ return [QUERY_KEY, tenantId, knockMsTeamsChannelId, ""];
54
+ }
55
+
56
+ // If there's no more data then return an empty next skiptoken
57
+ if (
58
+ previousPageData &&
59
+ ["", null].includes(previousPageData.skip_token)
60
+ ) {
61
+ return null;
62
+ }
63
+
64
+ // Next skiptoken exists so pass it
65
+ return [
66
+ QUERY_KEY,
67
+ tenantId,
68
+ knockMsTeamsChannelId,
69
+ previousPageData?.skip_token ?? "",
70
+ ];
71
+ },
72
+ [tenantId, knockMsTeamsChannelId, connectionStatus],
73
+ );
74
+
75
+ const fetchTeams = useCallback(
76
+ (queryKey: QueryKey) =>
77
+ knock.msTeams.getTeams({
78
+ knockChannelId: knockMsTeamsChannelId,
79
+ tenant: tenantId,
80
+ queryOptions: {
81
+ $skiptoken: queryKey?.[3],
82
+ $top: queryOptions?.limitPerPage,
83
+ $filter: queryOptions?.filter,
84
+ $select: queryOptions?.select,
85
+ },
86
+ }),
87
+ [knock.msTeams, knockMsTeamsChannelId, tenantId, queryOptions],
88
+ );
61
89
 
62
90
  const { data, error, isLoading, isValidating, setSize, mutate } =
63
91
  useSWRInfinite<GetMsTeamsTeamsResponse>(getQueryKey, fetchTeams, {
@@ -66,6 +94,28 @@ function useMsTeamsTeams({
66
94
  revalidateFirstPage: false,
67
95
  });
68
96
 
97
+ // Clear cache when tenant, channel, or connection status changes
98
+ // This ensures that when the user disconnects and reconnects (possibly to a different
99
+ // MS Teams workspace), or when the access token is revoked, the cached teams are cleared
100
+ useEffect(() => {
101
+ const tenantChanged = prevTenantRef.current !== tenantId;
102
+ const channelChanged = prevChannelRef.current !== knockMsTeamsChannelId;
103
+ // Detect when connection is re-established (was not connected, now is connected)
104
+ const wasConnected = prevConnectionStatusRef.current === "connected";
105
+ const isConnected = connectionStatus === "connected";
106
+ const connectionReestablished = !wasConnected && isConnected;
107
+
108
+ if (tenantChanged || channelChanged || connectionReestablished) {
109
+ // Reset the SWR state to clear cached data
110
+ mutate(undefined, { revalidate: false });
111
+ setSize(0);
112
+ }
113
+
114
+ prevTenantRef.current = tenantId;
115
+ prevChannelRef.current = knockMsTeamsChannelId;
116
+ prevConnectionStatusRef.current = connectionStatus;
117
+ }, [tenantId, knockMsTeamsChannelId, connectionStatus, mutate, setSize]);
118
+
69
119
  const lastPage = data?.at(-1);
70
120
  const hasNextPage = lastPage === undefined || !!lastPage.skip_token;
71
121
 
@@ -1,6 +1,6 @@
1
1
  import { SlackChannelQueryOptions, useKnockSlackClient } from "..";
2
2
  import { GetSlackChannelsResponse, SlackChannel } from "@knocklabs/client";
3
- import { useEffect, useMemo } from "react";
3
+ import { useCallback, useEffect, useMemo, useRef } from "react";
4
4
  import useSWRInfinite from "swr/infinite";
5
5
 
6
6
  import { useKnockClient } from "../../core";
@@ -21,25 +21,9 @@ type UseSlackChannelOutput = {
21
21
  refetch: () => void;
22
22
  };
23
23
 
24
- type QueryKey = [key: string, cursor: string] | null;
25
-
26
- function getQueryKey(
27
- pageIndex: number,
28
- previousPageData: GetSlackChannelsResponse,
29
- ): QueryKey {
30
- // First page so just pass empty
31
- if (pageIndex === 0) {
32
- return [QUERY_KEY, ""];
33
- }
34
-
35
- // If there's no more data then return an empty next cursor
36
- if (previousPageData && ["", null].includes(previousPageData.next_cursor)) {
37
- return null;
38
- }
39
-
40
- // Next cursor exists so pass it
41
- return [QUERY_KEY, previousPageData.next_cursor ?? ""];
42
- }
24
+ type QueryKey =
25
+ | [key: string, tenantId: string, channelId: string, cursor: string]
26
+ | null;
43
27
 
44
28
  function useSlackChannels({
45
29
  queryOptions,
@@ -48,18 +32,62 @@ function useSlackChannels({
48
32
  const { knockSlackChannelId, tenantId, connectionStatus } =
49
33
  useKnockSlackClient();
50
34
 
51
- const fetchChannels = (queryKey: QueryKey) => {
52
- return knock.slack.getChannels({
53
- tenant: tenantId,
54
- knockChannelId: knockSlackChannelId,
55
- queryOptions: {
56
- ...queryOptions,
57
- cursor: queryKey?.[1],
58
- limit: queryOptions?.limitPerPage || LIMIT_PER_PAGE,
59
- types: queryOptions?.types || CHANNEL_TYPES,
60
- },
61
- });
62
- };
35
+ // Track previous tenant/channel/connectionStatus to detect changes and clear cache
36
+ const prevTenantRef = useRef(tenantId);
37
+ const prevChannelRef = useRef(knockSlackChannelId);
38
+ const prevConnectionStatusRef = useRef(connectionStatus);
39
+
40
+ // Create a getQueryKey function that includes tenantId and knockSlackChannelId
41
+ // so that SWR treats different tenants as different cache entries
42
+ const getQueryKey = useCallback(
43
+ (
44
+ pageIndex: number,
45
+ previousPageData: GetSlackChannelsResponse | null,
46
+ ): QueryKey => {
47
+ // Don't fetch if not connected
48
+ if (connectionStatus !== "connected") {
49
+ return null;
50
+ }
51
+
52
+ // First page so just pass empty
53
+ if (pageIndex === 0) {
54
+ return [QUERY_KEY, tenantId, knockSlackChannelId, ""];
55
+ }
56
+
57
+ // If there's no more data then return an empty next cursor
58
+ if (
59
+ previousPageData &&
60
+ ["", null].includes(previousPageData.next_cursor)
61
+ ) {
62
+ return null;
63
+ }
64
+
65
+ // Next cursor exists so pass it
66
+ return [
67
+ QUERY_KEY,
68
+ tenantId,
69
+ knockSlackChannelId,
70
+ previousPageData?.next_cursor ?? "",
71
+ ];
72
+ },
73
+ [tenantId, knockSlackChannelId, connectionStatus],
74
+ );
75
+
76
+ const fetchChannels = useCallback(
77
+ (queryKey: QueryKey) => {
78
+ return knock.slack.getChannels({
79
+ tenant: tenantId,
80
+ knockChannelId: knockSlackChannelId,
81
+ queryOptions: {
82
+ ...queryOptions,
83
+ cursor: queryKey?.[3],
84
+ limit: queryOptions?.limitPerPage || LIMIT_PER_PAGE,
85
+ types: queryOptions?.types || CHANNEL_TYPES,
86
+ },
87
+ });
88
+ },
89
+ [knock.slack, tenantId, knockSlackChannelId, queryOptions],
90
+ );
63
91
 
64
92
  const { data, error, isLoading, isValidating, setSize, mutate } =
65
93
  useSWRInfinite<GetSlackChannelsResponse>(getQueryKey, fetchChannels, {
@@ -67,6 +95,28 @@ function useSlackChannels({
67
95
  revalidateFirstPage: false,
68
96
  });
69
97
 
98
+ // Clear cache when tenant, channel, or connection status changes
99
+ // This ensures that when the user disconnects and reconnects (possibly to a different
100
+ // Slack workspace), or when the access token is revoked, the cached channels are cleared
101
+ useEffect(() => {
102
+ const tenantChanged = prevTenantRef.current !== tenantId;
103
+ const channelChanged = prevChannelRef.current !== knockSlackChannelId;
104
+ // Detect when connection is re-established (was not connected, now is connected)
105
+ const wasConnected = prevConnectionStatusRef.current === "connected";
106
+ const isConnected = connectionStatus === "connected";
107
+ const connectionReestablished = !wasConnected && isConnected;
108
+
109
+ if (tenantChanged || channelChanged || connectionReestablished) {
110
+ // Reset the SWR state to clear cached data
111
+ mutate(undefined, { revalidate: false });
112
+ setSize(0);
113
+ }
114
+
115
+ prevTenantRef.current = tenantId;
116
+ prevChannelRef.current = knockSlackChannelId;
117
+ prevConnectionStatusRef.current = connectionStatus;
118
+ }, [tenantId, knockSlackChannelId, connectionStatus, mutate, setSize]);
119
+
70
120
  const lastPage = data?.at(-1);
71
121
  const hasNextPage = lastPage === undefined || !!lastPage.next_cursor;
72
122