[251019] fix: PlayerPanel Optimization-2

🕐 커밋 시간: 2025. 10. 19. 22:13:44

📊 변경 통계:
  • 총 파일: 8개
  • 추가: +264줄
  • 삭제: -100줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/actions/convertActions.js
  ~ com.twin.app.shoptime/src/components/TItemCard/TItemCard.new.jsx
  ~ com.twin.app.shoptime/src/components/TPopUp/TPopUp.module.less
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayChat.jsx
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayQRCode.jsx
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.jsx
  ~ com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.new.jsx

🔧 함수 변경 내용:
  📄 com.twin.app.shoptime/src/actions/convertActions.js (javascript):
     Added: attemptConversion(), onSuccess(), onFail()
     Deleted: onSuccess(), onFail()
  📄 com.twin.app.shoptime/src/components/TItemCard/TItemCard.new.jsx (javascript):
    🔄 Modified: generateMockEnergyLabels()
  📄 com.twin.app.shoptime/src/components/TPopUp/TPopUp.module.less (unknown):
     Added: style()
     Deleted: style()
  📄 com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayChat.jsx (javascript):
     Added: PlayerOverlayChat(), propsAreEqual()
  📄 com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayContents.jsx (javascript):
     Added: PlayerOverlayContents(), propsAreEqual()
  📄 com.twin.app.shoptime/src/views/PlayerPanel/PlayerOverlay/PlayerOverlayQRCode.jsx (javascript):
     Added: PlayerOverlayQRCode(), propsAreEqual()
  📄 com.twin.app.shoptime/src/views/PlayerPanel/PlayerPanel.new.jsx (javascript):
     Added: PlayerPanelNew()
    🔄 Modified: getLogTpNo()

🔧 주요 변경 내용:
  • 핵심 비즈니스 로직 개선
  • UI 컴포넌트 아키텍처 개선
This commit is contained in:
2025-10-19 22:13:48 +09:00
parent 95bb25a135
commit 5c70f1fa78
8 changed files with 769 additions and 800 deletions

View File

