Spaces:
Sleeping
Sleeping
File size: 4,997 Bytes
e1753d8 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | import { Share } from '@capacitor/share'
import { Filesystem, Directory } from '@capacitor/filesystem'
import { SERVER_URL } from '../config'
import { Editor } from 'tldraw'
import { customAlert, customPrompt } from './uiUtils'
// Helper: Convert Blob to Base64
const blobToBase64 = (blob: Blob): Promise<string> => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onerror = reject
reader.onload = () => resolve(reader.result as string)
reader.readAsDataURL(blob)
})
}
export const shareLink = async (roomId: string) => {
const url = `${SERVER_URL}/#/${roomId}`
try {
// Try native share or Web Share API first
await Share.share({
title: 'Join my Whiteboard',
text: 'Collaborate with me on this board:',
url: url,
dialogTitle: 'Share Board Link',
})
} catch (error) {
// Fallback to clipboard if Share API fails (common on desktop)
console.log('Share API not available, falling back to clipboard', error)
try {
await navigator.clipboard.writeText(url)
await customAlert('Link copied to clipboard!')
} catch (clipboardError) {
console.error('Failed to copy to clipboard:', clipboardError)
await customPrompt('Copy this link:', url)
}
}
}
export const exportToImage = async (editor: Editor, roomId: string) => {
try {
// 1. Get all shape IDs from the current page
const shapeIds = Array.from(editor.getCurrentPageShapeIds())
if (shapeIds.length === 0) {
await customAlert('Board is empty')
return
}
// 2. Calculate Bounds to prevent OOM
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity
shapeIds.forEach(id => {
const bounds = editor.getShapePageBounds(id)
if (bounds) {
minX = Math.min(minX, bounds.x)
minY = Math.min(minY, bounds.y)
maxX = Math.max(maxX, bounds.x + bounds.w)
maxY = Math.max(maxY, bounds.y + bounds.h)
}
})
const width = maxX - minX
const height = maxY - minY
// Limit max dimension to ~3000px to avoid Android texture/memory limits
const MAX_DIMENSION = 3000
let scale = 2
if (width * scale > MAX_DIMENSION || height * scale > MAX_DIMENSION) {
scale = Math.min(MAX_DIMENSION / width, MAX_DIMENSION / height)
console.log(`Large board detected. Reducing scale to ${scale.toFixed(2)} to prevent crash.`)
}
// 3. Generate Image with safe scale
let result
try {
result = await editor.toImage(shapeIds, {
format: 'png',
background: true,
scale: scale,
padding: 32,
})
} catch (e) {
throw new Error('Image generation failed: ' + (e as any).message)
}
if (!result || !result.blob) {
throw new Error('Failed to generate image blob (empty result)')
}
// 4. Check file size. If huge (>2MB), convert to JPEG to ensure shareability
if (result.blob.size > 2 * 1024 * 1024) {
console.log('Image too large (>2MB), switching to JPEG for compression')
try {
result = await editor.toImage(shapeIds, {
format: 'jpeg',
quality: 0.8,
background: true,
scale: scale,
padding: 32,
})
} catch (e) {
console.warn('JPEG fallback failed, using original PNG')
}
}
// 5. Convert Blob to Base64 for Capacitor
let pureBase64
try {
const base64Data = await blobToBase64(result.blob!)
pureBase64 = base64Data.split(',')[1]
} catch (e) {
throw new Error('Base64 conversion failed')
}
const ext = result.blob!.type === 'image/jpeg' ? 'jpg' : 'png'
const fileName = `board-${roomId}-${Date.now()}.${ext}`
// 6. Save to Capacitor Filesystem
let savedFile
try {
savedFile = await Filesystem.writeFile({
path: fileName,
data: pureBase64,
directory: Directory.Cache
})
} catch (e) {
throw new Error('File save failed: ' + (e as any).message)
}
// 7. Share
try {
await Share.share({
title: 'Export Board',
files: [savedFile.uri],
})
} catch (e) {
throw new Error('Share API failed: ' + (e as any).message)
}
} catch (error) {
console.error('Export failed:', error)
await customAlert((error as any).message)
}
} |