@oxyhq/services
Version:
225 lines (212 loc) • 8.03 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _react = require("react");
var _reactNative = require("react-native");
var _reactNativeGestureHandler = require("react-native-gesture-handler");
var _reactNativeSafeAreaContext = require("react-native-safe-area-context");
var _OxyContext = require("../context/OxyContext.js");
var _reactQuery = require("@tanstack/react-query");
var _FontLoader = require("./FontLoader.js");
var _sonner = require("../../lib/sonner");
var _queryClient = require("../hooks/queryClient.js");
var _storageHelpers = require("../utils/storageHelpers.js");
var _jsxRuntime = require("react/jsx-runtime");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } // Initialize fonts automatically
(0, _FontLoader.setupFonts)();
// Detect if running on web
const isWeb = _reactNative.Platform.OS === 'web';
// Conditionally import components
let KeyboardProvider = ({
children
}) => children;
let BottomSheetRouter = null;
let SignInModal = null;
// KeyboardProvider only on native
if (!isWeb) {
try {
KeyboardProvider = require('react-native-keyboard-controller').KeyboardProvider;
} catch {
// KeyboardProvider not available
}
}
// BottomSheetRouter works on all platforms
try {
BottomSheetRouter = require('./BottomSheetRouter').default;
} catch {
// BottomSheetRouter not available
}
// SignInModal works on all platforms
try {
SignInModal = require('./SignInModal').default;
} catch {
// SignInModal not available
}
/**
* OxyProvider - Universal provider for Expo apps (native + web)
*
* Zero-config authentication and session management:
* - Native (iOS/Android): Keychain-based identity, bottom sheet auth UI
* - Web: FedCM cross-domain SSO, popup fallback
*
* Usage:
* ```tsx
* import { OxyProvider, useAuth } from '@oxyhq/services';
*
* function App() {
* return (
* <OxyProvider baseURL="https://api.oxy.so">
* <YourApp />
* </OxyProvider>
* );
* }
*
* function MyComponent() {
* const { isAuthenticated, user, signIn, signOut } = useAuth();
*
* if (!isAuthenticated) {
* return <OxySignInButton />;
* }
* return <Text>Welcome, {user?.username}!</Text>;
* }
* ```
*/
const OxyProvider = ({
oxyServices,
children,
onAuthStateChange,
storageKeyPrefix,
baseURL,
authWebUrl,
authRedirectUri,
queryClient: providedQueryClient
}) => {
// Simple storage initialization for query persistence
const storageRef = (0, _react.useRef)(null);
const queryClientRef = (0, _react.useRef)(null);
const [queryClient, setQueryClient] = (0, _react.useState)(null);
(0, _react.useEffect)(() => {
if (providedQueryClient) {
queryClientRef.current = providedQueryClient;
setQueryClient(providedQueryClient);
return;
}
// Initialize storage and create query client
let mounted = true;
(0, _storageHelpers.createPlatformStorage)().then(storage => {
if (mounted && !queryClientRef.current) {
storageRef.current = storage;
const client = (0, _queryClient.createQueryClient)(storage);
queryClientRef.current = client;
setQueryClient(client);
}
}).catch(error => {
// If storage fails, create query client without persistence
if (mounted && !queryClientRef.current) {
if (__DEV__) {
console.warn('[OxyProvider] Failed to initialize storage for query persistence', error);
}
const client = (0, _queryClient.createQueryClient)(null);
queryClientRef.current = client;
setQueryClient(client);
}
});
return () => {
mounted = false;
};
}, [providedQueryClient]);
// Hook React Query focus manager into app state (native) or visibility (web)
(0, _react.useEffect)(() => {
if (isWeb) {
// Web: use document visibility
const handleVisibilityChange = () => {
_reactQuery.focusManager.setFocused(document.visibilityState === 'visible');
};
document.addEventListener('visibilitychange', handleVisibilityChange);
return () => {
document.removeEventListener('visibilitychange', handleVisibilityChange);
};
} else {
// Native: use AppState
const subscription = _reactNative.AppState.addEventListener('change', state => {
_reactQuery.focusManager.setFocused(state === 'active');
});
return () => {
subscription.remove();
};
}
}, []);
// Setup network status monitoring for offline detection
(0, _react.useEffect)(() => {
let cleanup;
const setupNetworkMonitoring = async () => {
try {
if (isWeb) {
// Web: use navigator.onLine
_reactQuery.onlineManager.setOnline(navigator.onLine);
const handleOnline = () => _reactQuery.onlineManager.setOnline(true);
const handleOffline = () => _reactQuery.onlineManager.setOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
cleanup = () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
} else {
// Native: try to use NetInfo
try {
const NetInfo = await Promise.resolve().then(() => _interopRequireWildcard(require('@react-native-community/netinfo')));
const state = await NetInfo.default.fetch();
_reactQuery.onlineManager.setOnline(state.isConnected ?? true);
const unsubscribe = NetInfo.default.addEventListener(state => {
_reactQuery.onlineManager.setOnline(state.isConnected ?? true);
});
cleanup = () => unsubscribe();
} catch {
// NetInfo not available, default to online
_reactQuery.onlineManager.setOnline(true);
}
}
} catch (error) {
// Default to online if detection fails
_reactQuery.onlineManager.setOnline(true);
}
};
setupNetworkMonitoring();
return () => {
cleanup?.();
};
}, []);
// Ensure we have a valid QueryClient
if (!queryClient) {
return null;
}
// Core content that works on all platforms
const coreContent = /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactQuery.QueryClientProvider, {
client: queryClient,
children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_OxyContext.OxyContextProvider, {
oxyServices: oxyServices,
baseURL: baseURL,
authWebUrl: authWebUrl,
authRedirectUri: authRedirectUri,
storageKeyPrefix: storageKeyPrefix,
onAuthStateChange: onAuthStateChange,
children: [children, BottomSheetRouter && /*#__PURE__*/(0, _jsxRuntime.jsx)(BottomSheetRouter, {}), SignInModal && /*#__PURE__*/(0, _jsxRuntime.jsx)(SignInModal, {}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_sonner.Toaster, {})]
})
});
// All platforms use same wrapper (KeyboardProvider is passthrough on web)
return /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeSafeAreaContext.SafeAreaProvider, {
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNativeGestureHandler.GestureHandlerRootView, {
style: {
flex: 1
},
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(KeyboardProvider, {
children: coreContent
})
})
});
};
var _default = exports.default = OxyProvider;
//# sourceMappingURL=OxyProvider.js.map