Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ const RetroColumn = ({
<input
type="file"
accept="image/*"
multiple={false}
onChange={(e) => onColumnHeaderImageUpload(columnKey, e)}
style={{ display: "none" }}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ const RetroErrorPopup = ({ title, retroId }: IProps) => {
const lobby = getCurrentRetroLobby(retroId);

if (retroNickname && lobby) {
if (!socket.connected) {
socket.connect();
}
socket.emit("joinRetro", {
nickname: retroNickname,
retroId: retroId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
"use client";

import { useState, useEffect } from "react";
import toast from "react-hot-toast";

const MAX_HEADER_IMAGE_SIZE_BYTES = 1024 * 1024; // 1MB
const MAX_HEADER_IMAGE_DIMENSION = 1200;

interface UseBoardImagesParams {
socket: any;
Expand Down Expand Up @@ -152,36 +156,54 @@ export const useBoardImages = ({
const file = e.target.files?.[0];
if (!file) return;

const reader = new FileReader();
reader.onload = (event) => {
const result = event.target?.result as string;
const img = new Image();
img.onload = () => {
const maxSize = 300;
let width = img.naturalWidth;
let height = img.naturalHeight;
if (width > maxSize || height > maxSize) {
const ratio = Math.min(maxSize / width, maxSize / height);
width = width * ratio;
height = height * ratio;
}
const newImage = {
id: Date.now().toString(),
src: result,
x: -99999,
y: -99999,
width,
height
};
const updatedImages = [...boardImages, newImage];
setBoardImages(updatedImages);
if (userInfo.lobby) {
socket.emit("updateBoardImages", roomId, updatedImages, userInfo.lobby.credentials);
try {
const reader = new FileReader();
reader.onload = (event) => {
try {
const result = event.target?.result as string;
const img = new Image();
img.onload = () => {
try {
const maxSize = 300;
let width = img.naturalWidth;
let height = img.naturalHeight;
if (width > maxSize || height > maxSize) {
const ratio = Math.min(maxSize / width, maxSize / height);
width = width * ratio;
height = height * ratio;
}
const newImage = {
id: Date.now().toString(),
src: result,
x: -99999,
y: -99999,
width,
height
};
const updatedImages = [...boardImages, newImage];
setBoardImages(updatedImages);
if (userInfo.lobby) {
socket.emit("updateBoardImages", roomId, updatedImages, userInfo.lobby.credentials);
}
} catch (err) {
console.error("Board image process error:", err);
toast.error("Failed to process image");
}
};
img.onerror = () => toast.error("Invalid image file");
img.src = result;
} catch (err) {
console.error("Board image load error:", err);
toast.error("Failed to load image");
}
};
img.src = result;
};
reader.readAsDataURL(file);
reader.onerror = () => toast.error("Failed to read file");
reader.readAsDataURL(file);
} catch (err) {
console.error("Board image upload error:", err);
toast.error("Failed to upload image");
}
e.target.value = "";
};

const handleImageDragStart = (image: any, e: React.DragEvent) => {
Expand Down Expand Up @@ -213,16 +235,77 @@ export const useBoardImages = ({
const handleColumnHeaderImageUpload = (columnKey: string, e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
const result = event.target?.result as string;
const updatedImages = { ...columnHeaderImages, [columnKey]: result };
setColumnHeaderImages(updatedImages);
if (userInfo.lobby) {
socket.emit("updateColumnHeaderImages", roomId, updatedImages, userInfo.lobby.credentials);
}
};
reader.readAsDataURL(file);

// Tek image: zaten varsa yükleme (UI'da gizli ama yine de guard)
if (columnHeaderImages[columnKey]) {
toast.error("Only one image per column. Remove the current image first.");
return;
}

if (file.size > MAX_HEADER_IMAGE_SIZE_BYTES) {
toast.error("Image must be under 1MB");
return;
}

try {
const reader = new FileReader();
reader.onload = (event) => {
try {
const result = event.target?.result as string;
const img = new Image();
img.onload = () => {
try {
let width = img.naturalWidth;
let height = img.naturalHeight;
if (width > MAX_HEADER_IMAGE_DIMENSION || height > MAX_HEADER_IMAGE_DIMENSION) {
const ratio = Math.min(MAX_HEADER_IMAGE_DIMENSION / width, MAX_HEADER_IMAGE_DIMENSION / height);
width = Math.round(width * ratio);
height = Math.round(height * ratio);
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.drawImage(img, 0, 0, width, height);
const resizedBase64 = canvas.toDataURL("image/jpeg", 0.85);
const updatedImages = { ...columnHeaderImages, [columnKey]: resizedBase64 };
setColumnHeaderImages(updatedImages);
if (userInfo.lobby) {
socket.emit("updateColumnHeaderImages", roomId, updatedImages, userInfo.lobby.credentials);
}
} else {
const updatedImages = { ...columnHeaderImages, [columnKey]: result };
setColumnHeaderImages(updatedImages);
if (userInfo.lobby) {
socket.emit("updateColumnHeaderImages", roomId, updatedImages, userInfo.lobby.credentials);
}
}
} else {
const updatedImages = { ...columnHeaderImages, [columnKey]: result };
setColumnHeaderImages(updatedImages);
if (userInfo.lobby) {
socket.emit("updateColumnHeaderImages", roomId, updatedImages, userInfo.lobby.credentials);
}
}
} catch (err) {
console.error("Header image process error:", err);
toast.error("Failed to process image");
}
};
img.onerror = () => toast.error("Invalid image file");
img.src = result;
} catch (err) {
console.error("Header image load error:", err);
toast.error("Failed to load image");
}
};
reader.onerror = () => toast.error("Failed to read file");
reader.readAsDataURL(file);
} catch (err) {
console.error("Header image upload error:", err);
toast.error("Failed to upload image");
}
e.target.value = "";
};

const handleRemoveColumnHeaderImage = (columnKey: string) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ export const useRetroSocketEvents = ({
socket.on("emoteReaction", handleEmoteReaction);
socket.on("retroEnded", handleRetroEnded);

const handleDisconnect = () => {
setShowErrorPopup(true);
};
socket.on("disconnect", handleDisconnect);

return () => {
clearInterval(heartbeatInterval);
document.removeEventListener('visibilitychange', handleVisibilityChange);
Expand Down Expand Up @@ -235,6 +240,7 @@ export const useRetroSocketEvents = ({
socket.off("cardsImported", handleCardsImported);
socket.off("emoteReaction", handleEmoteReaction);
socket.off("retroEnded", handleRetroEnded);
socket.off("disconnect", handleDisconnect);
};
}, [socket, roomId, avatarSeed]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -2149,7 +2149,7 @@
&__header-image {
position: relative;
width: 100%;
height: 120px;
height: 200px;
margin-bottom: 1rem;
overflow: hidden;
border-radius: 8px;
Expand Down
Loading