Skip to content

Commit

Permalink
fix: service worker, burger menu, leaderboard filter bugs (#109)
Browse files Browse the repository at this point in the history
  • Loading branch information
sapomaro authored Jul 23, 2023
1 parent 93df577 commit 7f973c8
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 43 deletions.
2 changes: 1 addition & 1 deletion packages/client/src/app.typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@ export type HandledMatch = {
};

export function hasKey<O extends object>(obj: O, key: PropertyKey): key is keyof O {
return key in obj && key !== 'username';
return key in obj;
}
13 changes: 11 additions & 2 deletions packages/client/src/components/BurgerMenu/BurgerMenu.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
.menu {
overflow-y: hidden;
font-family: "Press Start 2P", cursive;
display: flex;
flex-direction: column;
Expand All @@ -21,6 +20,16 @@
left: 20px;
width: 80px;

@media (max-width: 640px) {
left: 5px;
transform: scale(0.7);
}

@media (max-width: 430px) {
left: 0;
transform: scale(0.5);
}

&-line {
transition: 0.2s ease;
background-color: var(--burger-menu-lines-color);
Expand All @@ -36,7 +45,7 @@
}

&:nth-child(2) {
transform: translate(-200%);
transform: translate(-300%);
transition: ease-out 0.5s;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/components/Logo/Logo.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
font-size: 5.5rem;
font-family: "Press Start 2P", cursive;
text-align: center;
margin: 50px 0 var(--vertical-margin);
margin: 75px 0 var(--vertical-margin);
color: var(--accent-color);
-webkit-text-fill-color: transparent;
background: url("/assets/img/bricks.png");
Expand Down
11 changes: 9 additions & 2 deletions packages/client/src/components/MenuLink/MenuLink.css
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,17 @@
font-size: 1.6rem;
position: relative;

@media (max-width: 430px) {
@media (max-width: 640px) {
margin: 32px 0;
font-size: 1.2rem;
max-width: 80%;
line-height: 1.5rem;
line-height: 1.4rem;
}

@media (max-width: 430px) {
margin: 24px 0;
font-size: 1rem;
line-height: 1.2rem;
}
}

Expand Down
4 changes: 4 additions & 0 deletions packages/client/src/components/ThemeToggle/ThemeToggle.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
display: flex;
margin-bottom: 70px;
justify-content: flex-end;

@media (max-width: 640px) {
margin-bottom: 50px;
}
}

&__icon {
Expand Down
137 changes: 100 additions & 37 deletions packages/client/src/serviceWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,86 @@ const serviceWorker = self as unknown as ServiceWorkerGlobalScope; // чтобы

const REPORTING = false;
const CACHE_NAME = 'tanchiki-cache-1';
const PRECACHE_URLS = ['/', '/index.html'];
const CACHE_CONTENT_TYPES = ['document', 'script', 'style', 'font', 'image', 'audio', 'manifest'];
// Импорты в SW не работают, поэтому список игровых ресурсов нельзя забрать из game/services/Resources
// Теперь игра грузится быстрее
const GAME_ASSETS = [
'/assets/img/bricks.png',
'/assets/img/bricks_modern.png',
'/assets/img/sprite.png',
'/assets/img/sprite_modern.png',
'/assets/img/tarmac_background.png',
'/assets/sounds/level-intro.mp3',
'/assets/sounds/game-over.mp3',
'/assets/sounds/pause.mp3',
'/assets/sounds/tank-move.mp3',
'/assets/sounds/tank-idle.mp3',
'/assets/sounds/ice.mp3',
'/assets/sounds/shoot.mp3',
'/assets/sounds/hit-enemy.mp3',
'/assets/sounds/hit-brick.mp3',
'/assets/sounds/hit-steel.mp3',
'/assets/sounds/player-explosion.mp3',
'/assets/sounds/enemy-explosion.mp3',
'/assets/sounds/powerup-appear.mp3',
'/assets/sounds/powerup-pickup.mp3',
'/assets/sounds/life.mp3',
'/assets/sounds/score.mp3',
];
const PRECACHE_URLS = ['/', '/index.html', '/game', '/about', '/leaderboard', '/profile', ...GAME_ASSETS];
const CACHE_CONTENT_TYPES = ['script', 'style', 'font', 'image', 'audio', 'manifest'];
const FALLBACK_BODY = `
<h1>Интернеты упали! Но это неточно.</h1>
<h2>Надо бы обновить страницу...</h2>
<h1>Интернеты упали! Но это неточно...</h1>
<h2>Обнови страницу или вернись на главную</h2>
`;
const FALLBACK_HEADERS = { headers: { 'Content-Type': 'text/html; charset=utf-8' } };
// Таймауты запросов в зависимости от наличия кеша
const FETCH_CACHED_TIMEOUT = 5000;
const FETCH_NETWORK_TIMEOUT = 15000;

/** Для логирования. */
function logStatus<T>(msg: string, obj: T | null = null) {
if (REPORTING) {
console.log(msg, obj);
}
}

/** Проверяет нужно ли кешировать запрос. */
function shouldUseCache(req: Request) {
// Чтобы не проходили запросы типа chrome-extension://
if (!req.url.match(/^http/)) {
return false;
}

// Запросы к API не кешируются
if (req.url.includes('/api/')) {
return false;
}

return true;
}

/**
* Проверяет, нужно ли отдавать кеш сразу или попробовать дождаться ответа из сети.
* Если запрос к HTML-странице, то лучше получить её актуальную версию
* (т.к. нам не нужен кеш страницы с устаревшим preloaded_state, сгенерированным SSR)
* */
function shouldServeCacheInstantly(req: Request) {
// Проверка на тип контента (upd: некоторые браузеры не считают mp3 за audio)
if (CACHE_CONTENT_TYPES.includes(req.destination)) {
return true;
}

// Запросы к страницам (upd: некоторые браузеры не относят html к типу document)
if (req.destination === 'document' || req.url[req.url.length - 1] === '/' || req.url.match(/\/[A-Za-z0-9_-]+$/)) {
return false;
}

return true;
}

// При установке воркера кешируем часть данных (статику)
serviceWorker.addEventListener('install', (event: ExtendableEvent) => {
REPORTING && console.log('SW: installing', event);
logStatus('SW: installing', event);

event.waitUntil(
caches
Expand All @@ -22,14 +91,14 @@ serviceWorker.addEventListener('install', (event: ExtendableEvent) => {
// `skipWaiting()` для активации SW сразу, а не после перезагрузки страницы
.then(() => {
serviceWorker.skipWaiting();
REPORTING && console.log('SW: cache added & skipped waiting');
logStatus('SW: cache added & skipped waiting');
})
);
});

// Активация происходит только после того, как предыдущая версия SW была удалена из браузера
serviceWorker.addEventListener('activate', (event: ExtendableEvent) => {
REPORTING && console.log('SW: activating', event);
logStatus('SW: activating', event);

event.waitUntil(
// `clients.claim()` позволяет SW начать перехватывать запросы с самого начала,
Expand All @@ -38,61 +107,55 @@ serviceWorker.addEventListener('activate', (event: ExtendableEvent) => {
);
});

// Стратегия `stale-while-revalidate`
// Стратегия `stale-while-revalidate` (сначала отдаём кеш, а если есть свежее из сети - обновляем кеш и досылаем)
serviceWorker.addEventListener('fetch', (event: FetchEvent) => {
REPORTING && console.log('SW: fetch', event.request.destination, event);

// Проверка на тип контента
if (!CACHE_CONTENT_TYPES.includes(event.request.destination)) {
if (!shouldUseCache(event.request)) {
return;
}

// Чтобы не проходили запросы типа chrome-extension://
if (!event.request.url.match(/^http/)) {
return;
}
logStatus('SW: fetching', event.request.url);

event.respondWith(
caches
.open(CACHE_NAME)
.then(cache => {
return cache.match(event.request).then(cachedResponse => {
// Делаем запрос для обновления кеша
const fetchedResponse = fetch(event.request)
.then(cache =>
cache.match(event.request).then(cachedResponse => {
// Делаем запрос для обновления кеша с таймаутом
const abortController = new AbortController();
const abortTimeout = setTimeout(
() => abortController.abort(),
cachedResponse ? FETCH_CACHED_TIMEOUT : FETCH_NETWORK_TIMEOUT
);
const fetchedResponse = fetch(event.request, { signal: abortController.signal })
.then(networkResponse => {
clearTimeout(abortTimeout);
// Кладём ответ в кеш, если он содержит что-то субстантивное
if (networkResponse.status === 200) {
if (networkResponse.status >= 200 && networkResponse.status < 300) {
cache.put(event.request, networkResponse.clone()).catch(cacheError => {
REPORTING && console.warn('SW: cache put error', cacheError);
logStatus('SW: cache put error', cacheError);
});
}
return networkResponse;
})
.catch(fetchedError => {
REPORTING && console.warn('SW: network problem', fetchedError);
clearTimeout(abortTimeout);
logStatus('SW: network problem', fetchedError);
return cachedResponse ?? new Response(FALLBACK_BODY, FALLBACK_HEADERS);
});

/**
* В каких случаях сразу не отдаём ресурсы из кеша:
* - если запрос к HTML-странице, то ждём актуальную версию
* (т.к. нам не нужен кеш страницы с устаревшим preloaded_state, сгенерированным SSR)
*/
const shouldNotUseCache = event.request.destination === 'document';

// Если есть кеш, возвращаем его, не дожидаясь ответа из сети
if (cachedResponse && !shouldNotUseCache) {
REPORTING && console.log('SW: return cached response', event.request.url);
// Если есть кеш, возвращаем его, не дожидаясь ответа из сети (кроме определённых случаев)
if (cachedResponse && shouldServeCacheInstantly(event.request)) {
logStatus('SW: return cached response', event.request.url);
return cachedResponse;
}

// Если нет кеша, ждём и возвращаем ответ из сети
REPORTING && console.log('SW: return network response', event.request.url);
logStatus('SW: return network response', event.request.url);
return fetchedResponse;
});
})
})
)
.catch(cacheError => {
REPORTING && console.warn('SW: cache open error', cacheError);
logStatus('SW: cache open error', cacheError);
return new Response(FALLBACK_BODY, FALLBACK_HEADERS);
})
);
Expand Down

0 comments on commit 7f973c8

Please sign in to comment.