From 6db213d6028e4ea4224f0085a85c349b3a0d9281 Mon Sep 17 00:00:00 2001 From: aurinex Date: Tue, 16 Dec 2025 00:30:00 +0500 Subject: [PATCH] add tray and add settings --- src/main/main.ts | 68 +++++++++++++++--- src/main/preload.ts | 6 ++ src/renderer/App.tsx | 9 +++ src/renderer/assets/Icons/popa-popa.png | Bin 0 -> 6895 bytes src/renderer/assets/Icons/popa-popa.svg | 18 +++++ src/renderer/components/CoinsDisplay.tsx | 25 ++++++- .../Notifications/CustomTooltip.tsx | 62 ++++++++++++++-- src/renderer/components/TopBar.tsx | 1 + src/renderer/pages/DailyReward.tsx | 6 +- src/renderer/pages/Login.tsx | 2 +- src/renderer/pages/Settings.tsx | 22 +++++- src/renderer/utils/TrayBridge.tsx | 27 +++++++ 12 files changed, 224 insertions(+), 22 deletions(-) create mode 100644 src/renderer/assets/Icons/popa-popa.png create mode 100644 src/renderer/assets/Icons/popa-popa.svg create mode 100644 src/renderer/utils/TrayBridge.tsx diff --git a/src/main/main.ts b/src/main/main.ts index aaeee1b..7f57f11 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -9,7 +9,7 @@ * `./src/main.js` using webpack. This gives us some performance wins. */ import path from 'path'; -import { app, BrowserWindow, shell, ipcMain, Tray, Menu, nativeImage } from 'electron'; +import { app, BrowserWindow, shell, ipcMain, Tray, Menu, nativeImage, type MenuItemConstructorOptions } from 'electron'; import { autoUpdater } from 'electron-updater'; import log from 'electron-log'; import MenuBuilder from './menu'; @@ -68,14 +68,21 @@ const ensureTray = () => { if (tray) return; const RESOURCES_PATH = app.isPackaged - ? path.join(process.resourcesPath, 'assets') - : path.join(__dirname, '../../assets'); + ? path.join(process.resourcesPath, 'assets') + : path.join(__dirname, '../../assets'); - const iconPath = path.join(RESOURCES_PATH, 'icon.png'); - const image = nativeImage.createFromPath(iconPath); + const getAssetPath = (...paths: string[]) => path.join(RESOURCES_PATH, ...paths); - tray = new Tray(image); - tray.setToolTip('Popa Launcher'); + const trayIconPath = getAssetPath('pop-popa.png'); // или 'Icons/popa-popa.png' + const trayImage = nativeImage.createFromPath(trayIconPath); + + tray = new Tray(trayImage); + tray.setToolTip('popa-launcher'); + tray.on('click', () => { + if (!mainWindow) return; + mainWindow.show(); + mainWindow.focus(); + }); const menu = Menu.buildFromTemplate([ { @@ -111,9 +118,46 @@ const applyLoginItemSettings = () => { }; let tray: Tray | null = null; +let isAuthed = false; let mainWindow: BrowserWindow | null = null; +function buildTrayMenu() { + const icon = nativeImage.createFromPath( + app.isPackaged + ? path.join(__dirname, '../../assets/popa-popa.png') + : path.join(process.resourcesPath, 'assets', 'popa-popa.png'), + ); + + const template: MenuItemConstructorOptions[] = [ + { label: 'popa-popa', enabled: false, icon }, + { type: 'separator' }, + + ...(isAuthed + ? ([ + { label: 'Новости', click: () => mainWindow?.webContents.send('tray-navigate', '/news') }, + { label: 'Версии', click: () => mainWindow?.webContents.send('tray-navigate', '/') }, + { label: 'Магазин', click: () => mainWindow?.webContents.send('tray-navigate', '/shop') }, + { label: 'Рынок', click: () => mainWindow?.webContents.send('tray-navigate', '/marketplace') }, + { label: 'Профиль', click: () => mainWindow?.webContents.send('tray-navigate', '/profile') }, + { label: 'Настройки', click: () => mainWindow?.webContents.send('tray-navigate', '/settings') }, + { label: 'Ежедневная награда', click: () => mainWindow?.webContents.send('tray-navigate', '/daily') }, + { label: 'Ежедневные квесты', click: () => mainWindow?.webContents.send('tray-navigate', '/dailyquests') }, + { type: 'separator' }, + { label: 'Выйти', click: () => mainWindow?.webContents.send('tray-logout') }, + ] as MenuItemConstructorOptions[]) + : ([ + { label: 'Войти', click: () => mainWindow?.webContents.send('tray-navigate', '/login') }, + ] as MenuItemConstructorOptions[])), + + { type: 'separator' }, + { label: 'Показать', click: () => { mainWindow?.show(); mainWindow?.focus(); } }, + { label: 'Выход', click: () => app.quit() }, + ]; + + tray?.setContextMenu(Menu.buildFromTemplate(template)); +} + ipcMain.on('ipc-example', async (event, arg) => { const msgTemplate = (pingPong: string) => `IPC test: ${pingPong}`; console.log(msgTemplate(arg)); @@ -192,6 +236,8 @@ const createWindow = async () => { ? path.join(process.resourcesPath, 'assets') : path.join(__dirname, '../../assets'); + ensureTray(); + const getAssetPath = (...paths: string[]): string => { return path.join(RESOURCES_PATH, ...paths); }; @@ -203,7 +249,7 @@ const createWindow = async () => { autoHideMenuBar: true, resizable: true, frame: false, - icon: getAssetPath('icon.png'), + icon: getAssetPath('popa-popa.png'), webPreferences: { webSecurity: false, preload: app.isPackaged @@ -286,3 +332,9 @@ app ipcMain.handle('install-update', () => { autoUpdater.quitAndInstall(); }); + +ipcMain.handle('auth-changed', (_e, payload: { isAuthed: boolean }) => { + isAuthed = Boolean(payload?.isAuthed); + buildTrayMenu(); + return true; +}); diff --git a/src/main/preload.ts b/src/main/preload.ts index ae0a88f..06a8f80 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -20,6 +20,9 @@ export type Channels = | 'stop-minecraft' | 'minecraft-started' | 'apply-launcher-settings' + | 'tray-navigate' + | 'tray-logout' + | 'auth-changed' | 'minecraft-stopped'; const electronHandler = { @@ -42,6 +45,9 @@ const electronHandler = { invoke(channel: Channels, ...args: unknown[]): Promise { return ipcRenderer.invoke(channel, ...args); }, + removeAllListeners(channel: Channels) { + ipcRenderer.removeAllListeners(channel); + }, }, }; diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 13aaeab..95d68d1 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -25,6 +25,7 @@ import { useLocation } from 'react-router-dom'; import DailyReward from './pages/DailyReward'; import DailyQuests from './pages/DailyQuests'; import Settings from './pages/Settings'; +import { TrayBridge } from './utils/TrayBridge'; const AuthCheck = ({ children }: { children: ReactNode }) => { const [isAuthenticated, setIsAuthenticated] = useState(null); @@ -205,6 +206,13 @@ const AppLayout = () => { } }, []); + useEffect(() => { + const raw = localStorage.getItem('launcher_config'); + const isAuthed = !!raw && !!JSON.parse(raw).accessToken; + + window.electron.ipcRenderer.invoke('auth-changed', { isAuthed }); + }, []); + return ( {/* ФОН — НЕ масштабируется */} @@ -247,6 +255,7 @@ const AppLayout = () => { + cTChz@b>5E9QCLNg1`v~h2>=oq06_1S)2t(PH$Y&m_6z~s{?`iH z%2Mtu#BR#Qo&Z2X{hxqGcuxW48s!e`@5W;{F=|GG>ML|Y&Bow5Q$NA z)=TA~7cnp6A@8l$UbPB$_Y;Gj*AgtSKP6(1uoDUpD4LLZCD?I(-Fzr*f4gr?l1-6B!@yB>*c)&0;h7E>UhjPNVP!lAu?!^5CHf*`Udkh#KY6CbpDdV9t ztVnxmKo|fS^f!CCceCRNA45dcNs;e|gN?PWz;xniBQZoYV4Tp*g4mu5B35Q<8j)HD zAw~e*=roH1obJux0$izTnC^06O(Z^7paIQ{2_R=-v3#!(=<*QZ;{j?IJ5>SfxM7J~ zg_tRI$GzB?^JNPGDt!cJ0i*m02YeQU#^3-Cakz@Bky*1G zLjfKLCNw=rhz|8c!5s%k5w7>U(f}ku>|eQ~AJjU7t^He@4)#Wk$yN7?h$SAed1;J~j2XNrxsky|F`Ic4N6{9?HaY_075MCSU32 z?q+_-O%V(SW}?HBcEd&#mIA^!V#^VD4cVz27s`m|mC{_WbYrLdkkv?fR#>J|w`Ga$ zXHn9gC15xFN#}>d(c9bG zTC{O}Uo-tzkjLEc{zacnlBRM0ONw^CQh8`|{W3Qu`ml{6S6cetO5`b2pg5D1@3W^ z20SBPaIvd+m?X2efJj%r4At?rvVjY*^Ru&Ob4f`_FTH$x2ms0`tM(y3M@ODJQdxj| zj@n$t#-+~|^*;SfR%1hEEI{SXMlB;FC>jKN0a{SK|^N9Pw(7h6Tpd-r+> zWsX8;mFT|S$K}O`+(q|Fyx=Gol}-WnjskcPdBmNI%1tUK3+)@Ux!&-Xw)`-}?Ho;l zNO?@%_CAb`9!KHrIg<%WI&Lj_;Zr`v9Jbx_z3RfoS4ly#f18`De->vXN>Nd@WS)}AtzCxelL5zimxUMm&a3D)r>4G zvJC+zJKMbIAe5gXF96Xg(LMj`@i_=(`n(meQNJy;zoTx^aF9_$K0Dn=46&D+2p3K> zSpkJK(EKmA92#CRI9POG0gd`zV}%Ql638Dh7caE-aS28vzD@ZgR5nuKg%*tD#C@sW zIk{kg=%A31nPc4PUoEUQV1T-YMy|Mo#6=BusVVsKRI6d%EG<`*mjK>R3W!+)W!RmZ zg-Wa-OhBj7fIee-%cfk8OaQ$<&Y(2e18i$c~Vt>Jx8Kuokc2)cy9)D0A%0Y z8SB%^Q+A3+J2u8nOiWm`cw!l69UFIdPcGJnrA+zK)AtUE2kyuD25nTkoh8h!54E*S zB$;aC=Ly8`r+2WhTok>&zm2R8cr=qnh{mXEYG_=in^f9eTwdPF#1^{RxxUEZ-x#KS zWN@W+*(&JCcAMYcE@Kw3T~af%Ht$qDz3{SwIq-B3B-r$^R|g8h+Y54kNMJx_Ig%yZ zae9T*!AA0?#Qzb{45;H~_?Orto|_=E|88iicw7DH6Q89_N{{VZ^J_CFgdo$H5R@y) zXKfS|u`35!ub2$?7~~0YwZe|pHCQ^4`Ik5|A52c_f-O`(sU)K@yn^yiU z$8#MUfX@7C1wF-+A8*@YwxYyS+rR9-@e`i+3X6SO#W z2JsbvxFbH4fk7S`RWtK3neF^!bX1e(E}eD5TWwuk4@l%GgZwLET(YGqXLs`(XO>>l zA*5VB2f;96jWY*BoS%g))HGp`rQlzwew48x7rkU+-naLq8n=1O+;=z44gPK0M4wHW z#N8#XZgJ7pyY%NIAt6B?L-Z?b?Do0ln9zsDkMG|*ZGQXqE&K1Kc?vvirCYV+k6$vo zEJe@JgZ_+w&t_5BJQNqr4WCMK^r+Z+yzD&&hOR19%lsy_I~6WC$76{5YY|Y{!!$h& zTr51;jENzZgu~sk_&4xiI~tNFe_Sefay$bIxdPfXYQz?)^cDnhkhD%IY_J{M4P=}k znsfz~UDeqGyt?_=KDK1ih&+=Ba`!Y zbXoE2E8Z(um`UZS;Yq+X%WM@&QVD?@clqXwKVG5UX_n7<@soSkp_cRsJD^Zu)}1x& zbkW?>a!A_a7EFovSoBxUf;2z>Z<)1 z8RDmENpgIAyrr8PJcK=_aI5g{h0{(8*zRh@d|_9qJMRp|HT3fG5-Ke%V})&PA9PK) z>C>@h=M4S(=jYuQOB-wj^@&kdM3}UjBke02J!j@60I}?Cd@tw3Xs)E;$mnn)i|?ZQ6pV^`f(w#5znPpF*PlTS z{CA-^b;G6DVJXjsx>`B$xAyiBOatlk;K=L*{zpK;ob!^Th{)+Sm*c)q0rs1-$N*yk z!Rg`hYsF}Cn92u?Cka75k9@$9=joh13-Xzk_%D5+b9|gK&u@)bT}$iMwtns(tc=_D zU~lU@h=z9QTHy72@4?4mGbWViUK8i#kIe2= z58!t4!zn53`~V}K+h$kz`gMw)-mb?g)TY0sxw(oveP~R0eFG23@mf zS8QHwG1F4_ay`wyDF}&-o0H1p*1XSF8u;>`h3KN+--j=6Y?ImPn_B20wRfA(=1Co@ z?9UKAJ3hYZ6!%>|`f_w~{pj-bJZ)L zpCSWYO23swA2h!7btw#-{jj}oIW8KdNgAeb@`2>7YKd3L$Nc4uvU9<8Yx70tc(pZD zIDJ+?-n;y4OxLiR<`-iS#Ilk#(_7h^P%U{@m^UbduBE&f$ydc$sf%a(X>hxO-nn|g zcngy#6#s9J%$}?|KH&AV%B-rI^S1jL)Y^QRb+1(RUaAdcO zUNZp=S>cQrVyKihj}lNs7s|6Gj$SqW-5aw;=uqVSkd1yUJ7=)7XLPRAnbUGj@1DdR zssJFN9OG9XlfUd}^d%nI;NA30lJ1_Fid#EurfmNG zCK;1M9{sz$em6Q}zpRS^FWYTcn^zQTb7+TRxKxru^7HcAvoa}CRXGNK9KKWs39QZp z1_tiBGaWX~-0$r^c4$`;NFFg*Q$Pdq%IHDW54!AujqR~Q2$`7W%QAY_HOi-pWZcJL z!(iH-I$}A>;$0M5p`}L4Q-tLNxgtj2oAt%aC7I_4G;7@s&w$_oIu@PO{E!Nl4i~~< z(p_NJ(;!_Gy#DAR0V$4$CX+HqQ61)EyXbrz23JulPM-1*!1HvkZwXfo4kbWn!-?LJ zSi7i>NJ!!X#*X52knn~&o?L1N7|e5K4tJ_?J9yf$(JDhe=vZ>X@U5Kd=|fIVx27A@-!iAb&X=c#M#_6MOq z0*^SAKB+KYfHc&)gm$$~+ZWA}OPP_ZLNrSTJU+A3h8^#qY4;uaJ+*0ZxVbk^FqxA{ z$qID6PsHiluN8pKIE%T+3>$I?{^t%zsw=0by?uq#qk#>I_wp~S_udmuo)7ShB zkN%jRh~05$DMRVEq?Y-8Ww>n;N0b7)63FA4ZcWzu6&Ad*3G_bOEit( zi3%R4+_QJ{S$hqDUO^#mtT!tTyPuX#mCni=7DO_)XEvHE(7p=s#H4V zb;el5+I_dx4lMW82~9!S=GJMdxUWpZy*JKQAFF*_2A3qqk={Gl<%-ZB}Z z*A_ft{l@mF_z?=1$n!b7w@donQA^-g?dO4h2Yt(ZJE^-Xbo&WYeHG^ecq9>V>a5yt z6iBi@vnjYH(Yy^dN>=Jd;p??ds^}VS`QlLP=gh0o%o79kBLTk@n{H?0d=mDvf?h3q zzK*}m$Mse&Wvu|WKgX9<{pRZWrJ>)Xf4A?CF*+?p@qX45e}!UjIakIXQ*LC~@zFzS z4@N0`tMM6wQ(k^(XXK_uC<_6>^3s)fj%PvCO%+k?1B{VS3MVQ5A2P0AX{_`Gj@beQ z-eaxk395fr1$3l^Q*KAbJsXjVCxcCAbP~PLzn{R~$&`%lMNXyso^9oDG>iPE6C?(( z4R9UX>!PqYyS`_~5_o(GT?nH57k9e_9?#10?`qPzSMO@GBDZ6Znjy46_`WU~YuSc# z{g*#9%+IRQta)0^5H7fW)r2C5TIq^*#Vt-upwEe0#eKaRv%u2H60${i?0st0B`{|8 z%}P_1R?t(>vT#nd_KK8x;4Sm78Z<_klu#3ib-S>OeEQEd=0B)&UWbmFRD7n)x~6FVe;bd(S@4LM~V)-SY30?G%XNKB{FMX7Y9-n5{;eO zsFZwlG9^iY`*^Z2j0iME6pjCoh#e+i39bfdA-q3ZUv7|Nr5@n2PeQm z8TdMh`UPbO#CvNoU(dj_BKY8(fJQQ0#FyY0OZ*n$$!FXV<?I%uJ-H9kk&G$4svaCw7_M&cS_pCtQGd8Kd9G;?DE;_Yju2DkDzZ0{c1fpmy z86E?P86}gERemybFZXjnfI`G$bqQh*LJV*FMN>y-z3Cy+&OZ_JiYN92yEVEjn%~O? zrDhe9Z(f_6exlwGsq!$x-E#9l$0)lv|Mim-V$+%YMXk+yS|&8Jlw5YD_B72S6$!aS zKJj~iyT}I)aIP)|9$2Gb-FNSqq5orvxGr_1~omq`Q55cfo@(90^=zH3sHQdAQmLVGg8@=Jj8Tvz-GukSq@|5S-sGW-) zjyfqdXjsCY^g!@ALEYbiZ}>kYjqx!Jzo^h@9^oK^t$5D^4qG9%OeGQ;ptRFVG=;k? zo0)PmPm_aY^4h>maT%SOU+>J{8AsUvyYf&bCebl63|^3Tz$aFQCL$%`B(lt~>>Nix zGWEH09+V^T2+F57EFbG5j>8S}8w;*4Z*I7S?!}H)VOk8|3V9Kz@P2dd*Vd4w<0sH| zvckw)s*ep@JANz$wo7k+`v9FC!Ium|Yn%#JJCCR>p>hy>N_F7--m!F;0<{7@gcM@^ zOj!t#0}$GmIRv7ZJiu)uUBm#4^ zz!I`>_qG1j4s@EpV^AfxKFENT#>6mtwuOd_`vh0z_g^OwK66yvDhM*Ob zFK$fCXIZxo)g!F6kuh|oCz7f!G_2XUlxYZ8#73;24soeQD10xWQg&MWAFONqS0vfI z6}@2zMD4t7=v(r=E9`K1DH(Y^xAL-;u<@|HQ-Aw-sS$k04^_|zp(TF{{uvm^iuBtT>obUuNN-1o?cciZvQVvPy#OezZaS=FKfB@p literal 0 HcmV?d00001 diff --git a/src/renderer/assets/Icons/popa-popa.svg b/src/renderer/assets/Icons/popa-popa.svg new file mode 100644 index 0000000..6000dfc --- /dev/null +++ b/src/renderer/assets/Icons/popa-popa.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/renderer/components/CoinsDisplay.tsx b/src/renderer/components/CoinsDisplay.tsx index 47eb6a7..cd2b920 100644 --- a/src/renderer/components/CoinsDisplay.tsx +++ b/src/renderer/components/CoinsDisplay.tsx @@ -42,6 +42,8 @@ export default function CoinsDisplay({ sx, }: CoinsDisplayProps) { + const [isLoading, setIsLoading] = useState(false); + const [settingsVersion, setSettingsVersion] = useState(0); const storageKey = useMemo(() => { // ключ под конкретного пользователя return username ? `coins:${username}` : 'coins:anonymous'; @@ -71,7 +73,24 @@ export default function CoinsDisplay({ return 0; }); - const [isLoading, setIsLoading] = useState(false); + useEffect(() => { + const handler = () => setSettingsVersion((v) => v + 1); + window.addEventListener('settings-updated', handler as EventListener); + return () => window.removeEventListener('settings-updated', handler as EventListener); + }, []); + + const isTooltipDisabledBySettings = useMemo(() => { + try { + const raw = localStorage.getItem('launcher_settings'); + if (!raw) return false; + const s = JSON.parse(raw); + return Boolean(s?.disableToolTip); + } catch { + return false; + } + }, [settingsVersion]); + + const tooltipEnabled = showTooltip && !isTooltipDisabledBySettings; const getSizes = () => { switch (size) { @@ -172,7 +191,7 @@ export default function CoinsDisplay({ borderRadius: sizes.borderRadius, padding: sizes.containerPadding, border: '1px solid rgba(255, 255, 255, 0.1)', - cursor: showTooltip ? 'help' : 'default', + cursor: tooltipEnabled ? 'help' : 'default', // можно оставить лёгкий намёк на загрузку, но без "пульса" текста opacity: isLoading ? 0.85 : 1, @@ -222,7 +241,7 @@ export default function CoinsDisplay({ ); - if (showTooltip) { + if (tooltipEnabled) { return ( ( +const STORAGE_KEY = 'launcher_settings'; + +function readDisableTooltip(): boolean { + try { + const raw = localStorage.getItem(STORAGE_KEY); + if (!raw) return false; + const s = JSON.parse(raw); + return Boolean(s?.disableToolTip); + } catch { + return false; + } +} + +const readTooltipPolicy = () => { + try { + const raw = localStorage.getItem('launcher_settings'); + if (!raw) return { disableToolTip: false, allowEssentialTooltips: true }; + const s = JSON.parse(raw); + return { + disableToolTip: Boolean(s?.disableToolTip), + allowEssentialTooltips: s?.allowEssentialTooltips !== false, // default true + }; + } catch { + return { disableToolTip: false, allowEssentialTooltips: true }; + } +}; + +// ВАЖНО: styled-компонент отдельно, чтобы не пересоздавался на каждый рендер +const StyledTooltip = styled(({ className, ...props }: TooltipProps) => ( -))(({ theme }) => ({ +))(() => ({ [`& .${tooltipClasses.tooltip}`]: { backgroundColor: 'rgba(0, 0, 0, 0.9)', color: '#fff', @@ -72,4 +99,29 @@ const CustomTooltip = styled(({ className, ...props }: TooltipProps) => ( }, })); -export default CustomTooltip; +export type CustomTooltipProps = TooltipProps & { + /** + * Можно принудительно отключить тултип снаружи, + * плюс учитывается настройка disableToolTip из launcher_settings + */ + essential?: boolean; + disabled?: boolean; +}; + +export default function CustomTooltip(props: CustomTooltipProps) { + const { essential = false, children, ...rest } = props; + + const { disableToolTip, allowEssentialTooltips } = useMemo( + () => readTooltipPolicy(), + // важно: чтобы при "Save" пересчитывалось — ты уже диспатчишь settings-updated + // поэтому ниже мы просто прочитаем ещё раз через key в местах использования (или можно слушать event тут) + [], + ); + + const disabledBySettings = disableToolTip && !(essential && allowEssentialTooltips); + + // Если отключено — просто возвращаем children без обёртки Tooltip + if (disabledBySettings) return <>{children}; + + return {children}; +} diff --git a/src/renderer/components/TopBar.tsx b/src/renderer/components/TopBar.tsx index 3447505..fc6479d 100644 --- a/src/renderer/components/TopBar.tsx +++ b/src/renderer/components/TopBar.tsx @@ -206,6 +206,7 @@ export default function TopBar({ onRegister, username }: TopBarProps) { const logout = () => { localStorage.removeItem('launcher_config'); navigate('/login'); + window.electron.ipcRenderer.invoke('auth-changed', { isAuthed: false }); }; const loadSkin = useCallback(async () => { diff --git a/src/renderer/pages/DailyReward.tsx b/src/renderer/pages/DailyReward.tsx index 22ff693..52d7e43 100644 --- a/src/renderer/pages/DailyReward.tsx +++ b/src/renderer/pages/DailyReward.tsx @@ -435,7 +435,7 @@ export default function DailyReward({ onClaimed }: Props) { - + - + - + setSelected(today)} sx={{ diff --git a/src/renderer/pages/Login.tsx b/src/renderer/pages/Login.tsx index 4f5e294..a576be4 100644 --- a/src/renderer/pages/Login.tsx +++ b/src/renderer/pages/Login.tsx @@ -96,7 +96,7 @@ const Login = ({ onLoginSuccess }: LoginProps) => { if (onLoginSuccess) { onLoginSuccess(config.username); } - + await window.electron.ipcRenderer.invoke('auth-changed', { isAuthed: true }); navigate('/'); } catch (error: any) { console.log(`ОШИБКА при авторизации: ${error.message}`); diff --git a/src/renderer/pages/Settings.tsx b/src/renderer/pages/Settings.tsx index e96db4e..a4e1f09 100644 --- a/src/renderer/pages/Settings.tsx +++ b/src/renderer/pages/Settings.tsx @@ -29,6 +29,8 @@ type SettingsState = { autoLaunch: boolean; startInTray: boolean; closeToTray: boolean; + disableToolTip: boolean; + allowEssentialTooltips: boolean; // Game autoRotateSkinViewer: boolean; @@ -73,7 +75,7 @@ const GRADIENT = backgroundColor: 'rgba(10,10,20,0.92)', border: '2px solid rgba(255,255,255,0.18)', boxShadow: '0 0 1.6vw rgba(233,64,205,0.35)', - transition: 'transform 0.15s ease, box-shadow 0.15s ease', + transition: 'transform 0.15s ease, box-shadow 0.15s ease, height 0.3s ease, width 0.3s ease', '&:before': { display: 'none' }, '&:hover, &.Mui-focusVisible': { width: '1.95vw', @@ -100,6 +102,8 @@ const defaultSettings: SettingsState = { startInTray: false, autoLaunch: false, closeToTray: true, + disableToolTip: false, + allowEssentialTooltips: true, autoRotateSkinViewer: true, walkingSpeed: 0.5, @@ -351,7 +355,7 @@ const Settings = () => { sx={{ px: '2vw', pb: '2vw', - width: '100%', + width: '95%', boxSizing: 'border-box', }} > @@ -567,6 +571,20 @@ const Settings = () => { checked={settings.rememberLastRoute} onChange={setFlag('rememberLastRoute')} /> + + + + diff --git a/src/renderer/utils/TrayBridge.tsx b/src/renderer/utils/TrayBridge.tsx new file mode 100644 index 0000000..7cd448a --- /dev/null +++ b/src/renderer/utils/TrayBridge.tsx @@ -0,0 +1,27 @@ +import { useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; + +export function TrayBridge() { + const navigate = useNavigate(); + + useEffect(() => { + const onNavigate = (to: unknown) => { + navigate(String(to)); + }; + + const onLogout = () => { + localStorage.removeItem('launcher_config'); + navigate('/login'); + }; + + window.electron.ipcRenderer.on('tray-navigate', onNavigate); + window.electron.ipcRenderer.on('tray-logout', onLogout); + + return () => { + window.electron.ipcRenderer.removeAllListeners('tray-navigate'); + window.electron.ipcRenderer.removeAllListeners('tray-logout'); + }; + }, [navigate]); + + return null; +}