@knocklabs/react-core 0.11.4 → 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.
- package/CHANGELOG.md +26 -0
- package/dist/cjs/modules/guide/hooks/useGuide.js +1 -1
- package/dist/cjs/modules/guide/hooks/useGuide.js.map +1 -1
- package/dist/cjs/modules/guide/hooks/useGuides.js +1 -1
- package/dist/cjs/modules/guide/hooks/useGuides.js.map +1 -1
- package/dist/cjs/modules/ms-teams/hooks/useMsTeamsChannels.js +1 -1
- package/dist/cjs/modules/ms-teams/hooks/useMsTeamsChannels.js.map +1 -1
- package/dist/cjs/modules/ms-teams/hooks/useMsTeamsTeams.js +1 -1
- package/dist/cjs/modules/ms-teams/hooks/useMsTeamsTeams.js.map +1 -1
- package/dist/cjs/modules/slack/hooks/useSlackChannels.js +1 -1
- package/dist/cjs/modules/slack/hooks/useSlackChannels.js.map +1 -1
- package/dist/esm/modules/guide/hooks/useGuide.mjs +9 -9
- package/dist/esm/modules/guide/hooks/useGuide.mjs.map +1 -1
- package/dist/esm/modules/guide/hooks/useGuides.mjs +9 -9
- package/dist/esm/modules/guide/hooks/useGuides.mjs.map +1 -1
- package/dist/esm/modules/ms-teams/hooks/useMsTeamsChannels.mjs +31 -25
- package/dist/esm/modules/ms-teams/hooks/useMsTeamsChannels.mjs.map +1 -1
- package/dist/esm/modules/ms-teams/hooks/useMsTeamsTeams.mjs +37 -33
- package/dist/esm/modules/ms-teams/hooks/useMsTeamsTeams.mjs.map +1 -1
- package/dist/esm/modules/slack/hooks/useSlackChannels.mjs +41 -37
- package/dist/esm/modules/slack/hooks/useSlackChannels.mjs.map +1 -1
- package/dist/types/modules/guide/hooks/useGuide.d.ts +2 -2
- package/dist/types/modules/guide/hooks/useGuide.d.ts.map +1 -1
- package/dist/types/modules/guide/hooks/useGuides.d.ts +2 -2
- package/dist/types/modules/guide/hooks/useGuides.d.ts.map +1 -1
- package/dist/types/modules/ms-teams/hooks/useMsTeamsChannels.d.ts.map +1 -1
- package/dist/types/modules/ms-teams/hooks/useMsTeamsTeams.d.ts.map +1 -1
- package/dist/types/modules/slack/hooks/useSlackChannels.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/modules/guide/hooks/useGuide.ts +3 -1
- package/src/modules/guide/hooks/useGuides.ts +7 -2
- package/src/modules/ms-teams/hooks/useMsTeamsChannels.ts +48 -13
- package/src/modules/ms-teams/hooks/useMsTeamsTeams.ts +81 -31
- package/src/modules/slack/hooks/useSlackChannels.ts +82 -32
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
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
|
+
|
|
3
29
|
## 0.11.4
|
|
4
30
|
|
|
5
31
|
### Patch Changes
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const
|
|
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","
|
|
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
|
|
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 {
|
|
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
|
|
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 } =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2
|
-
import { useGuideContext as
|
|
3
|
-
const
|
|
4
|
-
const
|
|
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:
|
|
10
|
-
} =
|
|
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:
|
|
13
|
+
colorMode: s,
|
|
14
14
|
guide: o,
|
|
15
|
-
step:
|
|
15
|
+
step: i
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
18
|
export {
|
|
19
|
-
|
|
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":";;
|
|
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
|
|
2
|
-
import { useGuideContext as
|
|
3
|
-
const
|
|
4
|
-
const
|
|
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:
|
|
7
|
-
} =
|
|
6
|
+
colorMode: r
|
|
7
|
+
} = s, c = u(e.store, (n) => e.selectGuides(n, o, t));
|
|
8
8
|
return {
|
|
9
9
|
client: e,
|
|
10
|
-
colorMode:
|
|
11
|
-
guides:
|
|
10
|
+
colorMode: r,
|
|
11
|
+
guides: c
|
|
12
12
|
};
|
|
13
13
|
};
|
|
14
14
|
export {
|
|
15
|
-
|
|
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 {
|
|
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
|
|
2
|
-
import
|
|
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
|
|
8
|
-
const
|
|
9
|
-
function
|
|
10
|
-
teamId:
|
|
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
|
|
14
|
-
knockMsTeamsChannelId:
|
|
15
|
-
tenantId:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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:
|
|
26
|
-
isLoading:
|
|
27
|
-
isValidating:
|
|
28
|
-
mutate:
|
|
29
|
-
} =
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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 } =
|
|
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 {
|
|
2
|
-
import
|
|
3
|
-
import { useKnockClient as
|
|
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
|
|
8
|
-
const
|
|
9
|
-
function
|
|
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
|
|
16
|
-
knockMsTeamsChannelId:
|
|
17
|
-
tenantId:
|
|
18
|
-
connectionStatus:
|
|
19
|
-
} =
|
|
20
|
-
knockChannelId:
|
|
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:
|
|
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:
|
|
30
|
-
error:
|
|
31
|
-
isLoading:
|
|
32
|
-
isValidating:
|
|
33
|
-
setSize:
|
|
34
|
-
mutate:
|
|
35
|
-
} =
|
|
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
|
-
})
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
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
|
|
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":"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"}
|
|
@@ -1,52 +1,56 @@
|
|
|
1
|
-
import { useKnockSlackClient as
|
|
2
|
-
import {
|
|
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
|
|
5
|
-
import { useKnockClient as
|
|
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
|
|
11
|
-
function
|
|
12
|
-
|
|
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
|
|
18
|
-
knockSlackChannelId:
|
|
19
|
-
tenantId:
|
|
20
|
-
connectionStatus:
|
|
21
|
-
} =
|
|
22
|
-
tenant:
|
|
23
|
-
knockChannelId:
|
|
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
|
-
...
|
|
26
|
-
cursor:
|
|
27
|
-
limit: (
|
|
28
|
-
types: (
|
|
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:
|
|
32
|
-
error:
|
|
33
|
-
isLoading:
|
|
34
|
-
isValidating:
|
|
35
|
-
setSize:
|
|
36
|
-
mutate:
|
|
37
|
-
} =
|
|
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
|
-
})
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
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
|
|
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":"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"}
|
|
@@ -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,
|
|
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"
|
|
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,
|
|
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;
|
|
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;
|
|
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;
|
|
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.
|
|
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.
|
|
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 {
|
|
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 } =
|
|
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
|
|
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 =
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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 =
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|