Merge branch 'feat/VersionsExplorer' of https://git.popa-popa.ru/DIKER/popa-launcher into feat/VersionsExplorer
This commit is contained in:
154
package-lock.json
generated
154
package-lock.json
generated
@ -8,7 +8,6 @@
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@electron/notarize": "^3.0.0",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@mui/icons-material": "^7.2.0",
|
||||
@ -33,6 +32,7 @@
|
||||
"react-router-dom": "^7.3.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"skinview3d": "^3.4.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"three": "^0.178.0",
|
||||
"util": "^0.12.5",
|
||||
@ -2275,19 +2275,6 @@
|
||||
"node": ">=12.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/notarize": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-3.1.1.tgz",
|
||||
"integrity": "sha512-uQQSlOiJnqRkTL1wlEBAxe90nVN/Fc/hEmk0bqpKk8nKjV1if/tXLHKUPePtv9Xsx90PtZU8aidx5lAiOpjkQQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"promise-retry": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@electron/osx-sign": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.1.tgz",
|
||||
@ -4469,6 +4456,12 @@
|
||||
"@sinonjs/commons": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@svgr/babel-plugin-add-jsx-attribute": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz",
|
||||
@ -10619,6 +10612,66 @@
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "6.6.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
|
||||
"integrity": "sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.17.1",
|
||||
"xmlhttprequest-ssl": "~2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-client/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-client/node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "0.9.1",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz",
|
||||
@ -10682,6 +10735,7 @@
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
|
||||
"integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/error-ex": {
|
||||
@ -20088,6 +20142,7 @@
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
|
||||
"integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"err-code": "^2.0.2",
|
||||
@ -20999,6 +21054,7 @@
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
|
||||
"integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
@ -22107,6 +22163,68 @@
|
||||
"tslib": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-client": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
|
||||
"integrity": "sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io-client": "~6.6.1",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-client/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser/node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/sockjs": {
|
||||
"version": "0.3.24",
|
||||
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz",
|
||||
@ -25093,6 +25211,14 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/xmlhttprequest-ssl": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
|
||||
"integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
|
||||
@ -105,7 +105,6 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron/notarize": "^3.0.0",
|
||||
"@emotion/react": "^11.14.0",
|
||||
"@emotion/styled": "^11.14.1",
|
||||
"@mui/icons-material": "^7.2.0",
|
||||
@ -130,6 +129,7 @@
|
||||
"react-router-dom": "^7.3.0",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"skinview3d": "^3.4.1",
|
||||
"socket.io-client": "^4.8.1",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"three": "^0.178.0",
|
||||
"util": "^0.12.5",
|
||||
@ -206,7 +206,6 @@
|
||||
"productName": "popa-launcher",
|
||||
"appId": "org.erb.ElectronReact",
|
||||
"asar": true,
|
||||
"afterSign": ".erb/scripts/notarize.js",
|
||||
"asarUnpack": "**\\*.{node,dll}",
|
||||
"files": [
|
||||
"dist",
|
||||
@ -263,7 +262,7 @@
|
||||
],
|
||||
"publish": {
|
||||
"provider": "generic",
|
||||
"url": "https://git.popa-popa.ru/DIKER/popa-launcher/releases/download/v${version}",
|
||||
"url": "https://git.popa-popa.ru/DIKER/popa-launcher/releases/download/latest",
|
||||
"channel": "latest",
|
||||
"requestHeaders": {
|
||||
"Authorization": "token ${env.GH_TOKEN}"
|
||||
|
||||
4
release/app/package-lock.json
generated
4
release/app/package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "popa-launcher",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "popa-launcher",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT"
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "popa-launcher",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.1",
|
||||
"description": "Popa Launcher",
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
|
||||
@ -96,7 +96,16 @@ const ensureTray = () => {
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Выход',
|
||||
click: () => app.quit(),
|
||||
click: () => {
|
||||
isQuitting = true;
|
||||
|
||||
if (mainWindow) {
|
||||
mainWindow.removeAllListeners('close');
|
||||
mainWindow.destroy(); // ⬅ КЛЮЧЕВО
|
||||
}
|
||||
|
||||
app.quit();
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
@ -119,6 +128,7 @@ const applyLoginItemSettings = () => {
|
||||
|
||||
let tray: Tray | null = null;
|
||||
let isAuthed = false;
|
||||
let isQuitting = false;
|
||||
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
|
||||
@ -152,7 +162,19 @@ function buildTrayMenu() {
|
||||
|
||||
{ type: 'separator' },
|
||||
{ label: 'Показать', click: () => { mainWindow?.show(); mainWindow?.focus(); } },
|
||||
{ label: 'Выход', click: () => app.quit() },
|
||||
{
|
||||
label: 'Выход',
|
||||
click: () => {
|
||||
isQuitting = true;
|
||||
|
||||
if (mainWindow) {
|
||||
mainWindow.removeAllListeners('close');
|
||||
mainWindow.destroy(); // ⬅ КЛЮЧЕВО
|
||||
}
|
||||
|
||||
app.quit();
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
tray?.setContextMenu(Menu.buildFromTemplate(template));
|
||||
@ -259,9 +281,8 @@ const createWindow = async () => {
|
||||
});
|
||||
|
||||
mainWindow.on('close', (e) => {
|
||||
if (launcherSettings.closeToTray) {
|
||||
if (!isQuitting && launcherSettings.closeToTray) {
|
||||
e.preventDefault();
|
||||
ensureTray();
|
||||
mainWindow?.hide();
|
||||
}
|
||||
});
|
||||
@ -317,6 +338,10 @@ app.on('window-all-closed', () => {
|
||||
}
|
||||
});
|
||||
|
||||
app.on('before-quit', () => {
|
||||
isQuitting = true;
|
||||
});
|
||||
|
||||
app
|
||||
.whenReady()
|
||||
.then(() => {
|
||||
|
||||
@ -4,7 +4,7 @@ interface HeadAvatarProps {
|
||||
skinUrl?: string;
|
||||
size?: number;
|
||||
style?: React.CSSProperties;
|
||||
version?: number; // ✅ добавили
|
||||
version?: number;
|
||||
}
|
||||
|
||||
const DEFAULT_SKIN =
|
||||
@ -14,15 +14,17 @@ export const HeadAvatar: React.FC<HeadAvatarProps> = ({
|
||||
skinUrl,
|
||||
size = 24,
|
||||
style,
|
||||
version = 0, // ✅ дефолт
|
||||
version = 0,
|
||||
...canvasProps
|
||||
}) => {
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
const requestIdRef = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
const baseUrl = skinUrl?.trim() ? skinUrl : DEFAULT_SKIN;
|
||||
requestIdRef.current += 1;
|
||||
const requestId = requestIdRef.current;
|
||||
|
||||
// ✅ cache-bust: чтобы браузер НЕ отдавал старую картинку
|
||||
const baseUrl = skinUrl?.trim() ? skinUrl : DEFAULT_SKIN;
|
||||
const finalSkinUrl = `${baseUrl}${baseUrl.includes('?') ? '&' : '?'}v=${version}`;
|
||||
|
||||
const canvas = canvasRef.current;
|
||||
@ -33,6 +35,9 @@ export const HeadAvatar: React.FC<HeadAvatarProps> = ({
|
||||
img.src = finalSkinUrl;
|
||||
|
||||
img.onload = () => {
|
||||
// ✅ игнорим старые onload
|
||||
if (requestIdRef.current !== requestId) return;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) return;
|
||||
|
||||
@ -47,9 +52,16 @@ export const HeadAvatar: React.FC<HeadAvatarProps> = ({
|
||||
};
|
||||
|
||||
img.onerror = (e) => {
|
||||
if (requestIdRef.current !== requestId) return;
|
||||
console.error('Не удалось загрузить скин для HeadAvatar:', e);
|
||||
};
|
||||
}, [skinUrl, size, version]); // ✅ version добавили
|
||||
|
||||
return () => {
|
||||
// ✅ гарантированно “убиваем” обработчики старого запроса
|
||||
img.onload = null;
|
||||
img.onerror = null;
|
||||
};
|
||||
}, [skinUrl, size, version]);
|
||||
|
||||
return (
|
||||
<canvas
|
||||
|
||||
@ -49,6 +49,7 @@ import GradientTextField from '../components/GradientTextField';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import type { SxProps, Theme } from '@mui/material/styles';
|
||||
import { getWsBaseUrl } from '../realtime/wsBase';
|
||||
|
||||
interface TabPanelProps {
|
||||
children?: React.ReactNode;
|
||||
@ -80,6 +81,18 @@ const GLASS_BG =
|
||||
const GLASS_BORDER = '1px solid rgba(255,255,255,0.08)';
|
||||
const GLASS_SHADOW = '0 1.2vw 3.2vw rgba(0,0,0,0.55)';
|
||||
|
||||
function upsertIntoList<T extends { id: string }>(list: T[], item: T) {
|
||||
const idx = list.findIndex((x) => x.id === item.id);
|
||||
if (idx === -1) return [item, ...list];
|
||||
const copy = list.slice();
|
||||
copy[idx] = item;
|
||||
return copy;
|
||||
}
|
||||
|
||||
function removeFromList<T extends { id: string }>(list: T[], id: string) {
|
||||
return list.filter((x) => x.id !== id);
|
||||
}
|
||||
|
||||
export default function Marketplace() {
|
||||
const [marketLoading, setMarketLoading] = useState<boolean>(false);
|
||||
|
||||
@ -154,6 +167,91 @@ export default function Marketplace() {
|
||||
}
|
||||
};
|
||||
|
||||
type MarketListedPayload = { serverIp: string; item: MarketplaceItemResponse };
|
||||
type MarketSoldPayload = { serverIp: string; itemId: string };
|
||||
type MarketPricePayload = { serverIp: string; item: MarketplaceItemResponse };
|
||||
type MarketCancelledPayload = { serverIp: string; itemId: string };
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedServer) return;
|
||||
|
||||
const serverIp = selectedServer.ip;
|
||||
|
||||
const base = 'wss://minecraft.api.popa-popa.ru'
|
||||
const wsUrl = `${base}/ws/marketplace?server_ip=${encodeURIComponent(serverIp)}`;
|
||||
|
||||
const ws = new WebSocket(wsUrl);
|
||||
|
||||
const ping = window.setInterval(() => {
|
||||
if (ws.readyState === WebSocket.OPEN) ws.send('ping');
|
||||
}, 25000);
|
||||
|
||||
ws.onmessage = (ev) => {
|
||||
let msg: any;
|
||||
try {
|
||||
msg = JSON.parse(ev.data);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.server_ip !== serverIp && msg.serverIp !== serverIp) return;
|
||||
|
||||
switch (msg.event) {
|
||||
case 'market:item_listed': {
|
||||
const item: MarketplaceItemResponse = msg.item;
|
||||
setMarketItems((prev) => {
|
||||
if (!prev) return prev;
|
||||
if (prev.page !== 1) return prev;
|
||||
return { ...prev, items: upsertIntoList(prev.items, item) };
|
||||
});
|
||||
|
||||
if (item?.seller_name === username) {
|
||||
setMyItems((prev) => {
|
||||
if (!prev) return prev;
|
||||
if (prev.page !== 1) return prev;
|
||||
return { ...prev, items: upsertIntoList(prev.items, item) };
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'market:item_sold': {
|
||||
const itemId: string = msg.item_id ?? msg.itemId;
|
||||
setMarketItems((prev) => (prev ? { ...prev, items: removeFromList(prev.items, itemId) } : prev));
|
||||
setMyItems((prev) => (prev ? { ...prev, items: removeFromList(prev.items, itemId) } : prev));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'market:item_cancelled': {
|
||||
const itemId: string = msg.item_id ?? msg.itemId;
|
||||
setMarketItems((prev) => (prev ? { ...prev, items: removeFromList(prev.items, itemId) } : prev));
|
||||
setMyItems((prev) => (prev ? { ...prev, items: removeFromList(prev.items, itemId) } : prev));
|
||||
break;
|
||||
}
|
||||
|
||||
case 'market:item_price_updated': {
|
||||
const item: MarketplaceItemResponse = msg.item;
|
||||
setMarketItems((prev) => (prev ? { ...prev, items: upsertIntoList(prev.items, item) } : prev));
|
||||
setMyItems((prev) => (prev ? { ...prev, items: upsertIntoList(prev.items, item) } : prev));
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ws.onerror = (e) => {
|
||||
console.error('Marketplace WS error:', e);
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
window.clearInterval(ping);
|
||||
};
|
||||
|
||||
return () => {
|
||||
window.clearInterval(ping);
|
||||
ws.close();
|
||||
};
|
||||
}, [selectedServer?.ip, username]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tabValue !== 2) return;
|
||||
if (!selectedServer) return;
|
||||
@ -285,7 +383,7 @@ export default function Marketplace() {
|
||||
const loadMarketItems = async (serverIp: string, pageNumber: number) => {
|
||||
try {
|
||||
setMarketLoading(true);
|
||||
const marketData = await fetchMarketplace(serverIp, pageNumber, 10);
|
||||
const marketData = await fetchMarketplace(serverIp, pageNumber, 12);
|
||||
setMarketItems(marketData);
|
||||
setPage(marketData.page);
|
||||
setTotalPages(marketData.pages);
|
||||
@ -988,6 +1086,7 @@ export default function Marketplace() {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
ml: '25%',
|
||||
flexDirection: 'column',
|
||||
background: 'rgba(20,20,20,0.78)',
|
||||
borderRadius: '2.0vw',
|
||||
|
||||
14
src/renderer/realtime/wsBase.ts
Normal file
14
src/renderer/realtime/wsBase.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export function getWsBaseUrl(): string {
|
||||
// 1) если ты пробрасываешь конфиг в window
|
||||
const w = window as any;
|
||||
if (w.__ENV__?.WS_BASE) return String(w.__ENV__.WS_BASE);
|
||||
|
||||
// 2) если открыто с https/http — строим ws/wss автоматически
|
||||
if (typeof window !== 'undefined' && window.location?.origin) {
|
||||
const origin = window.location.origin; // http(s)://host
|
||||
return origin.replace(/^http/, 'ws');
|
||||
}
|
||||
|
||||
// 3) дефолт
|
||||
return 'wss://minecraft.api.popa-popa.ru';
|
||||
}
|
||||
Reference in New Issue
Block a user