UNPKG

@shopify/shop-minis-react

Version:

React component library for Shopify Shop Minis with Tailwind CSS v4 support (source-only, requires TypeScript)

96 lines (79 loc) 2.94 kB
import {useCallback, useRef} from 'react' import { GeneratedTokenData, UserTokenGenerateUserErrors, } from '@shopify/shop-minis-platform' import {useHandleAction} from '../../internal/useHandleAction' import {useShopActions} from '../../internal/useShopActions' interface UseGenerateUserTokenReturns { /** * Generates a temporary token for the user. * Tokens are cached in memory and reused if still valid (with a 5-minute expiry buffer). * A new token is automatically generated when the cached token is expired or missing. */ generateUserToken: () => Promise<{ data: GeneratedTokenData userErrors?: UserTokenGenerateUserErrors[] }> } interface CachedTokenResponse { data: GeneratedTokenData userErrors?: UserTokenGenerateUserErrors[] } export function useGenerateUserToken(): UseGenerateUserTokenReturns { const {generateUserToken: generateUserTokenAction} = useShopActions() const wrappedGenerateToken = useHandleAction(generateUserTokenAction) const cachedResponse = useRef<CachedTokenResponse | null>(null) const pendingRequest = useRef<Promise<CachedTokenResponse> | null>(null) const isTokenValid = useCallback( (response: CachedTokenResponse | null): boolean => { if (!response?.data?.token || !response.data.expiresAt) { return false } try { const expiryTime = new Date(response.data.expiresAt).getTime() const now = Date.now() // 5 minutes buffer to ensure token doesn't expire mid-request const bufferTime = 5 * 60 * 1000 return expiryTime - bufferTime > now } catch { // If date parsing fails, consider token invalid return false } }, [] ) const generateUserToken = useCallback(async (): Promise<CachedTokenResponse> => { // Check if cached token exists and is still valid if (cachedResponse.current && isTokenValid(cachedResponse.current)) { return cachedResponse.current } // If there's already a pending request, return the same promise if (pendingRequest.current) { return pendingRequest.current } // Create new request and store the promise pendingRequest.current = (async () => { try { const response = await wrappedGenerateToken() // Only cache if we got a valid token if (response.data?.token && response.data?.expiresAt) { cachedResponse.current = response } return response } catch (error) { // Clear cache on error to ensure fresh token on next attempt cachedResponse.current = null throw error } finally { // Clear pending request after completion (success or failure) pendingRequest.current = null } })() return pendingRequest.current }, [wrappedGenerateToken, isTokenValid]) return { generateUserToken, } }