ci / module boundaries / boundaries (push) Successful in 7s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m2s
ci / ts / ts (packages + web) (push) Successful in 3m8s
ci / desktop / rust (desktop proxy) (push) Successful in 4m21s
release / release (версия и заметки) (push) Successful in 6s
release / android (APK в релиз) (push) Successful in 4m0s
release / desktop (Linux и Windows в релиз) (push) Successful in 11m49s
release / prune (старые файлы и прогоны) (push) Successful in 5s
Clean release notes in Android and desktop update dialogs. Persist downloaded audio and offline playback events per server and user; add desktop update checks, checksum verification and installer launch.
88 lines
2.5 KiB
TypeScript
88 lines
2.5 KiB
TypeScript
import './styles.css';
|
|
|
|
import { AuthSession } from '@pipu/core';
|
|
import { createI18n, pickLocale } from '@pipu/i18n';
|
|
import { StrictMode } from 'react';
|
|
import { createRoot } from 'react-dom/client';
|
|
import { I18nextProvider } from 'react-i18next';
|
|
|
|
import { App } from './App';
|
|
import { createAppRouter } from './app/router';
|
|
import { applyTheme } from './app/session';
|
|
import { createDesktop, DesktopProvider, desktopFetch } from './desktop';
|
|
|
|
const LOCALE_KEY = 'pipu.locale';
|
|
|
|
function storedLocale(): string[] {
|
|
try {
|
|
const v = localStorage.getItem(LOCALE_KEY);
|
|
return v ? [v] : [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/** «Chrome / Windows» — подпись сессии в списке устройств. */
|
|
function deviceName(): string {
|
|
const ua = navigator.userAgent;
|
|
const browser = /Edg\//.test(ua)
|
|
? 'Edge'
|
|
: /Firefox\//.test(ua)
|
|
? 'Firefox'
|
|
: /Chrome\//.test(ua)
|
|
? 'Chrome'
|
|
: /Safari\//.test(ua)
|
|
? 'Safari'
|
|
: 'Browser';
|
|
const os = /Windows/.test(ua)
|
|
? 'Windows'
|
|
: /Android/.test(ua)
|
|
? 'Android'
|
|
: /iPhone|iPad/.test(ua)
|
|
? 'iOS'
|
|
: /Mac OS X/.test(ua)
|
|
? 'macOS'
|
|
: /Linux/.test(ua)
|
|
? 'Linux'
|
|
: '';
|
|
return os ? `${browser} / ${os}` : browser;
|
|
}
|
|
|
|
applyTheme(undefined);
|
|
if (import.meta.env.VITE_E2E === '1') {
|
|
const { installE2EFetch } = await import('./test/e2eBridge');
|
|
installE2EFetch();
|
|
}
|
|
const i18n = await createI18n(pickLocale([...storedLocale(), ...navigator.languages]));
|
|
document.documentElement.lang = i18n.language;
|
|
i18n.on('languageChanged', (l) => {
|
|
document.documentElement.lang = l;
|
|
try {
|
|
localStorage.setItem(LOCALE_KEY, l);
|
|
} catch {
|
|
// недоступное хранилище не мешает работе
|
|
}
|
|
});
|
|
|
|
const desktop = await createDesktop();
|
|
const session = new AuthSession({
|
|
clientType: desktop ? 'desktop' : 'web',
|
|
deviceName: desktop ? 'Pipu / Desktop' : deviceName(),
|
|
baseUrl: desktop?.proxyBase,
|
|
refreshStore: desktop?.refreshStore,
|
|
fetch: desktop ? desktopFetch : undefined,
|
|
});
|
|
// До первого рендера узнаём, есть ли сессия (refresh-cookie), — guards роутера решают сразу.
|
|
await session.restore();
|
|
const router = createAppRouter(session, { desktop });
|
|
|
|
createRoot(document.getElementById('root')!).render(
|
|
<StrictMode>
|
|
<I18nextProvider i18n={i18n}>
|
|
<DesktopProvider value={desktop}>
|
|
<App session={session} router={router} />
|
|
</DesktopProvider>
|
|
</I18nextProvider>
|
|
</StrictMode>,
|
|
);
|