diff --git a/packages/client/package.json b/packages/client/package.json index 9057f78..c821864 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -19,7 +19,6 @@ "@phosphor-icons/react": "2.0.10", "@reduxjs/toolkit": "1.9.5", "axios": "1.4.0", - "classnames": "^2.3.2", "clsx": "1.2.1", "dotenv": "16.0.2", "react": "18.2.0", @@ -60,7 +59,8 @@ "stylelint-config-standard-scss": "9.0.0", "ts-jest": "28.0.8", "typescript": "5.1.3", - "vite": "3.0.7" + "vite": "3.0.7", + "vite-plugin-pwa": "0.16.4" }, "license": "MIT" } diff --git a/packages/client/src/components/design-system/SVG/AvatarSVG/AvatarSVG.tsx b/packages/client/src/components/design-system/SVG/AvatarSVG/AvatarSVG.tsx index eea42cb..2b6a42e 100644 --- a/packages/client/src/components/design-system/SVG/AvatarSVG/AvatarSVG.tsx +++ b/packages/client/src/components/design-system/SVG/AvatarSVG/AvatarSVG.tsx @@ -3,7 +3,7 @@ import { SVGType } from '@typings/SVG'; import { useGetCSSVars } from '@lib/useGetCSSVars'; export const AvatarSVG: React.FC = ({ width, color }) => { - const currentColor = useGetCSSVars('color', color); + const currentColor = useGetCSSVars('color', color || ''); return ( <> diff --git a/packages/client/src/fuature/forum/components/CreateForm/CreateForm.tsx b/packages/client/src/fuature/forum/components/CreateForm/CreateForm.tsx index 34a3645..28b5d24 100644 --- a/packages/client/src/fuature/forum/components/CreateForm/CreateForm.tsx +++ b/packages/client/src/fuature/forum/components/CreateForm/CreateForm.tsx @@ -4,38 +4,28 @@ import { Button } from '@components/design-system'; import { BodyNormal } from '@components/design-system/Fonts'; export const ForumCreateForm: React.FC = () => { - - const [topic, setTopic] = useState(""); + const [topic, setTopic] = useState(''); const [description, setDesc] = useState(''); const createTopic = (e: React.ChangeEvent) => { e.preventDefault(); - setTopic(""); + setTopic(''); createDescription(e); - } + }; const createDescription = (e: React.ChangeEvent) => { e.preventDefault(); - setDesc(""); - } + setDesc(''); + }; return ( -
-
+
+
- setTopic(e.target.value) } - /> + setTopic(e.target.value)} /> - - +
+ Оставить комментарий + + +
); -} +}; diff --git a/packages/client/src/fuature/forum/components/fullscreen/FullScreen.tsx b/packages/client/src/fuature/forum/components/fullscreen/FullScreen.tsx index 7357539..3ac4604 100644 --- a/packages/client/src/fuature/forum/components/fullscreen/FullScreen.tsx +++ b/packages/client/src/fuature/forum/components/fullscreen/FullScreen.tsx @@ -1,30 +1,35 @@ -import React, { useState } from 'react' -import styles from './FullScreen.module.scss' -import classNames from 'classnames' +import React, { useState } from 'react'; +import clsx from 'clsx'; +import styles from './FullScreen.module.scss'; export const FullScreen: React.FC = () => { - const [isFullscreeen, setFullscreen] = useState(false) + const [isFullscreeen, setFullscreen] = useState(false); async function eventHandler(event: React.MouseEvent) { if (document.fullscreenElement) { try { - await document.exitFullscreen() - setFullscreen(false) + await document.exitFullscreen(); + setFullscreen(false); } catch (err) { - throw new Error() + throw new Error(); } } else { try { - await document.documentElement.requestFullscreen() - setFullscreen(true) + await document.documentElement.requestFullscreen(); + setFullscreen(true); } catch (err) { - throw new Error() + throw new Error(); } } } return ( - - ) -} + + ); +}; diff --git a/packages/client/src/fuature/forum/components/topics/TopicElements/TopicElements.tsx b/packages/client/src/fuature/forum/components/topics/TopicElements/TopicElements.tsx index 82065f6..2d73aec 100644 --- a/packages/client/src/fuature/forum/components/topics/TopicElements/TopicElements.tsx +++ b/packages/client/src/fuature/forum/components/topics/TopicElements/TopicElements.tsx @@ -1,11 +1,11 @@ import React from 'react'; -import { TopicsItem } from '../TopicItem' +import { TopicsItem } from '../TopicItem'; export const TopicElements = () => { const topicsData = [ - { id:1, title: 'Какие игры сейчас популярны', desc: 'Dota, CS, CS:GO', timestamp: '10 янв 2023' }, - { id:2, title: 'Го играть', desc: 'Я бы сыграл в денди', timestamp: '30 июня 2023' }, - { id:3, title: 'Я доделал форум)', desc: 'Почти в дед лайн =)', timestamp: '01 июля 2023' }, + { id: '1', title: 'Какие игры сейчас популярны', desc: 'Dota, CS, CS:GO', timestamp: '10 янв 2023' }, + { id: '2', title: 'Го играть', desc: 'Я бы сыграл в денди', timestamp: '30 июня 2023' }, + { id: '3', title: 'Я доделал форум)', desc: 'Почти в дед лайн =)', timestamp: '01 июля 2023' }, ]; - return topicsData.map( element => ) + return topicsData.map((element) => ); }; diff --git a/packages/client/src/index.tsx b/packages/client/src/index.tsx index 251abe0..b32cc4f 100644 --- a/packages/client/src/index.tsx +++ b/packages/client/src/index.tsx @@ -2,5 +2,8 @@ import './styles/index.scss'; import App from './App'; import ReactDOM from 'react-dom/client'; +import { startServiceWorker } from '@utils/sw/startServiceWorker'; ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(); + +startServiceWorker(); diff --git a/packages/client/src/utils/sw/startServiceWorker.ts b/packages/client/src/utils/sw/startServiceWorker.ts new file mode 100644 index 0000000..61fe3d8 --- /dev/null +++ b/packages/client/src/utils/sw/startServiceWorker.ts @@ -0,0 +1,15 @@ +export function startServiceWorker() { + if ('serviceWorker' in navigator) { + window.addEventListener('load', async () => { + const sw = '/src/utils/sw/sw.ts'; + + try { + const registration = await navigator.serviceWorker.register(sw); + + console.log('ServiceWorker registration successful with scope: ', registration.scope); + } catch (error) { + console.log('ServiceWorker registration failed: ', error); + } + }); + } +} diff --git a/packages/client/src/utils/sw/sw.ts b/packages/client/src/utils/sw/sw.ts new file mode 100644 index 0000000..40cf706 --- /dev/null +++ b/packages/client/src/utils/sw/sw.ts @@ -0,0 +1,108 @@ +/// + +const sw = self as unknown as ServiceWorkerGlobalScope & typeof globalThis; + +const staticCacheName = 'static-cache-v1'; +const dynamicCacheName = 'dynamic-cache-v1'; + +const assetUrls = [ + '/', + '/index.html', + '/src/index.tsx', + '/src/styles/index.scss', + '/src/styles/images/casper.png', + '/src/styles/images/add_msg.png', + '/src/styles/images/auth.png', + '/src/styles/images/error.png', + '/src/styles/images/forum-left-side.svg', + '/src/styles/images/forum.svg', + '/src/styles/images/fullscreen-close.png', + '/src/styles/images/fullscreen-open.png', + '/src/styles/images/fullscreen.png', + '/src/styles/images/Hipe.svg', + '/src/styles/images/leader.svg', + '/src/styles/images/leaders.svg', + '/src/styles/images/leaderVector.png', + '/src/styles/images/like.png', + '/src/styles/images/profile.svg', + '/src/styles/images/progress-bg-pic.svg', + '/src/styles/images/progress-pic.svg', + '/src/styles/images/progress.svg', + '/src/styles/images/start.svg', + '/src/styles/fonts/HeliosExt-Bold.ttf', + '/src/styles/fonts/HeliosExt.ttf', + '/src/styles/fonts/Roboto-Bold.eot', + '/src/styles/fonts/Roboto-Bold.ttf', + '/src/styles/fonts/Roboto-Bold.woff', + '/src/styles/fonts/Roboto-Bold.woff2', + '/src/styles/fonts/Roboto-Regular.eot', + '/src/styles/fonts/Roboto-Regular.ttf', + '/src/styles/fonts/Roboto-Regular.woff', + '/src/styles/fonts/Roboto-Regular.woff2', +]; + +sw.addEventListener('install', (event) => { + event.waitUntil(addResourcesToCache(assetUrls)); +}); + +sw.addEventListener('activate', async (event) => { + try { + const cacheNames = await caches.keys(); + + await Promise.all( + cacheNames + .filter((name) => name !== staticCacheName) + .filter((name) => name !== dynamicCacheName) + .map((name) => caches.delete(name)) + ); + } catch (error) { + console.log(error); + } +}); + +sw.addEventListener('fetch', (event) => { + const { request } = event; + + const url = new URL(request.url); + + if (url.origin === location.origin) { + event.respondWith(cacheFirst(request)); + return; + } + + // @ts-expect-error - response might be undefined + event.respondWith(networkFirst(request)); +}); + +async function addResourcesToCache(resources: string[]) { + try { + const cache = await caches.open(staticCacheName); + await cache.addAll(resources); + } catch (error) { + console.log(error); + } +} + +async function cacheFirst(request: Request) { + const cached = await caches.match(request); + + return cached ?? (await fetch(request)); +} + +async function networkFirst(request: Request) { + const cache = await caches.open(dynamicCacheName); + + try { + const response = await fetch(request); + + await cache.put(request, response.clone()); + + return response; + } catch (error) { + console.log(error); + + const cached = await caches.match(request); + + return cached; + } +} diff --git a/packages/client/vite.config.ts b/packages/client/vite.config.ts index bcfc171..a534cd9 100644 --- a/packages/client/vite.config.ts +++ b/packages/client/vite.config.ts @@ -2,6 +2,7 @@ import { defineConfig } from 'vite'; import dotenv from 'dotenv'; import react from '@vitejs/plugin-react'; import viteTsconfigPaths from 'vite-tsconfig-paths'; +import { VitePWA } from 'vite-plugin-pwa'; dotenv.config(); // https://vitejs.dev/config/ @@ -17,6 +18,18 @@ export default defineConfig({ react({ jsxRuntime: 'classic', // Add this line }), + VitePWA({ registerType: 'autoUpdate' }), viteTsconfigPaths(), ], + build: { + rollupOptions: { + input: { + app: './index.html', + 'service-worker': './src/utils/sw/sw.ts', + }, + output: { + entryFileNames: (asset) => (asset.name === 'service-worker' ? '[name].js' : 'assets/[name].[hash].js'), + }, + }, + }, });