WIP PWA for Grain
next.grain.social
1export function readFileAsDataURL(file) {
2 return new Promise((resolve, reject) => {
3 const reader = new FileReader();
4 reader.onload = () => resolve(reader.result);
5 reader.onerror = reject;
6 reader.readAsDataURL(file);
7 });
8}
9
10function getBase64Size(base64) {
11 const str = base64.split(',')[1] || base64;
12 return Math.ceil((str.length * 3) / 4);
13}
14
15function createResizedImage(dataUrl, options) {
16 return new Promise((resolve, reject) => {
17 const img = new Image();
18 img.onload = () => {
19 const scale = Math.min(
20 options.width / img.width,
21 options.height / img.height,
22 1
23 );
24 const w = Math.round(img.width * scale);
25 const h = Math.round(img.height * scale);
26
27 const canvas = document.createElement('canvas');
28 canvas.width = w;
29 canvas.height = h;
30 const ctx = canvas.getContext('2d');
31
32 ctx.fillStyle = '#fff';
33 ctx.fillRect(0, 0, w, h);
34 ctx.imageSmoothingEnabled = true;
35 ctx.imageSmoothingQuality = 'high';
36 ctx.drawImage(img, 0, 0, w, h);
37
38 resolve({
39 dataUrl: canvas.toDataURL('image/jpeg', options.quality),
40 width: w,
41 height: h
42 });
43 };
44 img.onerror = reject;
45 img.src = dataUrl;
46 });
47}
48
49export async function resizeImage(dataUrl, opts) {
50 let bestResult = null;
51 let minQuality = 0;
52 let maxQuality = 100;
53
54 while (maxQuality - minQuality > 1) {
55 const quality = Math.round((minQuality + maxQuality) / 2);
56 const result = await createResizedImage(dataUrl, {
57 width: opts.width,
58 height: opts.height,
59 quality: quality / 100
60 });
61
62 const size = getBase64Size(result.dataUrl);
63 if (size <= opts.maxSize) {
64 bestResult = result;
65 minQuality = quality;
66 } else {
67 maxQuality = quality;
68 }
69 }
70
71 if (!bestResult) {
72 throw new Error('Failed to compress image within size limit');
73 }
74
75 return bestResult;
76}
77
78export async function processPhotos(files) {
79 const processed = [];
80 for (const file of files) {
81 const dataUrl = await readFileAsDataURL(file);
82 const resized = await resizeImage(dataUrl, {
83 width: 2000,
84 height: 2000,
85 maxSize: 900000
86 });
87 processed.push({
88 dataUrl: resized.dataUrl,
89 width: resized.width,
90 height: resized.height
91 });
92 }
93 return processed;
94}