From 190dfcdb34bd569a1f40e7d4c53296a3eed8887d Mon Sep 17 00:00:00 2001 From: Dave Augustus Date: Fri, 23 Jan 2026 15:00:00 +0530 Subject: [PATCH 1/9] feat: native video tool, secure pdf reader, and storage v2 ui integration --- apps/the_monkeys/next.config.mjs | 5 + apps/the_monkeys/package.json | 3 +- .../src/app/edit/[blogId]/page.tsx | 94 +++++++------ apps/the_monkeys/src/app/read/pdf/page.tsx | 81 +++++++++++ .../src/components/blog/getBlogContent.tsx | 58 ++++++-- .../editor/customBlocks/PdfBlock/index.ts | 131 ++++++++++++++++++ .../editor/customBlocks/VideoBlock/index.ts | 128 +++++++++++++++++ .../layout/navbar/profileDropdown.tsx | 10 +- .../src/components/profileImage.tsx | 42 ++++-- .../user/dialogs/updateProfileDialog.tsx | 10 +- .../src/components/user/userInfo.tsx | 10 +- .../src/config/editor/editorjs.config.ts | 125 +++++++++++++++-- .../config/editor/editorjs_readonly.config.ts | 37 +++++ .../src/hooks/profile/useProfileImage.ts | 30 ++-- .../src/services/storage/storageV2.ts | 108 +++++++++++++++ pnpm-lock.yaml | 25 +++- 16 files changed, 792 insertions(+), 105 deletions(-) create mode 100644 apps/the_monkeys/src/app/read/pdf/page.tsx create mode 100644 apps/the_monkeys/src/components/editor/customBlocks/PdfBlock/index.ts create mode 100644 apps/the_monkeys/src/components/editor/customBlocks/VideoBlock/index.ts create mode 100644 apps/the_monkeys/src/services/storage/storageV2.ts diff --git a/apps/the_monkeys/next.config.mjs b/apps/the_monkeys/next.config.mjs index a128a23a..b1a8e356 100644 --- a/apps/the_monkeys/next.config.mjs +++ b/apps/the_monkeys/next.config.mjs @@ -24,6 +24,11 @@ const nextConfig = { hostname: '127.0.0.1', port: '8081', }, + { + protocol: 'http', + hostname: 'localhost', + port: '8081', + }, ], }, experimental: { diff --git a/apps/the_monkeys/package.json b/apps/the_monkeys/package.json index f9d4ea38..be1dc69d 100644 --- a/apps/the_monkeys/package.json +++ b/apps/the_monkeys/package.json @@ -12,6 +12,7 @@ "format": "prettier --write ." }, "dependencies": { + "@editorjs/attaches": "^1.3.1", "@editorjs/delimiter": "1.4.2", "@editorjs/editorjs": "2.29.1", "@editorjs/header": "2.8.1", @@ -88,4 +89,4 @@ "vite-tsconfig-paths": "^5.1.4", "vitest": "^2.1.8" } -} +} \ No newline at end of file diff --git a/apps/the_monkeys/src/app/edit/[blogId]/page.tsx b/apps/the_monkeys/src/app/edit/[blogId]/page.tsx index d03d7d44..9d50dc07 100644 --- a/apps/the_monkeys/src/app/edit/[blogId]/page.tsx +++ b/apps/the_monkeys/src/app/edit/[blogId]/page.tsx @@ -55,7 +55,6 @@ const EditPage = ({ params }: { params: { blogId: string } }) => { const [isSaving, setIsSaving] = useState(false); const [blogPublishLoading, setBlogPublishLoading] = useState(false); const [blogTopics, setBlogTopics] = useState([]); - const [token, setToken] = useState(null); const [isConnected, setIsConnected] = useState(false); const [connectionStatus, setConnectionStatus] = useState('Connecting...'); @@ -69,34 +68,6 @@ const EditPage = ({ params }: { params: { blogId: string } }) => { blogTopicsRef.current = blogTopics; }, [data, accountId, blogTopics]); - // Get WebSocket token - useEffect(() => { - if (!session) return; - - const fetchToken = async () => { - try { - const response = await axiosInstance.get('/auth/ws-token'); - setToken(response.data.token); - } catch (error) { - console.error('Failed to get WebSocket token:', error); - toast({ - variant: 'destructive', - title: 'Connection Error', - description: - 'Failed to establish connection. Please refresh the page.', - }); - } - }; - - fetchToken(); - - // Refresh token every 5 minutes - const tokenRefreshInterval = setInterval(fetchToken, 5 * 60 * 1000); - - return () => { - clearInterval(tokenRefreshInterval); - }; - }, [session]); // Format data const formatData = useCallback( @@ -127,9 +98,8 @@ const EditPage = ({ params }: { params: { blogId: string } }) => { const [editorConfig, setEditorConfig] = useState(null); - // WebSocket management useEffect(() => { - if (!token || !blogId) return; + if (!blogId) return; let isMounted = true; const MAX_RETRIES = 5; @@ -144,7 +114,7 @@ const EditPage = ({ params }: { params: { blogId: string } }) => { setConnectionStatus('Connecting...'); const ws = new WebSocket( - `${WSS_URL_V2}/blog/draft/${blogId}?token=${token}` + `${WSS_URL_V2}/blog/draft/${blogId}` ); ws.onopen = () => { @@ -204,7 +174,7 @@ const EditPage = ({ params }: { params: { blogId: string } }) => { // Handle tab visibility changes const handleVisibilityChange = () => { - if (document.visibilityState === 'visible' && !isConnected && token) { + if (document.visibilityState === 'visible' && !isConnected) { // Reconnect immediately when tab becomes visible if (reconnectTimeoutRef.current) { clearTimeout(reconnectTimeoutRef.current); @@ -225,7 +195,7 @@ const EditPage = ({ params }: { params: { blogId: string } }) => { } document.removeEventListener('visibilitychange', handleVisibilityChange); }; - }, [token, blogId, formatData]); + }, [blogId, formatData]); // Auto-save when data changes useEffect(() => { @@ -333,10 +303,53 @@ const EditPage = ({ params }: { params: { blogId: string } }) => { // Initialize editor data useEffect(() => { - if (blog && !data) { - setData(blog.blog || { time: Date.now(), blocks: [], version: '' }); - setBlogTopics(blog.tags || []); - } + const initializeData = async () => { + if (blog && !data) { + const blogData = blog.blog || { + time: Date.now(), + blocks: [], + version: '', + }; + + // Pre-resolve V2 image URLs for EditorJS + if (blogData.blocks && blogData.blocks.length > 0) { + const resolvedBlocks = await Promise.all( + blogData.blocks.map(async (block) => { + if ( + block.type === 'image' && + block.data?.file?.url?.endsWith('/url') + ) { + try { + const res = await fetch(block.data.file.url); + const urlData = await res.json(); + if (urlData && urlData.url) { + return { + ...block, + data: { + ...block.data, + file: { + ...block.data.file, + url: urlData.url, + }, + }, + }; + } + } catch (err) { + console.error('Failed to resolve image URL in editor:', err); + } + } + return block; + }) + ); + blogData.blocks = resolvedBlocks; + } + + setData(blogData); + setBlogTopics(blog.tags || []); + } + }; + + initializeData(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [blog]); @@ -365,9 +378,8 @@ const EditPage = ({ params }: { params: { blogId: string } }) => { )} >

{isConnected ? 'Online' : 'Offline'}

diff --git a/apps/the_monkeys/src/app/read/pdf/page.tsx b/apps/the_monkeys/src/app/read/pdf/page.tsx new file mode 100644 index 00000000..a4162248 --- /dev/null +++ b/apps/the_monkeys/src/app/read/pdf/page.tsx @@ -0,0 +1,81 @@ +'use client'; + +import { Suspense } from 'react'; +import { useSearchParams, useRouter } from 'next/navigation'; +import Container from '@/components/layout/Container'; +import Icon from '@/components/icon'; + +const PDFReaderContent = () => { + const searchParams = useSearchParams(); + const router = useRouter(); + const url = searchParams.get('url'); + const title = searchParams.get('title') || 'PDF Viewer'; + + if (!url) { + return ( +
+ +

No PDF URL provided

+ +
+ ); + } + + // Append toolbar=0 to hide browser download/print buttons + const viewerUrl = url.includes('#') ? url : `${url}#toolbar=0`; + + return ( +
+
+
+ +
+

{title}

+

Monkeys Secure PDF Reader

+
+
+
+
+ + Read Only Mode +
+
+
+ +
+