livesheets-sdk
Version:
LiveSheets SDK for Google Sheets integration with Lovable apps. OAuth onboarding URL: https://livesheets-backend-624058308714.europe-north1.run.app/onboarding
244 lines (243 loc) • 8.13 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.useLiveSheets = useLiveSheets;
exports.useSheetData = useSheetData;
const react_1 = require("react");
const index_1 = require("./index");
/**
* React Hook for LiveSheets integration
*
* @example
* ```tsx
* import { useLiveSheets } from 'livesheets-sdk/react';
*
* function MyComponent() {
* const {
* isAuthenticated,
* data,
* connect,
* selectSheet
* } = useLiveSheets();
*
* if (!isAuthenticated) {
* return <button onClick={connect}>Connect Google Sheets</button>;
* }
*
* return <div>{data.length} rows loaded</div>;
* }
* ```
*/
function useLiveSheets(options = {}) {
const { autoConnect = false, autoLoadData = true, onAuthSuccess, onAuthError, onDataLoaded, backendUrl } = options;
// State
const [isConnecting, setIsConnecting] = (0, react_1.useState)(false);
const [isSelectingSheet, setIsSelectingSheet] = (0, react_1.useState)(false);
const [isLoading, setIsLoading] = (0, react_1.useState)(false);
const [error, setError] = (0, react_1.useState)(null);
const [jwt, setJwt] = (0, react_1.useState)(null);
const [client, setClient] = (0, react_1.useState)(null);
const [data, setData] = (0, react_1.useState)([]);
const [sheetMetadata, setSheetMetadata] = (0, react_1.useState)(null);
// Refs to prevent stale closures
const authRef = (0, react_1.useRef)();
const clientRef = (0, react_1.useRef)(null);
// Initialize auth helper
(0, react_1.useEffect)(() => {
authRef.current = index_1.LiveSheetsClient.getAuth({ backendUrl });
}, [backendUrl]);
// Check for existing auth on mount
(0, react_1.useEffect)(() => {
const auth = authRef.current;
if (!auth)
return;
const token = auth.getJWT();
if (token) {
setJwt(token);
const newClient = new index_1.LiveSheetsClient(token);
setClient(newClient);
clientRef.current = newClient;
// Load saved metadata
const savedMetadata = localStorage.getItem('livesheets_metadata');
if (savedMetadata) {
try {
setSheetMetadata(JSON.parse(savedMetadata));
if (autoLoadData) {
loadData(newClient);
}
}
catch (e) {
console.error('Failed to parse saved metadata:', e);
}
}
onAuthSuccess?.();
}
else if (autoConnect) {
connect();
}
}, [autoConnect, autoLoadData]); // eslint-disable-line react-hooks/exhaustive-deps
// Load data helper
const loadData = (0, react_1.useCallback)(async (clientInstance) => {
const activeClient = clientInstance || clientRef.current;
if (!activeClient)
return;
setIsLoading(true);
setError(null);
try {
const [rows, metadata] = await Promise.all([
activeClient.listRows(),
activeClient.getMetadata()
]);
setData(rows);
setSheetMetadata(metadata);
// Save metadata for next session
localStorage.setItem('livesheets_metadata', JSON.stringify(metadata));
onDataLoaded?.(rows);
}
catch (err) {
const error = err instanceof Error ? err : new Error('Failed to load data');
setError(error);
console.error('Failed to load sheet data:', error);
}
finally {
setIsLoading(false);
}
}, [onDataLoaded]);
// Connect action
const connect = (0, react_1.useCallback)(() => {
const auth = authRef.current;
if (!auth)
return;
try {
setIsConnecting(true);
auth.startOAuth();
}
catch (err) {
const error = err instanceof Error ? err : new Error('Failed to start OAuth');
setError(error);
setIsConnecting(false);
onAuthError?.(error);
}
}, [onAuthError]);
// Disconnect action
const disconnect = (0, react_1.useCallback)(() => {
const auth = authRef.current;
if (!auth)
return;
auth.logout();
setJwt(null);
setClient(null);
clientRef.current = null;
setData([]);
setSheetMetadata(null);
setError(null);
localStorage.removeItem('livesheets_metadata');
}, []);
// Select sheet action
const selectSheet = (0, react_1.useCallback)(async () => {
if (!jwt) {
setError(new Error('Not authenticated'));
return;
}
setIsSelectingSheet(true);
setError(null);
try {
await index_1.LiveSheetsClient.openSheetPicker({
jwt,
onSuccess: async (result) => {
setIsSelectingSheet(false);
// Reload data after sheet selection
await loadData();
},
onCancel: () => {
setIsSelectingSheet(false);
}
});
}
catch (err) {
const error = err instanceof Error ? err : new Error('Failed to open sheet picker');
setError(error);
setIsSelectingSheet(false);
}
}, [jwt, loadData]);
// Refresh action
const refresh = (0, react_1.useCallback)(async () => {
if (!clientRef.current) {
setError(new Error('Not authenticated'));
return;
}
await loadData();
}, [loadData]);
return {
// Auth state
isAuthenticated: !!jwt,
isConnecting,
// Sheet state
hasSheet: !!sheetMetadata,
isSelectingSheet,
sheetMetadata,
// Data state
data,
isLoading,
error,
// Actions
connect,
disconnect,
selectSheet,
refresh,
// Direct client access
client
};
}
/**
* Simple hook for just reading sheet data
*
* @example
* ```tsx
* const { data, loading, error } = useSheetData();
* ```
*/
function useSheetData(jwt) {
const [data, setData] = (0, react_1.useState)([]);
const [loading, setLoading] = (0, react_1.useState)(true);
const [error, setError] = (0, react_1.useState)(null);
(0, react_1.useEffect)(() => {
const loadData = async () => {
try {
setLoading(true);
setError(null);
// Try to get JWT from auth if not provided
const token = jwt || index_1.LiveSheetsClient.getAuth().getJWT();
if (!token) {
throw new Error('No JWT token available');
}
const client = new index_1.LiveSheetsClient(token);
const rows = await client.listRows();
setData(rows);
}
catch (err) {
setError(err instanceof Error ? err : new Error('Failed to load data'));
}
finally {
setLoading(false);
}
};
loadData();
}, [jwt]);
return { data, loading, error };
}
// Export everything from main module
__exportStar(require("./index"), exports);