diff --git a/.clasp.json b/.clasp.json index 93e6d50..304a7d0 100644 --- a/.clasp.json +++ b/.clasp.json @@ -1,7 +1,7 @@ { + "scriptId": "1IzIEW1uGP4WWdowrlYyNEwhasdXObby_Lik-xwRXn3uWFUitm19SHAhU", "rootDir": "dist", - "scriptId": "1EBKJNiPs_XxntcBHxMKJgfBr7089AHhT3boz7_Lkc6AD0hjsQavkIjxz", "parentId": [ - "1UAVn7sP1pTutyOM2i95jxQz6sjVr2ZrxhG82uLIdd70" + "1NVNWs4Wdj2ib6hhPgq1kzA-BMtydvk_SnxoK1K2yX5o" ] -} \ No newline at end of file +} diff --git a/package.json b/package.json index 37b4a80..60c26ac 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,14 @@ { "name": "google-plugin", - "version": "1.0.0", + "version": "1.2.0", "type": "module", "scripts": { "dev": "vite", "lint": "eslint .", - "login": "clasp login", - "setup": "rimraf .clasp.json && mkdirp dist && clasp create --type sheets --title \"My React Project\" --rootDir ./dist && mv ./dist/.clasp.json ./.clasp.json && rimraf dist", - "open": "clasp open --addon", - "push": "clasp push", + "login": "npx clasp login", + "setup": "rimraf .clasp.json && mkdirp dist && npx clasp create --type sheets --title \"My React Project\" --rootDir ./dist && mv ./dist/.clasp.json ./.clasp.json && rimraf dist", + "open": "npx clasp open --addon", + "push": "npx clasp push", "setup:https": "mkdirp certs && mkcert -key-file ./certs/key.pem -cert-file ./certs/cert.pem localhost 127.0.0.1", "build:dev": "tsc && vite build --mode development", "build:test": "tsc && NODE_ENV=test vite build --mode test", diff --git a/src/analytics/analytics.ts b/src/analytics/analytics.ts new file mode 100644 index 0000000..d7d0515 --- /dev/null +++ b/src/analytics/analytics.ts @@ -0,0 +1,79 @@ +class Analytics { + + private async getServerFunctions() { + try { + // Dynamic import to avoid circular dependency issues + const { serverFunctions } = await import('../client/utils/serverFunctions'); + return serverFunctions; + } catch (error) { + console.warn('Failed to import serverFunctions:', error); + return null; + } + } + + public sendEvent(eventName: string, eventID:string, errorMessage?: string, diagramType?:string, userLoginState: boolean = true) { + const analyticsID = getAnalyticsID(); + const pluginID= "google-docs-plugin"; + const pluginSource = 'googledocs'; + const payload = { + analyticsID, + pluginID, + eventName, + eventID, + userLoginState, + pluginSource, + errorMessage, + diagramType + }; + + this.getServerFunctions().then(serverFunctions => { + if (serverFunctions && serverFunctions.sendAnalyticsEvent) { + serverFunctions.sendAnalyticsEvent(payload).catch(error => { + console.error('Failed to send analytics event:', error); + }); + } else { + console.warn('Analytics service unavailable - serverFunctions not available'); + } + }).catch(error => { + console.error('Failed to send analytics event:', error); + }); + } + + + public trackLogin() { + this.sendEvent('Google Docs Plugin Logged In','GOOGLE_DOCS_PLUGIN_LOGIN'); + } + + public trackLogout() { + this.sendEvent('Google Docs Logged Out','GOOGLE_DOCS_PLUGIN_LOGOUT', undefined, undefined, false); + } + + public trackBrowseDiagram() { + this.sendEvent('Google Docs Browse Diagram','GOOGLE_DOCS_PLUGIN_BROWSE_DIAGRAM'); + } + + public trackNewDiagram() { + this.sendEvent('Google Docs New Diagram', 'GOOGLE_DOCS_PLUGIN_NEW_DIAGRAM'); + } + + public trackEditDiagram() { + this.sendEvent('Google Docs Edit Diagram', 'GOOGLE_DOCS_PLUGIN_EDIT_DIAGRAM'); + } + + public trackUpdateAllDiagrams() { + this.sendEvent('Google Docs Update All Diagrams', 'GOOGLE_DOCS_PLUGIN_UPDATE_ALL_DIAGRAMS'); + } +} + +function getAnalyticsID() { + const STORAGE_KEY = 'MERMAIDCHART_ANALYTICS_ID'; + + let id = localStorage.getItem(STORAGE_KEY); + if (!id) { + id = crypto.randomUUID(); + localStorage.setItem(STORAGE_KEY, id); + } + return id; +} + +export default new Analytics(); \ No newline at end of file diff --git a/src/analytics/httpClient.ts b/src/analytics/httpClient.ts new file mode 100644 index 0000000..82847d1 --- /dev/null +++ b/src/analytics/httpClient.ts @@ -0,0 +1,26 @@ +import axios, { AxiosInstance, AxiosResponse, AxiosError } from 'axios'; +import { prodUrl } from '../config/urls'; + +const ANALYTICS_BASE_URL = prodUrl; + +const httpClient: AxiosInstance = axios.create({ + baseURL: ANALYTICS_BASE_URL, + headers: { + 'Content-Type': 'application/json', + }, + timeout: 10000, // 10 second timeout +}); + +httpClient.interceptors.response.use( + (response: AxiosResponse) => response, + (error: AxiosError) => { + if (error.code === 'ERR_NETWORK') { + console.warn('Analytics endpoint unreachable - continuing without tracking'); + return Promise.resolve({ data: null, status: 200, statusText: 'OK', headers: {}, config: error.config }); + } + console.error('HTTP Client error:', error); + return Promise.reject(error); + } +); + +export default httpClient; \ No newline at end of file diff --git a/src/client/components/button.module.css b/src/client/components/button.module.css index 3557db1..4143a3b 100644 --- a/src/client/components/button.module.css +++ b/src/client/components/button.module.css @@ -5,13 +5,14 @@ padding: 0.5rem; outline: none; border-radius: 0.375rem; - border: 1px solid rgb(207, 207, 211); - background-color: white; + border: none; + background-color: #E80962; cursor: pointer; + color: white; } .button:hover { - background-color: rgb(0, 66, 235); + background-color: #B20E45; color: white; transition: all 0.2s; } diff --git a/src/client/components/toast.tsx b/src/client/components/toast.tsx new file mode 100644 index 0000000..f062cb4 --- /dev/null +++ b/src/client/components/toast.tsx @@ -0,0 +1,30 @@ +import { Snackbar, Alert, AlertColor } from '@mui/material'; + +interface ToastProps { + open: boolean; + message: string; + severity: AlertColor; + onClose: () => void; + autoHideDuration?: number; +} + +const Toast = ({ + open, + message, + severity, + onClose, + autoHideDuration = 4000, +}: ToastProps) => ( + + + {message} + + +); + +export default Toast; diff --git a/src/client/create-diagram-dialog/components/create-diagram-dialog.tsx b/src/client/create-diagram-dialog/components/create-diagram-dialog.tsx index 9e42d87..e8dee98 100644 --- a/src/client/create-diagram-dialog/components/create-diagram-dialog.tsx +++ b/src/client/create-diagram-dialog/components/create-diagram-dialog.tsx @@ -1,13 +1,18 @@ import { useEffect, useState } from 'react'; -import { buildUrl, handleDialogClose } from '../../utils/helpers'; -import { serverFunctions } from '../../utils/serverFunctions'; +import { + buildUrl, + handleDialogClose, + compressBase64Image, +} from '../../utils/helpers'; + import useAuth from '../../hooks/useAuth'; -import { CircularProgress, Container, Typography } from '@mui/material'; +import { CircularProgress, Container, Typography, Box } from '@mui/material'; import { showAlertDialog } from '../../utils/alert'; const CreateDiagramDialog = () => { const { authState, authStatus } = useAuth(); const [diagramsUrl, setDiagramsUrl] = useState(''); + const [iframeLoading, setIframeLoading] = useState(true); useEffect(() => { if (!authState?.authorized) return; @@ -31,14 +36,23 @@ const CreateDiagramDialog = () => { }); try { - await serverFunctions.insertBase64ImageWithMetadata( - data.diagramImage, - metadata.toString() - ); + const compressedImage = await compressBase64Image(data.diagramImage); + + // Pass data to sidebar via BroadcastChannel and close immediately + const channel = new BroadcastChannel('diagram_channel'); + channel.postMessage({ + type: 'pendingInsertion', + payload: { + image: compressedImage, + metadata: metadata.toString(), + operation: 'insert', + }, + }); + channel.close(); handleDialogClose(); } catch (error) { - console.error('Error inserting image with metadata', error); - showAlertDialog('Error inserting image, please try again'); + console.error('Error preparing diagram insertion', error); + showAlertDialog('Error preparing diagram, please try again'); } } }; @@ -50,18 +64,21 @@ const CreateDiagramDialog = () => { }; }, []); + const handleIframeLoad = () => { + setIframeLoading(false); + }; + if (authStatus === 'idle' || authStatus === 'loading') { return ( - + ); } @@ -77,28 +94,65 @@ const CreateDiagramDialog = () => { height: '96.5vh', }} > - + Error - + Something went wrong. Please try again later. ); } - return ( -
-