- Admin panel: added Edit button on channel cards with inline edit form
(title, link, sort order) using existing PATCH API
- Subscription screen: filter out already-subscribed channels so users
only see what they still need to subscribe to
- Updated channelIdHint to remove @username reference (numeric only)
- Added i18n keys: edit, editing, form.save (en/ru/fa/zh)
- Channel subscription blocking screen with per-channel status display
- Safe URL handling with protocol validation (no open redirect)
- Race-condition-safe subscription check with useRef guard
- Admin channel management page with create, toggle, delete
- Error handlers on all admin mutations with haptic feedback
- Proper type guard for channel subscription error detection
- Accessibility: aria-labels on icon-only buttons
- Localization: en, ru, zh, fa translations
Replace all bare useAuthStore(), useBlockingStore(), and
useSuccessNotification() calls with individual field selectors.
This prevents components from re-rendering when unrelated store
fields change.
- 21 useAuthStore usages across 20 files: individual selectors
for 1-2 fields, useShallow for 3+ fields
- 5 useBlockingStore usages: individual selectors
- 2 useSuccessNotification usages: individual selectors
- Remove mix-blend-mode: overlay from body::before noise texture
(was forcing fullscreen GPU compositing on every paint)
- Disable noise texture on mobile via display:none media query
- Replace backdrop-filter animation in backdropFadeIn/Out keyframes
with opacity-only animation (backdrop-filter transition is expensive)
- Replace toast progress bar width animation with scaleX (GPU-composited)
- Remove redundant will-change: transform from .glass and .wave-blob
- Fix isWebglAvailable() leaking a full WebGL context on every check
by replacing OGL Renderer with raw canvas.getContext() + loseContext()
- Add visibilitychange listener to fully stop rAF loop when tab hidden
and restart on visibility restore (was running 24/7 in background)
- Properly destroy WebGL context on unmount via WEBGL_lose_context
extension and gl.deleteProgram() (was only nulling JS refs)
- Reduce target FPS from 20 to 10 (slow gradient, visually identical)
Mirrors campaign.ts localStorage pattern to capture ?ref= from URL and pass
referral_code to backend during Telegram Widget, OAuth, email login, and
Mini App authentication. Fixes redirect loop when email auth is disabled.
- Add campaign.ts utility (capture from URL, localStorage with TTL, validation)
- Pass campaign_slug in all auth API methods (telegram, widget, email, oauth)
- Consume slug in auth store login methods, set pendingCampaignBonus state
- Add CampaignBonusNotifier component (toast on bonus, mounted in AppShell)
- Show web_link alongside bot deep_link in AdminCampaignStats
- Add CampaignBonusInfo type with union bonus_type
- Fix VerifyEmail: ref guard against StrictMode double-fire, setTimeout cleanup
- Fix AdminCampaignStats: setTimeout refs with cleanup, i18n-aware locale
- Wrap all localStorage access in try/catch (Safari private browsing safety)
- Add campaignBonus translations (ru/en/fa/zh)
- Branding cache moved from localStorage to sessionStorage so it clears
when the mini-app is closed, preventing stale names after admin updates
- Added initialDataUpdatedAt: 0 to all branding queries so fresh data is
always fetched on page refresh while showing cached data instantly
- One-time migration moves existing localStorage value to sessionStorage
- Aurora component now checks WebGL availability upfront, skipping all
hooks and subscriptions when WebGL is not supported
- Add flex-wrap to color picker chips so they wrap on narrow cards
- Add min-w-0/truncate to header text, shrink-0 to badge+toggle group
- Reduce chip padding for better fit in 2-column grid
- Add overflow-hidden to card container as safety net
- Extract normalizeConfig() for consistent response normalization
- Add onError handlers to mutations with useNotify
- Add window.confirm before destructive reset action
- Add resetConfirm translation key to all locales
- Add Toggle per section card to show/hide button in main menu
- Add collapsible locale label inputs (ru, en, ua, zh, fa) per section
- Normalize server response for backward compatibility with older API
- Update all 4 locale files with new translation keys
Adds a 'default' style option that removes color from buttons, showing
Telegram's native default style. Updates type, UI chip, preview, and
translations for all 4 locales.
Visual editor in admin settings for configuring Telegram button colors
and custom emoji IDs per menu section. Includes API client, draft state
management, save/reset, and translations for ru/en/fa/zh locales.
OGL renderer.setSize() sets both the internal buffer and CSS dimensions,
so the 10% resolution canvas was only covering 10% of the screen.
Now explicitly reset canvas.style.width/height to 100% after setSize()
so the small buffer is upscaled by the browser to full viewport.
- Render WebGL canvas at 10% viewport resolution (~20K px vs 2M px)
- Replace backdrop-filter: blur(80px) with filter: blur(20px) on canvas
(backdrop-filter composites ALL layers underneath — 10-20x more expensive)
- Disable antialiasing (useless when output is blurred)
- Lower target FPS from 30 to 20 (imperceptible for ambient background)
- Add CSS contain: strict to isolate layout/paint recalculations
- Remove separate blur overlay div (one element instead of two)
Reported: GTX 1660S showed 30-55% GPU load with cabinet open.
Expected: ~1-5% GPU after this change.
Logo images were exposing the backend API URL in the DOM via <img src>.
Now preloadLogo() fetches the image as a blob and serves it through
URL.createObjectURL(), so only blob: URLs appear in the page source.
Blob is invalidated on logo upload/delete.
Replace generic placeholder icons with designs based on actual favicon
assets from payment provider websites. Updated icons for Platega,
Tribute, WATA, CloudPayments, Kassa AI, YooKassa, CryptoBot, Heleket,
MulenPay, PAL24, and Freekassa with correct brand colors and marks.
savedColorsRef was unconditionally updated in the serverColors sync
effect, including when queryClient.setQueryData was called from
updateDraftColor for live preview. This made hasUnsavedChanges always
false, hiding the save/cancel buttons. Move savedColorsRef update
inside the sync condition so it only updates when no local edits exist.
- Add OAuthProvider type and extend User.auth_type union
- Add OAuth API methods (providers, authorize, callback)
- Add loginWithOAuth to auth store
- Create OAuthCallback page with state validation
- Create OAuthProviderIcon component with brand SVGs
- Add OAuth buttons to Login page between Telegram and Email
- Add OAuth callback route to App.tsx
- Add translations for ru, en, zh, fa
Save and Cancel buttons were hidden inside the collapsible "Custom Colors"
section, so manual color changes were lost on navigation. Moved them to
the top level so they are always visible when there are unsaved changes.
- Solid opaque background with colored left border instead of translucent gradient
- Max 3 visible toasts with oldest eviction
- Telegram safe area support via env(safe-area-inset-*)
- Dismiss by clicking anywhere on toast, removed X button
- Adaptive width: full-width on mobile, fixed on desktop
- AdminEmailTemplates: removed local toast, migrated to useNotify
- AdminUsers: migrated from useToast to useNotify
- Switch desktop header from flex justify-between to CSS grid (auto/1fr/auto)
so center nav stays fixed regardless of right-side button count
- Update theme toggle and logout buttons to match bell/language style
(rounded-xl border bg-dark-800/50)
- ThemeTab: auto-save presets to server on apply so they persist across navigation
- PageLoader: add bg-dark-950 to prevent transparent flash during lazy-load transitions
- FortuneWheel: replace React state-based LED animation (10 re-renders/sec) with pure CSS @keyframes
- Add @kastov/cryptohapp + jsencrypt for encrypted Happ deep links
- Add templateEngine utility to resolve {{SUBSCRIPTION_LINK}},
{{HAPP_CRYPT3_LINK}}, {{HAPP_CRYPT4_LINK}}, {{USERNAME}} templates
- Convert ConnectionModal component into a standalone Connection page
- Add /connection route with ProtectedRoute and lazy loading
- Replace modal invocation in Dashboard and Subscription with
navigate('/connection')
- Add type and svgIconKey optional fields to RemnawaveButton
- Show button type badge and SVG icon in AdminApps BlockCard
- Refactor ThemeTab to use local draft state with Save/Cancel flow
Split the monolithic useEffect into two: one for WebGL init (depends
only on isEnabled) and one for updating color uniforms (depends on
theme colors). Previously the entire WebGL context was torn down and
rebuilt on every color change, causing updates to not apply until
page refresh or navigation.
- Remove native <input type="color">, eyedropper button, colorInputRef,
isTelegram check and isInTelegramWebApp import from ColorPicker
- Remove scroll event listener so picker only closes on click/tap outside
- Replace native color input in AdminWheel InlinePrizeForm with ColorPicker
CSS wave-blobs now use accent-800 instead of accent-500 for a subdued
background glow. Aurora WebGL shader receives a darkened accent (45%
brightness) so bright accent colors like #e85002 no longer produce
overly intense background blobs while button colors stay unchanged.
- Add /balance/top-up route for payment method selection
- Add /balance/top-up/:methodId route for amount entry and payment
- Remove TopUpModal component (619 lines)
- Simplify InsufficientBalancePrompt to use navigate instead of modals
- Support ?amount and ?returnTo query params for cross-page data flow
- Fix nav isActive to highlight Balance on sub-routes
- Remove unused MainButton platform abstraction
- Increase toast accent opacity for better visibility
Instead of generating color stops from accent at fixed intensities,
use the actual background, surface and accent colors from theme settings.
This ensures Aurora properly adapts to both dark and light themes.
Add sun/moon toggle button to the desktop navigation bar so theme
switching is available in web version. Sync theme state across multiple
useTheme() instances via custom DOM event to avoid requiring page reload.