From 626528f808b4d93f76c63184921fd202b36f7789 Mon Sep 17 00:00:00 2001 From: Jihoo Kim Date: Thu, 24 Aug 2023 15:44:26 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=98=A4=ED=94=84=EB=9D=BC=EC=9D=B8?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20asset=20=EC=BA=90=EC=8B=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 12 --- package.json | 3 + public/manifest.json | 46 ++++++++ public/robots.txt | 5 +- readme.md | 2 +- src/app/App.tsx | 7 +- .../system/AlertOffline/AlertOffline.tsx | 15 +++ .../components/system/AlertOffline/index.ts | 1 + src/common/components/system/index.ts | 1 + src/common/hooks/useUpdate.tsx | 13 ++- src/my-sw.js | 9 -- src/pages/edit-profile/EditProfilePage.tsx | 100 +++++++++--------- src/pages/home/HomePage.tsx | 17 +-- src/pages/unregister/UnregisterPage.tsx | 55 +++++----- src/pages/user/UserPage.tsx | 57 +++++----- src/pages/write/WritePage.tsx | 14 +-- src/sw.ts | 13 +++ tsconfig.json | 3 +- vite.config.js | 52 ++------- yarn.lock | 14 ++- 20 files changed, 246 insertions(+), 193 deletions(-) create mode 100644 public/manifest.json create mode 100644 src/common/components/system/AlertOffline/AlertOffline.tsx create mode 100644 src/common/components/system/AlertOffline/index.ts delete mode 100644 src/my-sw.js create mode 100644 src/sw.ts diff --git a/index.html b/index.html index f928bd95..f7b197cb 100644 --- a/index.html +++ b/index.html @@ -137,16 +137,4 @@
- diff --git a/package.json b/package.json index 19f1ba97..ec76450a 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "lodash": "^4.17.21", "pulltorefreshjs": "^0.1.22", "react": "^18.2.0", + "react-detect-offline": "^2.4.5", "react-device-detect": "^2.2.3", "react-dom": "^18.2.0", "react-error-boundary": "^4.0.11", @@ -74,6 +75,7 @@ "@types/node": "^20.4.8", "@types/pulltorefreshjs": "^0.1.5", "@types/react": "^18.2.18", + "@types/react-detect-offline": "^2.4.1", "@types/react-dom": "^18.2.7", "@types/react-lazy-load-image-component": "^1.5.3", "@types/testing-library__jest-dom": "^5.14.9", @@ -99,6 +101,7 @@ "vite-plugin-pwa": "^0.16.4", "vite-plugin-svgr": "^3.2.0", "vitest": "^0.34.1", + "workbox-core": "^7.0.0", "workbox-precaching": "^7.0.0", "workbox-window": "^7.0.0" }, diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 00000000..5cefa261 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,46 @@ +{ + "short_name": "고민의 참견", + "name": "고민의 참견, 고참", + "icons": [ + { + "src": "icon/android/icon-36.png", + "sizes": "36x36", + "type": "image/png" + }, + { + "src": "icon/android/icon-48.png", + "sizes": "48x48", + "type": "image/png" + }, + { + "src": "icon/android/icon-72.png", + "sizes": "72x72", + "type": "image/png" + }, + { + "src": "icon/android/icon-96.png", + "sizes": "96x96", + "type": "image/png" + }, + { + "src": "icon/android/icon-144.png", + "sizes": "144x144", + "type": "image/png" + }, + { + "src": "icon/android/icon-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "icon/android/icon-512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "start_url": "./index.html", + "display": "standalone", + "theme_color": "#F5F7FB", + "background_color": "#ffffff" +} diff --git a/public/robots.txt b/public/robots.txt index 8a3ca312..c2a49f4f 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,3 +1,2 @@ -# https://www.robotstxt.org/robotstxt.html -UserPage-agent: * -Disallow: +User-agent: * +Allow: / diff --git a/readme.md b/readme.md index 6e807eac..655aa175 100644 --- a/readme.md +++ b/readme.md @@ -202,7 +202,7 @@ auth_check: "/auth-check", // HOC적용이 힘든 케이스에 사용할 검증 ┃ ┣ 📜logo.svg # 미사용 ┃ ┣ 📜react-app-env.d.ts # 미사용 ┃ ┣ 📜reportWebVitals.ts # 미사용 -┃ ┣ 📜my-sw.js # PWA를 위한 서비스 워커 +┃ ┣ 📜sw.ts # PWA를 위한 서비스 워커 ┃ ┣ 📜serviceWorkerRegistration.ts # PWA를 위한 서비스 워커 ┃ ┣ 📜setupTests.ts # 미사용 ┃ ┗ 📜version.js # 버전 커밋 diff --git a/src/app/App.tsx b/src/app/App.tsx index 41e4964b..9cc47f89 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -1,5 +1,9 @@ import { RootLayout } from '@/common/components/layout'; -import { IosInstallGuide, UpdateBanner } from '@/common/components/system'; +import { + AlertOffline, + IosInstallGuide, + UpdateBanner, +} from '@/common/components/system'; import { GlobalErrorBoundary } from './GlobalErrorBoundary'; import { Provider } from './Provider'; import Routes from './Routes'; @@ -9,6 +13,7 @@ export function App() { + diff --git a/src/common/components/system/AlertOffline/AlertOffline.tsx b/src/common/components/system/AlertOffline/AlertOffline.tsx new file mode 100644 index 00000000..fb31ccc9 --- /dev/null +++ b/src/common/components/system/AlertOffline/AlertOffline.tsx @@ -0,0 +1,15 @@ +import { Offline } from 'react-detect-offline'; +import { Snackbar } from '@/common/components/ui/modal'; + +export function AlertOffline() { + return ( + + + + ); +} diff --git a/src/common/components/system/AlertOffline/index.ts b/src/common/components/system/AlertOffline/index.ts new file mode 100644 index 00000000..adda7436 --- /dev/null +++ b/src/common/components/system/AlertOffline/index.ts @@ -0,0 +1 @@ +export { AlertOffline } from './AlertOffline'; diff --git a/src/common/components/system/index.ts b/src/common/components/system/index.ts index edd245f8..92decb37 100644 --- a/src/common/components/system/index.ts +++ b/src/common/components/system/index.ts @@ -1,2 +1,3 @@ export * from './IosInstallGuide'; export * from './UpdateBanner'; +export * from './AlertOffline'; diff --git a/src/common/hooks/useUpdate.tsx b/src/common/hooks/useUpdate.tsx index fcbc4844..f1d2acc1 100644 --- a/src/common/hooks/useUpdate.tsx +++ b/src/common/hooks/useUpdate.tsx @@ -1,16 +1,19 @@ import { useState } from 'react'; import { useRegisterSW } from 'virtual:pwa-register/react'; -const UPDATE_INTERVAL = 60 * 60 * 1000; +const UPDATE_INTERVAL = 60 * 60 * 1000; // 1시간 export function useUpdate() { const [showUpdate, setShowUpdate] = useState(false); const { updateServiceWorker } = useRegisterSW({ onRegistered: (r) => { - r && - setInterval(() => { - r.update(); - }, UPDATE_INTERVAL); + if (!r) return; + + r.update(); + setInterval(() => { + console.log('interval'); + r.update(); + }, UPDATE_INTERVAL); }, onNeedRefresh: () => { setShowUpdate(true); diff --git a/src/my-sw.js b/src/my-sw.js deleted file mode 100644 index 4dba519a..00000000 --- a/src/my-sw.js +++ /dev/null @@ -1,9 +0,0 @@ -import { cleanupOutdatedCaches, precacheAndRoute } from 'workbox-precaching'; - -cleanupOutdatedCaches(); - -precacheAndRoute(self.__WB_MANIFEST); - -self.addEventListener('install', () => { - self.skipWaiting(); -}); diff --git a/src/pages/edit-profile/EditProfilePage.tsx b/src/pages/edit-profile/EditProfilePage.tsx index a5b035d9..41b0dad1 100644 --- a/src/pages/edit-profile/EditProfilePage.tsx +++ b/src/pages/edit-profile/EditProfilePage.tsx @@ -1,4 +1,5 @@ import { FormEvent, useEffect, useState } from 'react'; +import { Online } from 'react-detect-offline'; import { useNavigate } from 'react-router-dom'; import { TopAppBar } from '@/common/components/layout'; import { DockedButton } from '@/common/components/ui/buttons'; @@ -104,54 +105,57 @@ export default function EditProfilePage() { return (
-
-
- { - setFormData({ - ...formData, - nickname, - gender, - birthDate, - }); - setIsValid((prevIsValid) => ({ - ...prevIsValid, - nicknameAgeGender: isValid, - })); - }} - /> - cat.value) || [], - job: user.job, - residenceId: user.residence?.value || null, - }} - onChange={({ job, categoryIds, residenceId }, isValid) => { - setFormData({ - ...formData, - job, - categoryIds, - residenceId, - }); - setIsValid((prevIsValid) => ({ - ...prevIsValid, - regionJobCategory: isValid, - })); - }} - /> -
- - 변경 완료 - -
+ +
+
+ { + setFormData({ + ...formData, + nickname, + gender, + birthDate, + }); + setIsValid((prevIsValid) => ({ + ...prevIsValid, + nicknameAgeGender: isValid, + })); + }} + /> + cat.value) || [], + job: user.job, + residenceId: user.residence?.value || null, + }} + onChange={({ job, categoryIds, residenceId }, isValid) => { + setFormData({ + ...formData, + job, + categoryIds, + residenceId, + }); + setIsValid((prevIsValid) => ({ + ...prevIsValid, + regionJobCategory: isValid, + })); + }} + /> +
+ + 변경 완료 + +
+
); } diff --git a/src/pages/home/HomePage.tsx b/src/pages/home/HomePage.tsx index 0de1068b..3fa6ecb4 100644 --- a/src/pages/home/HomePage.tsx +++ b/src/pages/home/HomePage.tsx @@ -1,5 +1,6 @@ import { useQueryClient } from '@tanstack/react-query'; import { Suspense } from 'react'; +import { Online } from 'react-detect-offline'; import { BottomAppBar, PageWrapper } from '@/common/components/layout'; import { Spacing } from '@/common/components/ui'; import { POST_TYPE } from '@/common/constants/post-type'; @@ -44,13 +45,15 @@ export default function HomePage() { > -
- - }> - - - -
+ +
+ + }> + + + +
+
diff --git a/src/pages/unregister/UnregisterPage.tsx b/src/pages/unregister/UnregisterPage.tsx index ac3dc1fc..8924fb7b 100644 --- a/src/pages/unregister/UnregisterPage.tsx +++ b/src/pages/unregister/UnregisterPage.tsx @@ -1,5 +1,6 @@ import { useQueryClient } from '@tanstack/react-query'; import { useEffect, useState } from 'react'; +import { Online } from 'react-detect-offline'; import { useNavigate } from 'react-router-dom'; import { TopAppBar } from '@/common/components/layout'; import { Button } from '@/common/components/ui/buttons'; @@ -74,32 +75,34 @@ export default function UnregisterPage() { return (
-
-

- {user?.nickname}님이 떠나신다니 너무 아쉬워요. -

-

- 계정을 삭제해도 게시글, 댓글, 투표한 기록 등 모든 활동 정보는 그대로 - 유지됩니다. 계정 삭제 후 재가입 할 경우 이전에 활동한 정보를 수정할 수 - 없습니다. -

- -
- + +
+

+ {user?.nickname}님이 떠나신다니 너무 아쉬워요. +

+

+ 계정을 삭제해도 게시글, 댓글, 투표한 기록 등 모든 활동 정보는 그대로 + 유지됩니다. 계정 삭제 후 재가입 할 경우 이전에 활동한 정보를 수정할 + 수 없습니다. +

+ +
+ +
-
- - - - -
- assignMultipleRefs(el, [ - scrollToTopRef, - pullToRefreshRef, - selectedPostType === POST_TYPE.MY - ? myPostsRef - : participatedPostsRef, - ]) - } - className={'hide-scrollbar h-full overflow-y-scroll px-[2.5rem]'} - > - }> - - + +
+ + -
-
+ +
+ assignMultipleRefs(el, [ + scrollToTopRef, + pullToRefreshRef, + selectedPostType === POST_TYPE.MY + ? myPostsRef + : participatedPostsRef, + ]) + } + className={'hide-scrollbar h-full overflow-y-scroll px-[2.5rem]'} + > + }> + + + +
+ + ); diff --git a/src/pages/write/WritePage.tsx b/src/pages/write/WritePage.tsx index e47c8bee..f4944bb7 100644 --- a/src/pages/write/WritePage.tsx +++ b/src/pages/write/WritePage.tsx @@ -1,7 +1,9 @@ import { useSetAtom } from 'jotai'; import { useEffect, useState } from 'react'; +import { Offline, Online } from 'react-detect-offline'; import { useNavigate } from 'react-router-dom'; -import { TopAppBar } from '@/common/components/layout'; +import { PageWrapper, TopAppBar } from '@/common/components/layout'; +import { AlertOffline } from '@/common/components/system'; import { Popup } from '@/common/components/ui/modal'; import { uploadImage } from '@/common/libs/cloudinary'; import { scrollRestorationAtom } from '@/common/states/scroll-restoration'; @@ -113,9 +115,9 @@ export default function WritePage() { } return ( - <> -
- + + +
-
+ - + ); } diff --git a/src/sw.ts b/src/sw.ts new file mode 100644 index 00000000..753c2e06 --- /dev/null +++ b/src/sw.ts @@ -0,0 +1,13 @@ +import { cleanupOutdatedCaches, precacheAndRoute } from 'workbox-precaching'; + +declare let self: ServiceWorkerGlobalScope; + +cleanupOutdatedCaches(); + +precacheAndRoute(self.__WB_MANIFEST); + +self.addEventListener('message', (event) => { + if (event.data && event.data.type === 'SKIP_WAITING') { + self.skipWaiting(); + } +}); diff --git a/tsconfig.json b/tsconfig.json index c88b5bea..295387f9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,8 @@ "lib": [ "dom", "dom.iterable", - "esnext" + "esnext", + "WebWorker" ], "allowJs": true, "skipLibCheck": true, diff --git a/vite.config.js b/vite.config.js index 2506acff..e01ca16d 100644 --- a/vite.config.js +++ b/vite.config.js @@ -18,54 +18,14 @@ export default defineConfig({ }), svgr(), VitePWA({ + registerType: 'prompt', strategies: 'injectManifest', - srcDir: 'src', - filename: 'my-sw.js', - manifest: { - short_name: '고민의 참견', - name: '고민의 참견, 고참', - icons: [ - { - src: 'icon/android/icon-36.png', - sizes: '36x36', - type: 'image/png', - }, - { - src: 'icon/android/icon-48.png', - sizes: '48x48', - type: 'image/png', - }, - { - src: 'icon/android/icon-72.png', - sizes: '72x72', - type: 'image/png', - }, - { - src: 'icon/android/icon-96.png', - sizes: '96x96', - type: 'image/png', - }, - { - src: 'icon/android/icon-144.png', - sizes: '144x144', - type: 'image/png', - }, - { - src: 'icon/android/icon-192.png', - sizes: '192x192', - type: 'image/png', - }, - { - src: 'icon/android/icon-512.png', - sizes: '512x512', - type: 'image/png', - }, - ], - start_url: './index.html', - display: 'standalone', - theme_color: '#F5F7FB', - background_color: '#ffffff', + injectManifest: { + globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2,otf}'], }, + srcDir: 'src', + filename: 'sw.ts', + manifestFilename: 'manifest.json', }), ], resolve: { diff --git a/yarn.lock b/yarn.lock index 8b9f621e..57a6574c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3549,6 +3549,13 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== +"@types/react-detect-offline@^2.4.1": + version "2.4.1" + resolved "https://registry.yarnpkg.com/@types/react-detect-offline/-/react-detect-offline-2.4.1.tgz#5a71625d3614a56a6c3dacc53c5e9baed0610de0" + integrity sha512-ympSl07fvDbu7+EsnhrKwDWkdOqHp22lukhJYIHxnTfSIzOh1T/K9NGmpwKUtgJaNCfllwLvMSXBlyC6nHEObw== + dependencies: + "@types/react" "*" + "@types/react-dom@^18.0.0", "@types/react-dom@^18.2.7": version "18.2.7" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.7.tgz#67222a08c0a6ae0a0da33c3532348277c70abb63" @@ -8128,6 +8135,11 @@ react-confetti@^6.1.0: dependencies: tween-functions "^1.2.0" +react-detect-offline@^2.4.5: + version "2.4.5" + resolved "https://registry.yarnpkg.com/react-detect-offline/-/react-detect-offline-2.4.5.tgz#3c242516c37b6789cf89102881031f87e70b80e6" + integrity sha512-sI13NPEKl3uQp95FT5CwrYzH3DnXCwNP6TnY6NRF5gFDM4NU9KDlbtA6HG2dwhDVS0RYQGXwZW/mHbdf8fCnaw== + react-device-detect@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/react-device-detect/-/react-device-detect-2.2.3.tgz#97a7ae767cdd004e7c3578260f48cf70c036e7ca" @@ -10006,7 +10018,7 @@ workbox-cacheable-response@7.0.0: dependencies: workbox-core "7.0.0" -workbox-core@7.0.0: +workbox-core@7.0.0, workbox-core@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-7.0.0.tgz#dec114ec923cc2adc967dd9be1b8a0bed50a3545" integrity sha512-81JkAAZtfVP8darBpfRTovHg8DGAVrKFgHpOArZbdFd78VqHr5Iw65f2guwjE2NlCFbPFDoez3D3/6ZvhI/rwQ==