mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-27 20:10:58 +00:00
66 lines
2.2 KiB
TypeScript
66 lines
2.2 KiB
TypeScript
import { useApiLazyQuery, useApiMutation, useApiQuery } from '@ema-platform/api';
|
|
|
|
export interface MySession {
|
|
id: string;
|
|
createdAt: string;
|
|
email: string;
|
|
/** IP address the session was created from — IAM sends no user agent. */
|
|
device: string;
|
|
expiryTime: string;
|
|
refreshCount: number;
|
|
status: string;
|
|
}
|
|
|
|
/** `/sessions/my-sessions` answers with a tuple, not the usual `{items, count}`. */
|
|
type SessionsResponse = [MySession[], number];
|
|
|
|
const SESSIONS_URL = '/sessions/my-sessions';
|
|
const ORDER_BY = 'CreatedAt:DESC';
|
|
|
|
function unwrapList(data: unknown): SessionsResponse {
|
|
if (!Array.isArray(data)) return [[], 0];
|
|
const [items, total] = data as Partial<SessionsResponse>;
|
|
return [items ?? [], total ?? 0];
|
|
}
|
|
|
|
/**
|
|
* The signed-in user's login sessions, and the two ways to end them.
|
|
*
|
|
* Uses the generic query/mutation endpoints rather than its own slice, so
|
|
* freshness comes from `refetch()` rather than cache tags — the same shape as
|
|
* `useTwoFactor`.
|
|
*/
|
|
export function useSessions({ skip, take }: { skip: number; take: number }) {
|
|
const { data, isFetching, refetch } = useApiQuery<SessionsResponse>({
|
|
url: SESSIONS_URL,
|
|
params: { skip, take, orderBy: ORDER_BY },
|
|
});
|
|
const [fetchAll] = useApiLazyQuery<SessionsResponse>();
|
|
const [send, { isLoading: isRevoking }] = useApiMutation();
|
|
|
|
const [sessions, total] = unwrapList(data);
|
|
|
|
/** Every session id the user has, not just the ones on the current page. */
|
|
const allSessionIds = async (): Promise<string[]> => {
|
|
// `total` is one page stale at worst; ask for a page big enough to cover it
|
|
// growing between render and click.
|
|
const result = await fetchAll({
|
|
url: SESSIONS_URL,
|
|
params: { skip: 0, take: Math.max(total, sessions.length) + 20, orderBy: ORDER_BY },
|
|
}).unwrap();
|
|
return unwrapList(result)[0].map((s) => s.id);
|
|
};
|
|
|
|
const revoke = async (ids: string[]) => {
|
|
if (ids.length === 0) return;
|
|
await send(
|
|
ids.length === 1
|
|
? { url: `/sessions/revoke/${ids[0]}`, method: 'DELETE' }
|
|
: { url: '/sessions/bulk-revoke', method: 'POST', body: { sessionIds: ids } },
|
|
).unwrap();
|
|
await refetch();
|
|
};
|
|
|
|
return { sessions, total, isFetching, refetch, revoke, isRevoking, allSessionIds };
|
|
}
|