From e33f0943bf0552e2bf5ecf6ed6635578acef7b29 Mon Sep 17 00:00:00 2001 From: Rob Gordon Date: Sun, 26 Feb 2023 18:01:50 -0500 Subject: [PATCH 01/19] fix: move doc details into own store --- app/package.json | 9 +- app/src/components/ShareDialog.tsx | 42 ++- app/src/components/TextEditor.tsx | 9 + app/src/lib/prepareChart/prepareChart.ts | 6 +- app/src/lib/useDoc.ts | 27 +- pnpm-lock.yaml | 452 ++++++++++++++++++++++- 6 files changed, 524 insertions(+), 21 deletions(-) diff --git a/app/package.json b/app/package.json index 9f25eb1f..3d5fceda 100644 --- a/app/package.json +++ b/app/package.json @@ -54,8 +54,9 @@ "@sentry/tracing": "^7.38.0", "@stripe/react-stripe-js": "^1.16.4", "@stripe/stripe-js": "^1.46.0", - "@supabase/gotrue-js": "^2", - "@supabase/supabase-js": "^2", + "@supabase/gotrue-js": "^2.12.1", + "@supabase/realtime-js": "^2.6.0", + "@supabase/supabase-js": "^2.8.0", "@svgr/webpack": "^6.3.1", "@testing-library/jest-dom": "^5.11.4", "@testing-library/react": "^11.1.0", @@ -107,6 +108,10 @@ "svgo": "^2.8.0", "use-debounce": "^5.2.1", "web-vitals": "^1.0.1", + "y-monaco": "^0.1.4", + "y-protocols": "^1.0.5", + "y-websocket": "^1.4.5", + "yjs": "^13.5.47", "zustand": "^4.3.3" }, "eslintConfig": { diff --git a/app/src/components/ShareDialog.tsx b/app/src/components/ShareDialog.tsx index 23f3c016..487a9c1a 100644 --- a/app/src/components/ShareDialog.tsx +++ b/app/src/components/ShareDialog.tsx @@ -13,6 +13,7 @@ import { } from "react"; import { useMutation } from "react-query"; +import { useChartId } from "../lib/hooks"; import { track_copyEditableShareLink, track_copyFullscreenShareLink, @@ -24,7 +25,13 @@ import { } from "../lib/logsnag"; import { toMermaidJS } from "../lib/mermaid"; import { makeChartPublic } from "../lib/queries"; -import { docToString, useDoc, useDocDetails } from "../lib/useDoc"; +import { supabase } from "../lib/supabaseClient"; +import { + docToString, + useDoc, + useDocDetails, + useDocDetailsStore, +} from "../lib/useDoc"; import { useGraphStore } from "../lib/useGraphStore"; import { Box, Type } from "../slang"; import { AppContext } from "./AppContext"; @@ -43,6 +50,7 @@ export default function ShareDialog() { const fullscreen = `${new URL(window.location.href).origin}/f#${shareLink}`; const readOnly = `${new URL(window.location.href).origin}/c#${shareLink}`; const editable = `${new URL(window.location.href).origin}/n#${shareLink}`; + const chartId = useChartId(); return ( + {isHosted ? ( + + Share TODO + + + ) : null} ); } @@ -297,11 +331,11 @@ function HostedOptions() { { onSuccess: (result) => { if (!result) return; - useDoc.setState( + useDocDetailsStore.setState( (state) => { return produce(state, (draft) => { - draft.details.isPublic = result.isPublic; - draft.details.publicId = result.publicId; + draft.isPublic = result.isPublic; + draft.publicId = result.publicId; }); }, false, diff --git a/app/src/components/TextEditor.tsx b/app/src/components/TextEditor.tsx index b992e7a2..0da54869 100644 --- a/app/src/components/TextEditor.tsx +++ b/app/src/components/TextEditor.tsx @@ -79,6 +79,15 @@ export function TextEditor({ monacoRef.current.editor.setModelLanguage(model, languageId); }, [editorIsReady, languageId]); + // TODO: Remove this + // Bind to realtime updates + // useEffect(() => { + // if (!bindToRealtime) return; + // if (!editorIsReady) return; + // if (!editorRef.current) return; + // initRealtime(editorRef.current); + // }, [bindToRealtime, editorIsReady, editorRef]); + return ( ; - /** Details are *not* stored in DB. They represent the chart currently being viewed. */ - details: Details; +}; + +const initialDetails: Details = { + id: "", + title: "", + isHosted: false, }; export const initialDoc = { text: "", meta: {}, - details: { - id: "", - title: "", - isHosted: false, - }, + details: initialDetails, }; export const useDoc = create( @@ -54,6 +55,7 @@ export const useDoc = create( ) ); +/** Turns doc into the string we store in the database */ export function docToString(doc: Doc) { const { text, meta } = doc; return [ @@ -71,6 +73,15 @@ export const useParseError = create<{ error: string; errorFromStyle: string }>( }) ); +/** + * Custom useDocDetails store + */ +export const useDocDetailsStore = create
()( + devtools(() => initialDetails, { + name: "useDocDetailsStore", + }) +); + /** * Get a type-safe version of any property * of the doc details @@ -79,5 +90,5 @@ export function useDocDetails( prop: K, fallback?: Details[K] ) { - return useDoc((state) => state.details[prop] || fallback) as Details[K]; + return useDocDetailsStore((state) => state[prop] || fallback) as Details[K]; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c92027fe..a9f9a688 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -93,8 +93,9 @@ importers: '@sentry/tracing': ^7.38.0 '@stripe/react-stripe-js': ^1.16.4 '@stripe/stripe-js': ^1.46.0 - '@supabase/gotrue-js': ^2 - '@supabase/supabase-js': ^2 + '@supabase/gotrue-js': ^2.12.1 + '@supabase/realtime-js': ^2.6.0 + '@supabase/supabase-js': ^2.8.0 '@svgr/webpack': ^6.3.1 '@testing-library/jest-dom': ^5.11.4 '@testing-library/react': ^11.1.0 @@ -196,6 +197,10 @@ importers: use-debounce: ^5.2.1 web-vitals: ^1.0.1 web-worker: ^1.2.0 + y-monaco: ^0.1.4 + y-protocols: ^1.0.5 + y-websocket: ^1.4.5 + yjs: ^13.5.47 zustand: ^4.3.3 dependencies: '@lingui/core': 3.14.0 @@ -223,8 +228,9 @@ importers: '@sentry/tracing': 7.38.0 '@stripe/react-stripe-js': 1.16.4_wuutodrnuujxynxjikyep652cy '@stripe/stripe-js': 1.46.0 - '@supabase/gotrue-js': 2.1.0 - '@supabase/supabase-js': 2.0.2 + '@supabase/gotrue-js': 2.12.1 + '@supabase/realtime-js': 2.6.0 + '@supabase/supabase-js': 2.8.0 '@svgr/webpack': 6.4.0 '@testing-library/jest-dom': 5.16.5 '@testing-library/react': 11.2.7_sfoxds7t5ydpegc3knd667wn6m @@ -276,6 +282,10 @@ importers: svgo: 2.8.0 use-debounce: 5.2.1_react@17.0.2 web-vitals: 1.1.2 + y-monaco: 0.1.4_xkgk6mmjz52pc4du3xhfls7v24 + y-protocols: 1.0.5 + y-websocket: 1.4.5_yjs@13.5.47 + yjs: 13.5.47 zustand: 4.3.3_immer@9.0.16+react@17.0.2 devDependencies: '@lingui/cli': 3.14.0_tm4webiq2u57mg24iaejc4km4e @@ -332,6 +342,15 @@ importers: typescript: 4.8.4 web-worker: 1.2.0 + wss: + specifiers: + '@hocuspocus/server': ^1.1.0 + nodemon: ^2.0.20 + dependencies: + '@hocuspocus/server': 1.1.0_bjezgfodkvttvr5cbwk4e3pgwe + devDependencies: + nodemon: 2.0.20 + packages: /@adobe/css-tools/4.0.1: @@ -1979,6 +1998,34 @@ packages: graphql: 15.8.0 dev: false + /@hocuspocus/common/1.1.0: + resolution: {integrity: sha512-7a9bw7wi4fQUGNZuytlratjVuLUKm64jVlHWojS0Z06HCzokoJ/QP5H+1WtyiDM9OBiXaKGdZao2j+qxoUTPDg==} + dependencies: + lib0: 0.2.63 + dev: false + + /@hocuspocus/server/1.1.0_bjezgfodkvttvr5cbwk4e3pgwe: + resolution: {integrity: sha512-lcWOjbq0fl9CookGzqkxmwq2s5qQosa8dHvYYvRul7wnt3ha0S4grO4G8a+lU70EmseG9xJxeH/bwGwhDCftJA==} + peerDependencies: + y-protocols: ^1.0.5 + yjs: ^13.5.29 + dependencies: + '@hocuspocus/common': 1.1.0 + '@types/async-lock': 1.4.0 + '@types/uuid': 9.0.1 + '@types/ws': 8.5.3 + async-lock: 1.4.0 + kleur: 4.1.5 + lib0: 0.2.63 + uuid: 9.0.0 + ws: 8.11.0 + y-protocols: 1.0.5 + yjs: 13.5.47 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + /@humanwhocodes/config-array/0.10.7: resolution: {integrity: sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==} engines: {node: '>=10.10.0'} @@ -4149,6 +4196,14 @@ packages: - encoding dev: false + /@supabase/gotrue-js/2.12.1: + resolution: {integrity: sha512-r8Jfq8FvP6q4kp7sI33X1RWfEEHzJFu9uM1Q6HgiDVkY89NNgqYy2kxaRGtidPFllND7vpcJUcpoWS5oq+4u0g==} + dependencies: + cross-fetch: 3.1.5 + transitivePeerDependencies: + - encoding + dev: false + /@supabase/postgrest-js/1.1.0: resolution: {integrity: sha512-qkY8TqIu5sJuae8gjeDPjEqPrefzcTraW9PNSVJQHq4TEv98ZmwaXGwBGz0bVL63bqrGA5hqREbQHkANUTXrvA==} dependencies: @@ -4157,6 +4212,14 @@ packages: - encoding dev: false + /@supabase/postgrest-js/1.4.1: + resolution: {integrity: sha512-aruqwV/aTggkM7OVv2JinCeXmRMKHJCZpkuS1nuoa0NgLw7g3NyILSyWOKYTBJ/PxE/zXtWsBhdxFzaaNz5uxg==} + dependencies: + cross-fetch: 3.1.5 + transitivePeerDependencies: + - encoding + dev: false + /@supabase/realtime-js/2.1.0: resolution: {integrity: sha512-iplLCofTeYjnx9FIOsIwHLhMp0+7UVyiA4/sCeq40VdOgN9eTIhjEno9Tgh4dJARi4aaXoKfRX1DTxgZaOpPAw==} dependencies: @@ -4166,6 +4229,15 @@ packages: - supports-color dev: false + /@supabase/realtime-js/2.6.0: + resolution: {integrity: sha512-tOVulMobhpxyDuu8VIImpL8FXmZOKsGNOSyS5ihJdj2xYmPPvYG+D2J51Ewfl+MFF65tweiB6p9N9bNIW1cDNA==} + dependencies: + '@types/phoenix': 1.5.4 + websocket: 1.0.34 + transitivePeerDependencies: + - supports-color + dev: false + /@supabase/storage-js/2.0.0: resolution: {integrity: sha512-7kXThdRt/xqnOOvZZxBqNkeX1CFNUWc0hYBJtNN/Uvt8ok9hD14foYmroWrHn046wEYFqUrB9U35JYsfTrvltA==} dependencies: @@ -4174,6 +4246,14 @@ packages: - encoding dev: false + /@supabase/storage-js/2.3.0: + resolution: {integrity: sha512-YGWVCEYYYF3+UiyL8O4xC78N9n9paLbT0hHl8dmYAtd3DqyWtu5Eph9JTu0PWm+/29Zhns5TbhUZW4xpWjJfPQ==} + dependencies: + cross-fetch: 3.1.5 + transitivePeerDependencies: + - encoding + dev: false + /@supabase/supabase-js/2.0.2: resolution: {integrity: sha512-ssRxPvkMcERx8bsrEaBjvRpNRz2V4ZjObPh5ky8CVxXH1vEWIb10Qq6WMG0LB32Z5lEWplEPy0zfy+LIetcNRQ==} dependencies: @@ -4188,6 +4268,20 @@ packages: - supports-color dev: false + /@supabase/supabase-js/2.8.0: + resolution: {integrity: sha512-uzf4J+qAKdUMhB2tnJl6BrQRUQBinwjJ2eWo2ZsDw9EUUP5JcHsxTamiq6p91DpqzmTIRg3xRAT+bItTzbfa0w==} + dependencies: + '@supabase/functions-js': 2.0.0 + '@supabase/gotrue-js': 2.12.1 + '@supabase/postgrest-js': 1.4.1 + '@supabase/realtime-js': 2.6.0 + '@supabase/storage-js': 2.3.0 + cross-fetch: 3.1.5 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + /@surma/rollup-plugin-off-main-thread/2.2.3: resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==} dependencies: @@ -4739,6 +4833,10 @@ packages: resolution: {integrity: sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==} dev: false + /@types/async-lock/1.4.0: + resolution: {integrity: sha512-2+rYSaWrpdbQG3SA0LmMT6YxWLrI81AqpMlSkw3QtFc2HGDufkweQSn30Eiev7x9LL0oyFrBqk1PXOnB9IEgKg==} + dev: false + /@types/babel__core/7.1.19: resolution: {integrity: sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==} dependencies: @@ -5145,6 +5243,10 @@ packages: resolution: {integrity: sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==} dev: false + /@types/uuid/9.0.1: + resolution: {integrity: sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA==} + dev: false + /@types/ws/8.5.3: resolution: {integrity: sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==} dependencies: @@ -5568,6 +5670,30 @@ packages: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} dev: true + /abstract-leveldown/6.2.3: + resolution: {integrity: sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ==} + engines: {node: '>=6'} + dependencies: + buffer: 5.7.1 + immediate: 3.3.0 + level-concat-iterator: 2.0.1 + level-supports: 1.0.1 + xtend: 4.0.2 + dev: false + optional: true + + /abstract-leveldown/6.3.0: + resolution: {integrity: sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ==} + engines: {node: '>=6'} + dependencies: + buffer: 5.7.1 + immediate: 3.3.0 + level-concat-iterator: 2.0.1 + level-supports: 1.0.1 + xtend: 4.0.2 + dev: false + optional: true + /accepts/1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -5889,6 +6015,15 @@ packages: engines: {node: '>=8'} dev: true + /async-limiter/1.0.1: + resolution: {integrity: sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==} + dev: false + optional: true + + /async-lock/1.4.0: + resolution: {integrity: sha512-coglx5yIWuetakm3/1dsX9hxCNox22h7+V80RQOu2XUUMidtArxKoZoOtHUPuR84SycKTXzgGzAUR5hJxujyJQ==} + dev: false + /async-sema/3.1.1: resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==} dev: true @@ -6334,7 +6469,6 @@ packages: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: true /buffer/6.0.3: resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} @@ -7230,6 +7364,18 @@ packages: dependencies: ms: 2.1.3 + /debug/3.2.7_supports-color@5.5.0: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 + supports-color: 5.5.0 + dev: true + /debug/4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -7283,6 +7429,15 @@ packages: resolution: {integrity: sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==} dev: true + /deferred-leveldown/5.3.0: + resolution: {integrity: sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw==} + engines: {node: '>=6'} + dependencies: + abstract-leveldown: 6.2.3 + inherits: 2.0.4 + dev: false + optional: true + /define-lazy-prop/2.0.0: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} @@ -7597,6 +7752,17 @@ packages: engines: {node: '>= 0.8'} dev: false + /encoding-down/6.3.0: + resolution: {integrity: sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw==} + engines: {node: '>=6'} + dependencies: + abstract-leveldown: 6.3.0 + inherits: 2.0.4 + level-codec: 9.0.2 + level-errors: 2.0.1 + dev: false + optional: true + /end-of-stream/1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} dependencies: @@ -7619,6 +7785,14 @@ packages: resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==} engines: {node: '>=0.12'} + /errno/0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + dependencies: + prr: 1.0.1 + dev: false + optional: true + /error-ex/1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} dependencies: @@ -9305,10 +9479,19 @@ packages: /ieee754/1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + /ignore-by-default/1.0.1: + resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} + dev: true + /ignore/5.2.0: resolution: {integrity: sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==} engines: {node: '>= 4'} + /immediate/3.3.0: + resolution: {integrity: sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==} + dev: false + optional: true + /immer/9.0.16: resolution: {integrity: sha512-qenGE7CstVm1NrHQbMh8YaSzTZTFNP3zPqr3YU0S0UY441j4bJTg4A2Hh5KAhwgaiU6ZZ1Ar6y/2f4TblnMReQ==} dev: false @@ -9680,6 +9863,10 @@ packages: - encoding dev: false + /isomorphic.js/0.2.5: + resolution: {integrity: sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==} + dev: false + /istanbul-lib-coverage/3.2.0: resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} engines: {node: '>=8'} @@ -10907,6 +11094,11 @@ packages: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} + /kleur/4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + dev: false + /klona/2.0.5: resolution: {integrity: sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==} engines: {node: '>= 8'} @@ -10931,6 +11123,98 @@ packages: resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} dev: false + /level-codec/9.0.2: + resolution: {integrity: sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ==} + engines: {node: '>=6'} + dependencies: + buffer: 5.7.1 + dev: false + optional: true + + /level-concat-iterator/2.0.1: + resolution: {integrity: sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw==} + engines: {node: '>=6'} + dev: false + optional: true + + /level-errors/2.0.1: + resolution: {integrity: sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw==} + engines: {node: '>=6'} + dependencies: + errno: 0.1.8 + dev: false + optional: true + + /level-iterator-stream/4.0.2: + resolution: {integrity: sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q==} + engines: {node: '>=6'} + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.0 + xtend: 4.0.2 + dev: false + optional: true + + /level-js/5.0.2: + resolution: {integrity: sha512-SnBIDo2pdO5VXh02ZmtAyPP6/+6YTJg2ibLtl9C34pWvmtMEmRTWpra+qO/hifkUtBTOtfx6S9vLDjBsBK4gRg==} + dependencies: + abstract-leveldown: 6.2.3 + buffer: 5.7.1 + inherits: 2.0.4 + ltgt: 2.2.1 + dev: false + optional: true + + /level-packager/5.1.1: + resolution: {integrity: sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ==} + engines: {node: '>=6'} + dependencies: + encoding-down: 6.3.0 + levelup: 4.4.0 + dev: false + optional: true + + /level-supports/1.0.1: + resolution: {integrity: sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg==} + engines: {node: '>=6'} + dependencies: + xtend: 4.0.2 + dev: false + optional: true + + /level/6.0.1: + resolution: {integrity: sha512-psRSqJZCsC/irNhfHzrVZbmPYXDcEYhA5TVNwr+V92jF44rbf86hqGp8fiT702FyiArScYIlPSBTDUASCVNSpw==} + engines: {node: '>=8.6.0'} + dependencies: + level-js: 5.0.2 + level-packager: 5.1.1 + leveldown: 5.6.0 + dev: false + optional: true + + /leveldown/5.6.0: + resolution: {integrity: sha512-iB8O/7Db9lPaITU1aA2txU/cBEXAt4vWwKQRrrWuS6XDgbP4QZGj9BL2aNbwb002atoQ/lIotJkfyzz+ygQnUQ==} + engines: {node: '>=8.6.0'} + requiresBuild: true + dependencies: + abstract-leveldown: 6.2.3 + napi-macros: 2.0.0 + node-gyp-build: 4.1.1 + dev: false + optional: true + + /levelup/4.4.0: + resolution: {integrity: sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ==} + engines: {node: '>=6'} + dependencies: + deferred-leveldown: 5.3.0 + level-errors: 2.0.1 + level-iterator-stream: 4.0.2 + level-supports: 1.0.1 + xtend: 4.0.2 + dev: false + optional: true + /leven/3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -10949,6 +11233,13 @@ packages: prelude-ls: 1.2.1 type-check: 0.4.0 + /lib0/0.2.63: + resolution: {integrity: sha512-JhUd/JXR4rnWsSP1zup904ACbVFcdRieWoIGwGAtHww4zms0gNyi8EM30Rfftnk+6A10qQMSufjLM0/ncq06xw==} + engines: {node: '>=14'} + dependencies: + isomorphic.js: 0.2.5 + dev: false + /lilconfig/2.0.5: resolution: {integrity: sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==} engines: {node: '>=10'} @@ -11125,6 +11416,11 @@ packages: dependencies: yallist: 4.0.0 + /ltgt/2.2.1: + resolution: {integrity: sha512-AI2r85+4MquTw9ZYqabu4nMwy9Oftlfa/e/52t9IjtfG+mGBbTNdAoZ3RQKLHR6r0wQnwZnPIEh/Ya6XTWAKNA==} + dev: false + optional: true + /lz-string/1.4.4: resolution: {integrity: sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==} hasBin: true @@ -11415,6 +11711,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + /napi-macros/2.0.0: + resolution: {integrity: sha512-A0xLykHtARfueITVDernsAWdtIMbOJgKgcluwENp3AlsKN/PloyO10HtmoqnFAQAcxPkgZN7wdfPfEd0zNGxbg==} + dev: false + optional: true + /natural-compare/1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -11460,6 +11761,12 @@ packages: lodash.get: 4.4.2 dev: true + /node-gyp-build/4.1.1: + resolution: {integrity: sha512-dSq1xmcPDKPZ2EED2S6zw/b9NKsqzXRE6dVr8TVQnI3FJOTteUMuqF3Qqs6LZg+mLGYJWqQzMbIjMtJqTv87nQ==} + hasBin: true + dev: false + optional: true + /node-gyp-build/4.5.0: resolution: {integrity: sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==} hasBin: true @@ -11477,6 +11784,30 @@ packages: /node-releases/2.0.6: resolution: {integrity: sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==} + /nodemon/2.0.20: + resolution: {integrity: sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==} + engines: {node: '>=8.10.0'} + hasBin: true + dependencies: + chokidar: 3.5.3 + debug: 3.2.7_supports-color@5.5.0 + ignore-by-default: 1.0.1 + minimatch: 3.1.2 + pstree.remy: 1.1.8 + semver: 5.7.1 + simple-update-notifier: 1.1.0 + supports-color: 5.5.0 + touch: 3.1.0 + undefsafe: 2.0.5 + dev: true + + /nopt/1.0.10: + resolution: {integrity: sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==} + hasBin: true + dependencies: + abbrev: 1.1.1 + dev: true + /nopt/5.0.0: resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} engines: {node: '>=6'} @@ -12916,6 +13247,11 @@ packages: ipaddr.js: 1.9.1 dev: false + /prr/1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + dev: false + optional: true + /pseudolocale/1.2.0: resolution: {integrity: sha512-k0OQFvIlvpRdzR0dPVrrbWX7eE9EaZ6gpZtTlFSDi1Gf9tMy9wiANCNu7JZ0drcKgUri/39a2mBbH0goiQmrmQ==} dependencies: @@ -12925,6 +13261,10 @@ packages: /psl/1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + /pstree.remy/1.1.8: + resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} + dev: true + /pump/3.0.0: resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} dependencies: @@ -13845,6 +14185,11 @@ packages: semver: 6.3.0 dev: true + /semver/5.7.1: + resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} + hasBin: true + dev: true + /semver/6.1.1: resolution: {integrity: sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==} hasBin: true @@ -13854,6 +14199,11 @@ packages: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} hasBin: true + /semver/7.0.0: + resolution: {integrity: sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==} + hasBin: true + dev: true + /semver/7.3.8: resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==} engines: {node: '>=10'} @@ -13961,6 +14311,13 @@ packages: /signal-exit/3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + /simple-update-notifier/1.1.0: + resolution: {integrity: sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==} + engines: {node: '>=8.10.0'} + dependencies: + semver: 7.0.0 + dev: true + /sisteransi/1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -14630,6 +14987,13 @@ packages: engines: {node: '>=0.6'} dev: false + /touch/3.1.0: + resolution: {integrity: sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==} + hasBin: true + dependencies: + nopt: 1.0.10 + dev: true + /tough-cookie/4.1.2: resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==} engines: {node: '>=6'} @@ -14882,6 +15246,10 @@ packages: has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 + /undefsafe/2.0.5: + resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + dev: true + /unfetch/4.2.0: resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} dev: false @@ -15091,6 +15459,11 @@ packages: hasBin: true dev: false + /uuid/9.0.0: + resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==} + hasBin: true + dev: false + /v8-compile-cache-lib/3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} @@ -15683,6 +16056,22 @@ packages: signal-exit: 3.0.7 dev: true + /ws/6.2.2: + resolution: {integrity: sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==} + requiresBuild: true + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dependencies: + async-limiter: 1.0.1 + dev: false + optional: true + /ws/7.5.9: resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} engines: {node: '>=8.3.0'} @@ -15729,6 +16118,53 @@ packages: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} + /y-leveldb/0.1.2_yjs@13.5.47: + resolution: {integrity: sha512-6ulEn5AXfXJYi89rXPEg2mMHAyyw8+ZfeMMdOtBbV8FJpQ1NOrcgi6DTAcXof0dap84NjHPT2+9d0rb6cFsjEg==} + requiresBuild: true + peerDependencies: + yjs: ^13.0.0 + dependencies: + level: 6.0.1 + lib0: 0.2.63 + yjs: 13.5.47 + dev: false + optional: true + + /y-monaco/0.1.4_xkgk6mmjz52pc4du3xhfls7v24: + resolution: {integrity: sha512-RzAvjKJ2yt3VjlKK55U8STJyd1gz5/Qm6pjt7SAVb+k+g+KaSeTuSBGt+lURN+dzzSNs73ZkIAerW3JM0mzBSw==} + peerDependencies: + monaco-editor: '>=0.20.0' + yjs: ^13.3.1 + dependencies: + lib0: 0.2.63 + monaco-editor: 0.34.0 + yjs: 13.5.47 + dev: false + + /y-protocols/1.0.5: + resolution: {integrity: sha512-Wil92b7cGk712lRHDqS4T90IczF6RkcvCwAD0A2OPg+adKmOe+nOiT/N2hvpQIWS3zfjmtL4CPaH5sIW1Hkm/A==} + dependencies: + lib0: 0.2.63 + dev: false + + /y-websocket/1.4.5_yjs@13.5.47: + resolution: {integrity: sha512-5d9LTSy0GQKqSd/FKRo5DMBlsiTlCipbKcIgPLlno+5xHtfT8bm3uQdcbY9JvLfckojilLZWauXJu0vzDZX05w==} + hasBin: true + peerDependencies: + yjs: ^13.5.6 + dependencies: + lib0: 0.2.63 + lodash.debounce: 4.0.8 + y-protocols: 1.0.5 + yjs: 13.5.47 + optionalDependencies: + ws: 6.2.2 + y-leveldb: 0.1.2_yjs@13.5.47 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + dev: false + /y18n/5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -15784,6 +16220,12 @@ packages: yargs-parser: 21.1.1 dev: true + /yjs/13.5.47: + resolution: {integrity: sha512-F7BZ+Bt36OAt+bdSQS7TN43KSxsHjfXWfcLC526tJ3mctO1FYHGEtOrUqpLC7pSp4jPn4rKSYHv1PVQcdb/vIQ==} + dependencies: + lib0: 0.2.63 + dev: false + /yn/3.1.1: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} From b4a340774264886df9f1ec9055a5a5a828545b3c Mon Sep 17 00:00:00 2001 From: Rob Gordon Date: Mon, 27 Feb 2023 12:18:21 -0500 Subject: [PATCH 02/19] feat(wss): basic websocket server working --- api/share.ts | 33 ++ app/src/components/CloneButton.tsx | 5 +- app/src/components/EditorError.tsx | 2 +- app/src/components/EditorOptions.tsx | 15 +- app/src/components/EditorWrapper.tsx | 4 +- app/src/components/Graph.tsx | 35 +- app/src/components/GraphContextMenu.tsx | 36 +- app/src/components/RenameButton.tsx | 12 +- app/src/components/ShareDialog.tsx | 22 +- app/src/components/Tabs/EditLayoutTab.tsx | 77 +-- app/src/components/Tabs/EditMetaTab.tsx | 6 +- app/src/components/Tabs/EditStyleTab.tsx | 41 +- app/src/components/TextEditor.tsx | 15 +- app/src/lib/docHelpers.ts | 76 +++ app/src/lib/docToString.ts | 14 + app/src/lib/getTheme.ts | 4 +- app/src/lib/graphThemes.tsx | 6 +- app/src/lib/parsers.ts | 4 +- app/src/lib/prepareChart/prepareChart.ts | 8 +- app/src/lib/realtime.ts | 87 ++++ app/src/lib/useDetails.ts | 13 + app/src/lib/useDoc.ts | 35 +- app/src/lib/useIsFrozen.ts | 18 +- app/src/lib/useParseError.ts | 8 + app/src/pages/Edit.tsx | 12 +- app/src/pages/EditHosted.tsx | 31 +- app/src/pages/ReadOnly.tsx | 4 +- pnpm-lock.yaml | 566 ++++++++++++++++++++++ pnpm-workspace.yaml | 1 + wss/jsconfig.json | 5 + wss/package.json | 26 + wss/src/index.mjs | 192 ++++++++ wss/src/supabase.mjs | 8 + 33 files changed, 1182 insertions(+), 239 deletions(-) create mode 100644 api/share.ts create mode 100644 app/src/lib/docHelpers.ts create mode 100644 app/src/lib/docToString.ts create mode 100644 app/src/lib/realtime.ts create mode 100644 app/src/lib/useDetails.ts create mode 100644 app/src/lib/useParseError.ts create mode 100644 wss/jsconfig.json create mode 100644 wss/package.json create mode 100644 wss/src/index.mjs create mode 100644 wss/src/supabase.mjs diff --git a/api/share.ts b/api/share.ts new file mode 100644 index 00000000..936144ed --- /dev/null +++ b/api/share.ts @@ -0,0 +1,33 @@ +import { VercelRequest, VercelResponse } from "@vercel/node"; +import { confirmActiveSubscriptionFromToken } from "./_lib/_helpers"; +import { supabase } from "./_lib/_supabase"; + +export default async function handler(req: VercelRequest, res: VercelResponse) { + // make sure user is logged in + const token = req.headers.authorization; + if (!token) { + return res.status(401).send("Unauthorized"); + } + if (!confirmActiveSubscriptionFromToken(token)) { + return res.status(402).send("Unauthorized"); + } + + // get chartId and userEmail from request body + const { chartId, userEmail } = req.body; + if (!chartId || !userEmail) { + return res.status(400).send("Missing chartId or userEmail"); + } + + const result = await supabase.from("shared_charts").insert([ + { + flowchart_id: chartId, + user_email: userEmail, + }, + ]); + + if (result.error) { + return res.status(400).send(result.error.message); + } + + return res.status(200).send("Chart shared successfully"); +} diff --git a/app/src/components/CloneButton.tsx b/app/src/components/CloneButton.tsx index 497ac8de..3b0ca84e 100644 --- a/app/src/components/CloneButton.tsx +++ b/app/src/components/CloneButton.tsx @@ -2,14 +2,15 @@ import { Trans } from "@lingui/macro"; import { FaCopy } from "react-icons/fa"; import { useHistory } from "react-router-dom"; +import { getDoc } from "../lib/docHelpers"; +import { docToString } from "../lib/docToString"; import { randomChartName, titleToLocalStorageKey } from "../lib/helpers"; -import { docToString, useDoc } from "../lib/useDoc"; import { Type } from "../slang"; import styles from "./EditorWrapper.module.css"; export function CloneButton() { const { push } = useHistory(); - const fullText = useDoc((s) => docToString(s)); + const fullText = docToString(getDoc()); return (