Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SOK-31] Service Worker #19

Merged
merged 4 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/client/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/favicon.png" />
<link rel="manifest" href="/manifest.json">

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
Expand Down
15 changes: 15 additions & 0 deletions packages/client/public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"short_name": "FTanks",
"name": "Falcon Tanks",
"icons": [
{
"src": "./public/favicon.png",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
55 changes: 55 additions & 0 deletions packages/client/public/serviceWorker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
const STATIC_CACHE_NAME = 'static-data-v1'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Не понимаю смысла разделения тут кеша на два. Если направишь меня, буду благодарен.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Честно, я тоже не до конца понимаю. Я изначально подумал, что тут просто несколько стратегий реализуется, и для каждой стратегии свой кеш.

Думаю, что в данном случае, это просто доп. усложнение, так как придётся в случае сброса кеша, обновлять две переменные, а не одну.

const DYNAMIC_CACHE_NAME = 'dynamic-data-v1'
const urlsToCache = [
'/'
]

const networkFirst = async (request) => {
const cache = await caches.open(DYNAMIC_CACHE_NAME)
try {
const response = await fetch(request)
if (response && response.status === 200 && response.type === 'basic') {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Возможно стоит вынести в переменную isResponseSuccess, так как если добавиться хотя бы еще одно условие, может быть слишком много условий в if-е.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Поддерживаю. Доп. переменные — это всегда хорошо, потому что код становится более читаемым.

await cache.put(request, response.clone())
}
return response
} catch (error) {
console.error('Fetch failed; returning cached page instead.', error)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Возможно не стоит это указывать в error. Ведь это поведение мы отлавливаем. И ожидаем, что такой сценарий может быть.

Возможно стоит warning или info использовать. Но не уверен.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут на ваше усмотрение. Консоль только разработчики открывают. Если так удобно, то окей.

В реальном же мире, на реальном продукте, тут будет отправка запроса в аналитику (раз интернета нет, то сохранение где-то в localStorage, а отправка уже при появлении инета), чтобы аналитики, да и разработчики могли отслеживать графики того, как часто у юзеров отваливается инет

const cachedResponse = await cache.match(request)
if (cachedResponse) {
return cachedResponse
}
return new Response('Network error happened', {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Выглядит так, что где-то тут приложение упадет, если пользователь не залогинен. Нам нужно как-то обработать сценарий, где пользователь попал на главую, приложение закешировалось, затем интернет отвалился. У пользователя должна остаться возможность хотябы попасть на главую.

Верхом будет, если закинуть ему тостер, что "Ваше интернет соединение не стабильно".

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Так смотри. Если после отвала интернета, пользователь заходит в приложение (на главную), то тут сработает сценарий на 19 строке. Он достанет страницу из кеша и отдаст. Так что тут всё хорошо в плане логики.

status: 408,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут можно кинуть 408, но чаще тут кидают 503

headers: { 'Content-Type': 'text/plain' },
})
}
}

self.addEventListener('install', (event) => {
event.waitUntil(
caches
.open(STATIC_CACHE_NAME)
.then((cache) => {
return cache.addAll(urlsToCache)
})
.then(() => self.skipWaiting())
.catch((err) => {
console.error('Cache installation failed:', err)
})
)
})

self.addEventListener('fetch', (event) => {
const { request } = event
const { url, method } = request

if (method !== 'GET') {
return
}

if (!(url.indexOf('http') === 0)) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Можно сразу !== сделать, чем отрицать равенство, через !.

Можно лучше: немного сложная проверка, потому что сложно читать, и легко ошибиться. Давай разбираться. Ты ищешь в урле http. Метод indexOf либо вернёт индекс, либо -1. Ты проверяешь, что http находится не в начале строки. И если это так, то делаешь return. То есть ты не обрабатываешь не http(s) запросы через sw.

Тут понятно. Но можно красивее:
!url.startsWith('http')

return
}

event.respondWith(networkFirst(request))
})
3 changes: 3 additions & 0 deletions packages/client/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { ToastContainer } from 'react-toastify'
import 'react-toastify/dist/ReactToastify.css'
import App from './app/App'
import '@/scss/styles.scss'
import { registerServiceWorker } from './registerServiceWorker'

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
Expand All @@ -15,3 +16,5 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
</Provider>
</React.StrictMode>
)

registerServiceWorker()
24 changes: 24 additions & 0 deletions packages/client/src/registerServiceWorker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export const registerServiceWorker = () => {
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker
.register('./serviceWorker.js')
.then(
registration => {
console.log(
'ServiceWorker registration successful with scope: ',
registration.scope
)
},
err => {
throw new Error(`ServiceWorker registration failed: ${err}`)
}
)
.catch(err => {
throw new Error(err)
})
})
} else {
throw new Error('service worker is not supported')
}
}
9 changes: 9 additions & 0 deletions packages/client/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,13 @@ export default defineConfig({
},
plugins: [react()],
envDir: '../../',
build: {
outDir: 'dist',
rollupOptions: {
input: {
main: path.resolve(__dirname, 'index.html'),
serviceWorker: path.resolve(__dirname, 'public/serviceWorker.js'),
},
},
},
})
Loading