@@ -3,85 +3,178 @@ import { TAxios } from '../api/TAxios';
import { types } from './actionTypes';
/**
* PDF를 이미지로 변환
* PDF를 이미지로 변환 (재시도 로직 포함)
* @param {string} pdfUrl - 변환할 PDF URL
* @param {function} callback - 성공/실패 후 실행할 콜백 (error, imageUrl)
* @param {number} maxRetries - 최대 재시도 횟수 (기본값: 5)
* @param {number} timeout - 타임아웃 (기본값: 60000ms = 60초)
*/
export const convertPdfToImage = (pdfUrl, callback) => (dispatch, getState) => {
dispatch({
type: types.CONVERT_PDF_TO_IMAGE,
payload: pdfUrl,
});
export const convertPdfToImage =
(pdfUrl, callback, maxRetries = 5, timeout = 60000) =>
(dispatch, getState) => {
let attempts = 0;
let timeoutId = null;
const onSuccess = (response) => {
// retCode 체크 (프로젝트 API 규약: 200이어도 retCode로 성공/실패 구분)
const retCode = response.headers?.retcode || response.headers?.retCode;
const attemptConversion = () => {
attempts++;
// console.log(`🔄 [EnergyLabel] Converting PDF attempt ${attempts}/${maxRetries + 1}:`, pdfUrl);
if (retCode !== undefined && retCode !== 0 && retCode !== '0') {
const error = new Error(`API Error: retCode=${retCode}`);
dispatch({
type: types.CONVERT_PDF_TO_IMAGE_FAILURE,
payload: { pdfUrl, error },
});
callback && callback(error, null);
return;
}
// 타임아웃 설정
timeoutId = setTimeout(() => {
clearTimeout(timeoutId);
const timeoutError = new Error(
`Conversion timeout after ${timeout}ms (attempt ${attempts})`
);
console.warn(`⏱️ [EnergyLabel] Timeout on attempt ${attempts}:`, timeoutError.message);
let imageUrl;
try {
if (response.data instanceof Blob) {
if (response.data.size < 100) {
throw new Error('Invalid image data (size too small)');
// 재시도 가능한 경우
if (attempts < maxRetries + 1) {
// console.log(`🔄 [EnergyLabel] Retrying... (${attempts}/${maxRetries + 1})`);
attemptConversion();
} else {
// 최종 실패
console.error(`❌ [EnergyLabel] Final failure after ${attempts} attempts:`, pdfUrl);
dispatch({
type: types.CONVERT_PDF_TO_IMAGE_FAILURE,
payload: { pdfUrl, error: timeoutError },
});
callback && callback(timeoutError, null);
}
imageUrl = URL.createObjectURL(response.data);
} else if (response.data instanceof ArrayBuffer) {
if (response.data.byteLength < 100) {
throw new Error('Invalid image data (size too small)');
}, timeout);
dispatch({
type: types.CONVERT_PDF_TO_IMAGE,
payload: pdfUrl,
});
const onSuccess = (response) => {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
const blob = new Blob([response.data], { type: 'image/png' });
imageUrl = URL.createObjectURL(blob);
} else {
const blob = new Blob([response.data], { type: 'image/png' });
imageUrl = URL.createObjectURL(blob);
}
dispatch({
type: types.CONVERT_PDF_TO_IMAGE_SUCCESS,
payload: { pdfUrl, imageUrl },
});
// retCode 체크 (프로젝트 API 규약: 200이어도 retCode로 성공/실패 구분)
const retCode = response.headers?.retcode || response.headers?.retCode;
callback && callback(null, imageUrl);
} catch (error) {
dispatch({
type: types.CONVERT_PDF_TO_IMAGE_FAILURE,
payload: { pdfUrl, error },
});
callback && callback(error, null);
}
if (retCode !== undefined && retCode !== 0 && retCode !== '0') {
const error = new Error(`API Error: retCode=${retCode}`);
console.warn(`⚠️ [EnergyLabel] API returned error on attempt ${attempts}:`, retCode);
// retCode 에러도 재시도
if (attempts < maxRetries + 1) {
console.log(
`🔄 [EnergyLabel] Retrying due to API error... (${attempts}/${maxRetries + 1})`
);
attemptConversion();
} else {
console.error(
`❌ [EnergyLabel] Final failure after ${attempts} attempts (API error):`,
pdfUrl
);
dispatch({
type: types.CONVERT_PDF_TO_IMAGE_FAILURE,
payload: { pdfUrl, error },
});
callback && callback(error, null);
}
return;
}
let imageUrl;
try {
if (response.data instanceof Blob) {
if (response.data.size === 0) {
throw new Error('Invalid image data (empty blob)');
}
imageUrl = URL.createObjectURL(response.data);
} else if (response.data instanceof ArrayBuffer) {
if (response.data.byteLength === 0) {
throw new Error('Invalid image data (empty buffer)');
}
const blob = new Blob([response.data], { type: 'image/png' });
imageUrl = URL.createObjectURL(blob);
} else {
const blob = new Blob([response.data], { type: 'image/png' });
if (blob.size === 0) {
throw new Error('Invalid image data (empty blob)');
}
imageUrl = URL.createObjectURL(blob);
}
console.log(`✅ [EnergyLabel] Conversion successful on attempt ${attempts}:`, pdfUrl);
dispatch({
type: types.CONVERT_PDF_TO_IMAGE_SUCCESS,
payload: { pdfUrl, imageUrl },
});
callback && callback(null, imageUrl);
} catch (error) {
console.error(`❌ [EnergyLabel] Image creation failed on attempt ${attempts}:`, error);
// 이미지 생성 실패도 재시도
if (attempts < maxRetries + 1) {
console.log(
`🔄 [EnergyLabel] Retrying due to image creation error... (${attempts}/${maxRetries + 1})`
);
attemptConversion();
} else {
console.error(
`❌ [EnergyLabel] Final failure after ${attempts} attempts (image error):`,
pdfUrl
);
dispatch({
type: types.CONVERT_PDF_TO_IMAGE_FAILURE,
payload: { pdfUrl, error },
});
callback && callback(error, null);
}
}
};
const onFail = (error) => {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
console.warn(`⚠️ [EnergyLabel] Network error on attempt ${attempts}:`, error.message);
// 네트워크 에러도 재시도
if (attempts < maxRetries + 1) {
console.log(
`🔄 [EnergyLabel] Retrying due to network error... (${attempts}/${maxRetries + 1})`
);
attemptConversion();
} else {
console.error(
`❌ [EnergyLabel] Final failure after ${attempts} attempts (network error):`,
pdfUrl
);
dispatch({
type: types.CONVERT_PDF_TO_IMAGE_FAILURE,
payload: { pdfUrl, error },
});
callback && callback(error, null);
}
};
TAxios(
dispatch,
getState,
'post',
URLS.CONVERT_IMG,
{},
{ pdfUrl },
onSuccess,
onFail,
false,
'blob'
);
};
attemptConversion();
};
const onFail = (error) => {
dispatch({
type: types.CONVERT_PDF_TO_IMAGE_FAILURE,
payload: { pdfUrl, error },
});
callback && callback(error, null);
};
TAxios(
dispatch,
getState,
'post',
URLS.CONVERT_IMG,
{},
{ pdfUrl },
onSuccess,
onFail,
false,
'blob'
);
};
/**
* 여러 PDF를 순차적으로 변환 (백그라운드)
* @param {Array<string>} pdfUrls - 변환할 PDF URL 배열