3504ec97cc
Move skipper and crew master data to the user profile pool, replace the logbook crew tab with selection from that pool, inherit crew on new travel days, and sync via new PersonPayload and LogbookCrewSelection models. Includes migration from legacy crew records, tour/demo updates, and i18n. Co-authored-by: Cursor <cursoragent@cursor.com>
40 lines
1.3 KiB
TypeScript
40 lines
1.3 KiB
TypeScript
/** Resize and compress an image file to a JPEG data URL (max 800×600). */
|
||
export function resizeImageFile(file: File): Promise<string> {
|
||
return new Promise((resolve, reject) => {
|
||
const reader = new FileReader()
|
||
reader.onload = (event) => {
|
||
const img = new Image()
|
||
img.onload = () => {
|
||
try {
|
||
const canvas = document.createElement('canvas')
|
||
const ctx = canvas.getContext('2d')
|
||
if (!ctx) throw new Error('Could not get canvas context')
|
||
|
||
let width = img.width
|
||
let height = img.height
|
||
const MAX_WIDTH = 800
|
||
const MAX_HEIGHT = 600
|
||
|
||
if (width > MAX_WIDTH || height > MAX_HEIGHT) {
|
||
const ratio = Math.min(MAX_WIDTH / width, MAX_HEIGHT / height)
|
||
width = Math.round(width * ratio)
|
||
height = Math.round(height * ratio)
|
||
}
|
||
|
||
canvas.width = width
|
||
canvas.height = height
|
||
ctx.drawImage(img, 0, 0, width, height)
|
||
|
||
resolve(canvas.toDataURL('image/jpeg', 0.7))
|
||
} catch (err) {
|
||
reject(err)
|
||
}
|
||
}
|
||
img.onerror = () => reject(new Error('Invalid image file'))
|
||
img.src = event.target?.result as string
|
||
}
|
||
reader.onerror = () => reject(new Error('Failed to read file'))
|
||
reader.readAsDataURL(file)
|
||
})
|
||
}
|