@open-game-system/app-bridge-react
Version:
React hooks and components for the app-bridge ecosystem
1 lines • 9.08 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.tsx"],"sourcesContent":["import {\n createContext,\n memo,\n type ReactNode,\n useCallback,\n useContext,\n useMemo,\n useSyncExternalStore,\n} from \"react\";\nimport type {\n BridgeStores,\n Store,\n Bridge,\n} from \"@open-game-system/app-bridge-types\";\n\ninterface BridgeContextValue<TStores extends BridgeStores> {\n bridge: Bridge<TStores>;\n}\n\nexport function createBridgeContext<TStores extends BridgeStores>() {\n // Create a dummy bridge that throws on any method call\n const throwBridge = new Proxy({} as Bridge<TStores>, {\n get() {\n throw new Error(\n \"Bridge not found in context. Did you forget to wrap your app in <BridgeContext.Provider bridge={...}>?\"\n );\n },\n });\n\n const BridgeContext = createContext<BridgeContextValue<TStores>>({\n bridge: throwBridge,\n });\n\n const Provider = memo(\n ({\n children,\n bridge,\n }: {\n children: ReactNode;\n bridge: Bridge<TStores>;\n }) => {\n const value = useMemo(() => ({ bridge }), [bridge]);\n\n return (\n <BridgeContext.Provider value={value}>\n {children}\n </BridgeContext.Provider>\n );\n }\n );\n Provider.displayName = \"BridgeProvider\";\n\n /**\n * Internal hook to access the bridge instance\n * @returns The bridge instance\n * @internal\n */\n function useBridge(): Bridge<TStores> {\n const context = useContext(BridgeContext);\n return context.bridge;\n }\n\n /**\n * Create a set of hooks and components for interacting with a specific store\n * @param storeKey The key of the store to interact with\n * @returns A set of hooks and components for the specific store\n */\n function createStoreContext<K extends keyof TStores>(storeKey: K) {\n type StoreType = Store<TStores[K][\"state\"], TStores[K][\"events\"]>;\n type StoreState = TStores[K][\"state\"];\n \n // Create a dummy store that throws on any method call\n const throwStore = new Proxy({} as StoreType, {\n get() {\n throw new Error(\n `Store \"${String(storeKey)}\" is not available. Make sure to use the store hooks inside a StoreContext.Provider.`\n );\n },\n });\n \n // Create a context with a throw store as default - this ensures type safety\n // while still throwing helpful errors when accessed outside a provider\n const StoreContext = createContext<StoreType>(throwStore);\n StoreContext.displayName = `Store<${String(storeKey)}>`;\n\n /**\n * Provider component that automatically subscribes to store availability \n * and provides the store when it becomes available\n */\n const Provider = memo(({ children }: { children: ReactNode }) => {\n const bridge = useBridge();\n \n // Subscribe to store availability\n const getStore = useCallback(() => {\n return bridge.getStore(storeKey) || null;\n }, [bridge]);\n \n // Subscribe to changes in store availability\n const subscribe = useCallback((callback: () => void) => {\n return bridge.subscribe(callback);\n }, [bridge]);\n \n // Use sync external store to track store availability\n const store = useSyncExternalStore(\n subscribe,\n getStore,\n getStore // Same for server\n );\n \n // Only render children if store is available\n if (!store) return null;\n \n return (\n <StoreContext.Provider value={store}>\n {children}\n </StoreContext.Provider>\n );\n });\n Provider.displayName = `Store.Provider<${String(storeKey)}>`;\n\n /**\n * Loading component that renders children only when the bridge is supported \n * but the store is not yet available\n */\n const Loading = memo(({ children }: { children: ReactNode }) => {\n const bridge = useBridge();\n \n // Check if bridge is supported - this is a static property that won't change\n const isSupported = bridge.isSupported();\n \n // Subscribe to store availability\n const getStoreAvailability = useCallback(() => {\n return bridge.getStore(storeKey);\n }, [bridge]);\n \n // Subscribe to changes in store availability\n const subscribe = useCallback((callback: () => void) => {\n return bridge.subscribe(callback);\n }, [bridge]);\n \n // Use sync external store to track store availability\n const store = useSyncExternalStore(\n subscribe,\n getStoreAvailability,\n getStoreAvailability // Same for server\n );\n \n // Only render children if bridge is supported but store is not available\n return isSupported && !store ? <>{children}</> : null;\n });\n Loading.displayName = `Store.Loading<${String(storeKey)}>`;\n\n /**\n * Hook to access the store.\n * Must be used inside a Store.Provider component.\n * @throws Error if used outside of Store.Provider\n * @returns The store instance\n */\n function useStore(): StoreType {\n return useContext(StoreContext);\n }\n\n /**\n * Hook to select data from the store.\n * Must be used inside a Store.Provider component.\n * @param selector Function to select data from the store state\n * @returns The selected data from the store\n * @throws Error if used outside of Store.Provider\n */\n function useSelector<T>(selector: (state: StoreState) => T): T {\n const store = useStore();\n const memoizedSelector = useMemo(() => selector, [selector]);\n \n return useSyncExternalStore(\n store.subscribe,\n () => memoizedSelector(store.getSnapshot()),\n () => memoizedSelector(store.getSnapshot()) // Same for server\n );\n }\n\n return {\n Provider,\n Loading,\n useStore,\n useSelector,\n };\n }\n\n /**\n * Renders children only when the bridge is supported\n */\n const Supported = memo(({ children }: { children: ReactNode }) => {\n const bridge = useBridge();\n const isSupported = bridge.isSupported();\n return isSupported ? <>{children}</> : null;\n });\n Supported.displayName = \"BridgeSupported\";\n\n /**\n * Renders children only when the bridge is not supported\n */\n const Unsupported = memo(({ children }: { children: ReactNode }) => {\n const bridge = useBridge();\n const isSupported = bridge.isSupported();\n return !isSupported ? <>{children}</> : null;\n });\n Unsupported.displayName = \"BridgeUnsupported\";\n\n return {\n Provider,\n createStoreContext,\n Supported,\n Unsupported,\n };\n} "],"mappings":"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","names":["Provider"]}