+68
-37
src/routes/post.lazy.tsx
+68
-37
src/routes/post.lazy.tsx
···
3
3
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
4
4
import { Input } from "@/components/ui/input";
5
5
import { Switch } from "@/components/ui/switch";
6
+
import { Textarea } from "@/components/ui/textarea";
6
7
import { QtContext } from "@/providers/qtprovider";
7
8
import { AppBskyFeedPost } from "@atcute/client/lexicons";
8
9
import { createLazyFileRoute, useNavigate } from "@tanstack/react-router";
···
25
26
const ctx = canvas.getContext("2d");
26
27
27
28
img.onload = () => {
28
-
let { width, height } = img;
29
-
let quality = 0.9;
30
-
let blob: Blob | null = null;
29
+
// Calculate aspect ratio
30
+
const aspectRatio = img.width / img.height;
31
31
32
-
// First pass: try with original dimensions
33
-
canvas.width = width;
34
-
canvas.height = height;
35
-
ctx?.drawImage(img, 0, 0, width, height);
32
+
// Start with a reasonable size that's likely to be under 1MB
33
+
let targetWidth = 2000; // common width for social media
34
+
let targetHeight = Math.round(targetWidth / aspectRatio);
36
35
37
-
// Try to get under maxSize with quality reduction first
38
-
while (quality > 0.1) {
39
-
blob = canvas.toBlob(
40
-
(b) => {
41
-
if (b && b.size <= maxSize) {
42
-
resolve(b);
43
-
} else if (quality > 0.1) {
44
-
quality -= 0.1;
45
-
canvas.toBlob((b) => (blob = b), "image/jpeg", quality);
46
-
} else {
47
-
// If we get here, we need to reduce dimensions
48
-
width *= 0.9;
49
-
height *= 0.9;
50
-
canvas.width = width;
51
-
canvas.height = height;
52
-
ctx?.drawImage(img, 0, 0, width, height);
53
-
quality = 0.9;
54
-
canvas.toBlob((b) => (blob = b), "image/jpeg", quality);
55
-
}
56
-
},
57
-
"image/jpeg",
58
-
quality,
59
-
);
36
+
if (targetHeight > targetWidth) {
37
+
// swap width and height
38
+
const temp = targetHeight;
39
+
targetHeight = targetWidth;
40
+
targetWidth = temp;
60
41
}
61
42
62
-
// If we still haven't resolved, use the last blob
63
-
if (blob) {
64
-
resolve(blob);
65
-
} else {
66
-
reject(new Error("Could not downscale image sufficiently"));
43
+
// If image is smaller than target size, use original dimensions
44
+
if (img.width < targetWidth) {
45
+
targetWidth = img.width;
46
+
targetHeight = img.height;
67
47
}
48
+
49
+
// Set canvas size to target dimensions
50
+
canvas.width = targetWidth;
51
+
canvas.height = targetHeight;
52
+
53
+
// Draw image at new size
54
+
ctx?.drawImage(img, 0, 0, targetWidth, targetHeight);
55
+
56
+
// Convert to blob with reasonable quality
57
+
canvas.toBlob(
58
+
(blob) => {
59
+
if (!blob) {
60
+
reject(new Error("Failed to create blob"));
61
+
return;
62
+
}
63
+
// If still too large, reduce quality
64
+
if (blob.size > maxSize) {
65
+
canvas.toBlob(
66
+
(finalBlob) => {
67
+
if (!finalBlob) {
68
+
reject(new Error("Failed to create blob"));
69
+
return;
70
+
}
71
+
resolve(finalBlob);
72
+
},
73
+
"image/jpeg",
74
+
0.7, // reduced quality
75
+
);
76
+
} else {
77
+
resolve(blob);
78
+
}
79
+
},
80
+
"image/jpeg",
81
+
0.9, // initial quality
82
+
);
68
83
};
69
84
70
85
img.onerror = () => reject(new Error("Failed to load image"));
···
82
97
const [isUploading, setIsUploading] = useState(false);
83
98
const [uploadError, setUploadError] = useState<Error | null>(null);
84
99
const [postToBluesky, setPostToBluesky] = useState(false);
100
+
const [textToPost, setTextToPost] = useState("");
85
101
const fileInputRef = useRef<HTMLInputElement | null>(null);
86
102
87
103
const navigate = useNavigate();
···
158
174
}));
159
175
160
176
let dummyRecord: AppBskyFeedPost.Record = {
161
-
text: "",
177
+
text: textToPost,
162
178
createdAt: new Date().toISOString(),
163
179
embed: {
164
180
$type: "app.bsky.embed.images",
···
190
206
}));
191
207
192
208
let bskyRecord: AppBskyFeedPost.Record = {
193
-
text: "",
209
+
text: textToPost,
194
210
createdAt: new Date().toISOString(),
195
211
embed: {
196
212
$type: "app.bsky.embed.images",
···
301
317
Select Images (up to 4)
302
318
</Button>
303
319
</div>
320
+
321
+
<Textarea
322
+
value={textToPost}
323
+
onChange={(e) => setTextToPost(e.currentTarget.value)}
324
+
placeholder="Blaze your glory! (leave empty if you don't want to include text with your post)"
325
+
/>
326
+
327
+
{textToPost.length > 300 - 16 ? (
328
+
<p>
329
+
Your post is too long! Remove{" "}
330
+
{Math.abs(textToPost.length - 300 - 16)} chars!
331
+
</p>
332
+
) : (
333
+
<p>{textToPost.length}</p>
334
+
)}
304
335
305
336
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
306
337
{images.map((imageData, index) => (