From 0e270b1f01fdcd7af3fd3ad1034f5601092d48fe Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Wed, 12 Feb 2025 21:56:22 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20impl=C3=A9mentation=20de=20la=20PWA=20a?= =?UTF-8?q?vec=20service=20worker,=20manifest=20et=20installation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 459 ++++++++++++++++++++++++- package.json | 6 +- public/images/icons/icon-128x128.png | Bin 0 -> 2396 bytes public/images/icons/icon-144x144.png | Bin 0 -> 3277 bytes public/images/icons/icon-152x152.png | Bin 0 -> 3343 bytes public/images/icons/icon-192x192.png | Bin 0 -> 4025 bytes public/images/icons/icon-384x384.png | Bin 0 -> 9426 bytes public/images/icons/icon-512x512.png | Bin 0 -> 13436 bytes public/images/icons/icon-72x72.png | Bin 0 -> 1534 bytes public/images/icons/icon-96x96.png | Bin 0 -> 1843 bytes public/manifest.json | 96 +++++- public/offline.html | 62 ++++ public/sw.js | 58 ++++ scripts/generate-icons.js | 30 ++ src/components/layout/ClientLayout.tsx | 16 + src/components/ui/InstallPWA.tsx | 88 +++++ 16 files changed, 807 insertions(+), 8 deletions(-) create mode 100644 public/images/icons/icon-128x128.png create mode 100644 public/images/icons/icon-144x144.png create mode 100644 public/images/icons/icon-152x152.png create mode 100644 public/images/icons/icon-192x192.png create mode 100644 public/images/icons/icon-384x384.png create mode 100644 public/images/icons/icon-512x512.png create mode 100644 public/images/icons/icon-72x72.png create mode 100644 public/images/icons/icon-96x96.png create mode 100644 public/offline.html create mode 100644 public/sw.js create mode 100644 scripts/generate-icons.js create mode 100644 src/components/ui/InstallPWA.tsx diff --git a/package-lock.json b/package-lock.json index 52d104a..d5fa89b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "next-themes": "^0.4.4", "react": "^18.2.0", "react-dom": "^18.2.0", + "sharp": "^0.33.2", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", "zod": "^3.22.4" @@ -50,6 +51,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@emnapi/runtime": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", + "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", @@ -189,6 +200,367 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -2044,6 +2416,19 @@ "node": ">=6" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2062,6 +2447,16 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -2233,6 +2628,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", @@ -3607,6 +4011,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT" + }, "node_modules/is-async-function": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", @@ -5332,7 +5742,6 @@ "version": "7.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -5390,6 +5799,45 @@ "node": ">= 0.4" } }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -5499,6 +5947,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", diff --git a/package.json b/package.json index dda9673..483c5f5 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "icons": "node scripts/generate-icons.js" }, "dependencies": { "@radix-ui/react-dialog": "^1.0.5", @@ -22,7 +23,8 @@ "react-dom": "^18.2.0", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", - "zod": "^3.22.4" + "zod": "^3.22.4", + "sharp": "^0.33.2" }, "devDependencies": { "@types/node": "^20.11.16", diff --git a/public/images/icons/icon-128x128.png b/public/images/icons/icon-128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..77789fa14b334a6d037ef956ef3f8cd243113353 GIT binary patch literal 2396 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 literal 0 HcmV?d00001 diff --git a/public/images/icons/icon-512x512.png b/public/images/icons/icon-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..90ccb41b7c03e470ecb5ec714f7e50e2d50e6604 GIT binary patch literal 13436 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%0DkRdl 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 Lp07CPbo2iJt2T4i literal 0 HcmV?d00001 diff --git a/public/images/icons/icon-72x72.png b/public/images/icons/icon-72x72.png new file mode 100644 index 0000000000000000000000000000000000000000..3e26ccc06990de11f49105a511198bbca08c6276 GIT binary patch literal 1534 zcmV138tyG2AlL3h;1MGsL{j*N@=N83KXm+rtHkUu&^R*T3A?i%$cR6 zW%pk8(mQu{Sii}6+r4|{vvX(8IrrQN1Y9bF$F>@XHX4|A5X^2Fh{X-e76@j%kPLh> zFq;xSg0F}+goF293dm4N34hwatj0jBSM|W37#w0(31-!i2;bxhoDepH1hc!U2j;mU zHX23@uNzER8GFvav|af?q=Ta0L|z_y))ky`WW7KT{Z+a1z8US01kKGZ;IL8kPBWip z5*u9;65LkBHFH?jOe!h)`x#q6MX}~!XEQK8krNKA=&GF~1!7@jZqJHyHsn^0*J+53 zJQgQ~khxonf3}qI<89M?Xdub|y1T$HbR>9X$dvM12BJGx01?ctsUAGZ!!C3t_-YN^ zP6}||{~qnh%pN?pRrSD09=5C8;!8_Br&-O9GH>>h@J}=nEmGWV0XM`^2WC&c=Lyo{FD7tjs~BB z&%$@U@0iVAg9c=+K_Vu)8Cb5F_yXqB<|*EPeU2xSY3^3Z`84nEo8y-+CivjMJpc2L z1>SXjmPaJ*VgeANv4DYCQZ|^H3QK_TN;j^}@#+do{-282d_eY(yisX!7G30Ci$)ju zo``%%QUO(LXZ+4klFJgrK;=P=0Ud9f=CXC{=(N6o#x0wD*+ZFZQBOckZ`%iUpy9zJ zztok;%kB(1-=^=@#q-jBltA9T0TGRh5i9l34Ku22Rwt&XX z6mQr&;rf~YqPKf)rR)JkvnGiPv{XQQBKGwgP&&=|WGp3MIhkcH@{!>r|M|!J0y-qE zklEK(`H*VJj)QJ`n18LzJ}V_a`2j~dp!XYMVy4It#7DBq9KHb}Yi##VD!8`xfWoCr z{M#}Fv-7=rO&4jh%8sy^?xR&lUIeAXvXPR6VGv&bgup zyIAMhSuxX`3_b(7n3LYwH=!RO0ySxeoyRLNs%cG{tg%pgCAgKf1ymkn;@Oof-Tr-E z@8@Do}*vy^{zJ5cXduQRQch%R6tdR$uEAF5a|YVWL&!;u@ZFF1OX>yk8&G+W<$T}=Jw8x2t>2q}e^C9Knf@R@!AJ*m*e3FYE-}dL zRSdSE0G&E%pK3tAAt`o1tqwD2C;5K{amb%?#Xw!>Xfob=nZyZ*uc8C<1g-dDW3LtoawI~l0 zz&3{HaMs}`Az4w9smFU literal 0 HcmV?d00001 diff --git a/public/images/icons/icon-96x96.png b/public/images/icons/icon-96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..bf276acd4b234b8b1b729fe2c5db38ca71f5d71a GIT binary patch literal 1843 zcmV-32h8}1P)z%}hc|cI=iA(g;)z zwWw_3gA+7t)H zUOQRuo9yh9eu^Avy>H%cJ@e+x+xINX1DQYn)?sH&E;vq;JRQCw>gXn4r(^T z#xXF6i~7S_BNZj=sp@xw6J@YP$SD=Cu)q=g}rLe_PF}u=&ajgPA-1^@rS zssZ11vYA{_JpBYZqy zzDKS6Nfy14gmDBoBsAmn(?M+@QDbzuOhFgVWk~eGLmVrA&Pi`1QA4!9TV@f?=13M{ zhX5{5AOU|-8%X>QZ?^WS!}}7>;wfWUmC2N!LZ%YD2Cv2MIJ7UJ_ID&yDmmE=O(rMQ zwhxEs#>(J2x}ZbU1{VdqvA9p2{ytsuX`PAWa!Q^3C8LfWNH`m@U0dU7G2KS206BmJ zj8Yq3a8O$|4ylaGV(Eq8g8b12!l%9%4DMPwD01{b!g)gd61>-u{R#4MBghHZvSG+q z;}?^l!!N<2p7S$4OgrWL|JPgk%q8H*Q)xvlr%$BKBmiB_iOF6eiJ-@YzeM8*IQ(fs z=^_U|Ntj1~TX+CvY%J#xH64E)uR%`-zr#(aLJ_0{KrHKO7&N{YF0Py4j@dW@+#-}H zUhGs`7xgMPL0ya^pb`;2k!>Uam3TnW25;_#CtkXph>;TzA99bltzO>m$-OY#jA3Wp z!qKDz+_*mGO2CyrMpd)u@#KCOcAhFLf;1dWNkHefDOHIK4veeL<0&VETn#=yG^}=S zi>ozl11eIVzh_Hya$XRArZhe&0qffbm0yy$J)ur@rqt5cXyZOw5wP$DP$1p+0$xfs zn^VWXPO6s{cr&fdY6AYvR-=%sS4P#6X8P%eoPbC@s0-&ZBqVg-%&L|M{V#$1K&Bz5!KYPH?xN!vdc}P-;gak|> zUcAK^9!bvNQ2vMq`J3UP{50196ig{W1L9L$?f2s~cx@#cJ0sIl@C6KAd!xokk9#A5wN2~4WIpc6Al@; zS&MZ~^F{5EoQmDdI-4x?^C?|TEeX)EhL13b03B=i2$KlVw}cFY1fYZj7+S+em_mTA zHGG6A1R%C=itAUy$0Pzo;eB3wkwTvuK86t>42L!an}mtLf=xP=EEC2dU$X25y&_;H zoT7aOl&ut+!YNy6Lg7}SDV)Ns*qD-95n(7I_3~v43rGjOc(>3Jh2q^#`TBfUPz{s< zlW?Y}e*m;;ppY^OOfH|?!X#|qqsUsA3o=B*cDCScW9K?)Th&N} z_bjDWTm%9#C+grWYdR9PG=~|%nMGJ^*|dy*j|2`8be*UNBW4?z9IkrWDJ2W)Vo!(O z5maORE_7xrFJ;agb)Q`atHNIclrwxObIyx0szoy-viS8;6l hprvfaF?(UWe*mjRna3ALBnkik002ovPDHLkV1ho4XCwds literal 0 HcmV?d00001 diff --git a/public/manifest.json b/public/manifest.json index 9bce1ea..09ad56e 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,17 +1,103 @@ { "name": "StripStream", "short_name": "StripStream", - "description": "A modern web reader for Komga", + "description": "Votre bibliothèque numérique pour lire vos BD, mangas et comics préférés", "start_url": "/", "display": "standalone", + "orientation": "portrait", "background_color": "#0F172A", "theme_color": "#4F46E5", + "categories": ["books", "entertainment", "reading"], "icons": [ { - "src": "/favicon.svg", - "sizes": "32x32", - "type": "image/svg+xml", + "src": "/images/icons/icon-72x72.png", + "sizes": "72x72", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/images/icons/icon-96x96.png", + "sizes": "96x96", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/images/icons/icon-128x128.png", + "sizes": "128x128", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/images/icons/icon-144x144.png", + "sizes": "144x144", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/images/icons/icon-152x152.png", + "sizes": "152x152", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/images/icons/icon-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/images/icons/icon-384x384.png", + "sizes": "384x384", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/images/icons/icon-512x512.png", + "sizes": "512x512", + "type": "image/png", "purpose": "any maskable" } - ] + ], + "screenshots": [ + { + "src": "/images/screenshots/home.png", + "sizes": "1280x720", + "type": "image/png", + "platform": "wide", + "label": "Page d'accueil de StripStream" + }, + { + "src": "/images/screenshots/reader.png", + "sizes": "1280x720", + "type": "image/png", + "platform": "wide", + "label": "Lecteur de BD StripStream" + } + ], + "shortcuts": [ + { + "name": "Accueil", + "url": "/", + "icons": [ + { + "src": "/images/icons/home.png", + "sizes": "96x96", + "type": "image/png" + } + ] + }, + { + "name": "Bibliothèques", + "url": "/libraries", + "icons": [ + { + "src": "/images/icons/library.png", + "sizes": "96x96", + "type": "image/png" + } + ] + } + ], + "related_applications": [], + "prefer_related_applications": false } diff --git a/public/offline.html b/public/offline.html new file mode 100644 index 0000000..6de462a --- /dev/null +++ b/public/offline.html @@ -0,0 +1,62 @@ + + + + + + Hors ligne - StripStream + + + +
+

Vous êtes hors ligne

+

+ Il semble que vous n'ayez pas de connexion internet. Certaines fonctionnalités de + StripStream peuvent ne pas être disponibles en mode hors ligne. +

+ +
+ + diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..6ada58b --- /dev/null +++ b/public/sw.js @@ -0,0 +1,58 @@ +const CACHE_NAME = "stripstream-cache-v1"; +const OFFLINE_PAGE = "/offline.html"; + +const STATIC_ASSETS = [ + "/", + "/offline.html", + "/manifest.json", + "/favicon.svg", + "/images/icons/icon-192x192.png", + "/images/icons/icon-512x512.png", +]; + +// Installation du service worker +self.addEventListener("install", (event) => { + event.waitUntil( + caches.open(CACHE_NAME).then((cache) => { + return cache.addAll(STATIC_ASSETS); + }) + ); + self.skipWaiting(); +}); + +// Activation et nettoyage des anciens caches +self.addEventListener("activate", (event) => { + event.waitUntil( + caches.keys().then((cacheNames) => { + return Promise.all( + cacheNames.filter((name) => name !== CACHE_NAME).map((name) => caches.delete(name)) + ); + }) + ); + self.clients.claim(); +}); + +// Stratégie de cache : Network First avec fallback sur le cache +self.addEventListener("fetch", (event) => { + // Ne pas intercepter les requêtes vers l'API + if (event.request.url.includes("/api/")) { + return; + } + + event.respondWith( + fetch(event.request) + .then((response) => { + // Mettre en cache la nouvelle réponse + const responseClone = response.clone(); + caches.open(CACHE_NAME).then((cache) => { + cache.put(event.request, responseClone); + }); + return response; + }) + .catch(async () => { + const cache = await caches.open(CACHE_NAME); + const cachedResponse = await cache.match(event.request); + return cachedResponse || cache.match(OFFLINE_PAGE); + }) + ); +}); diff --git a/scripts/generate-icons.js b/scripts/generate-icons.js new file mode 100644 index 0000000..c307daf --- /dev/null +++ b/scripts/generate-icons.js @@ -0,0 +1,30 @@ +const sharp = require("sharp"); +const fs = require("fs").promises; +const path = require("path"); + +const sizes = [72, 96, 128, 144, 152, 192, 384, 512]; +const inputSvg = path.join(__dirname, "../public/favicon.svg"); +const outputDir = path.join(__dirname, "../public/images/icons"); + +async function generateIcons() { + try { + // Créer le dossier de sortie s'il n'existe pas + await fs.mkdir(outputDir, { recursive: true }); + + // Générer les icônes pour chaque taille + for (const size of sizes) { + const outputPath = path.join(outputDir, `icon-${size}x${size}.png`); + + await sharp(inputSvg).resize(size, size).png().toFile(outputPath); + + console.log(`✓ Icône ${size}x${size} générée`); + } + + console.log("\n✨ Toutes les icônes ont été générées avec succès !"); + } catch (error) { + console.error("Erreur lors de la génération des icônes:", error); + process.exit(1); + } +} + +generateIcons(); diff --git a/src/components/layout/ClientLayout.tsx b/src/components/layout/ClientLayout.tsx index 4e19875..8d7238c 100644 --- a/src/components/layout/ClientLayout.tsx +++ b/src/components/layout/ClientLayout.tsx @@ -4,6 +4,7 @@ import { ThemeProvider } from "next-themes"; import { useState, useEffect } from "react"; import { Header } from "@/components/layout/Header"; import { Sidebar } from "@/components/layout/Sidebar"; +import { InstallPWA } from "../ui/InstallPWA"; import { Toaster } from "@/components/ui/toaster"; export default function ClientLayout({ children }: { children: React.ReactNode }) { @@ -34,12 +35,27 @@ export default function ClientLayout({ children }: { children: React.ReactNode } }; }, [isSidebarOpen]); + useEffect(() => { + // Enregistrer le service worker + if ("serviceWorker" in navigator) { + navigator.serviceWorker + .register("/sw.js") + .then((registration) => { + console.log("Service Worker enregistré avec succès:", registration); + }) + .catch((error) => { + console.error("Erreur lors de l'enregistrement du Service Worker:", error); + }); + } + }, []); + return (
setIsSidebarOpen(!isSidebarOpen)} />
{children}
+
diff --git a/src/components/ui/InstallPWA.tsx b/src/components/ui/InstallPWA.tsx new file mode 100644 index 0000000..6e12fd5 --- /dev/null +++ b/src/components/ui/InstallPWA.tsx @@ -0,0 +1,88 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { Download } from "lucide-react"; + +interface BeforeInstallPromptEvent extends Event { + prompt: () => Promise; + userChoice: Promise<{ outcome: "accepted" | "dismissed" }>; +} + +export function InstallPWA() { + const [deferredPrompt, setDeferredPrompt] = useState(null); + const [isInstallable, setIsInstallable] = useState(false); + const [isIOS, setIsIOS] = useState(false); + + useEffect(() => { + // Détecter si c'est un appareil iOS + const isIOSDevice = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; + setIsIOS(isIOSDevice); + + const handleBeforeInstallPrompt = (e: Event) => { + e.preventDefault(); + setDeferredPrompt(e as BeforeInstallPromptEvent); + setIsInstallable(true); + }; + + window.addEventListener("beforeinstallprompt", handleBeforeInstallPrompt); + + // Vérifier si l'app est déjà installée + if (window.matchMedia("(display-mode: standalone)").matches) { + setIsInstallable(false); + } + + return () => { + window.removeEventListener("beforeinstallprompt", handleBeforeInstallPrompt); + }; + }, []); + + const handleInstallClick = async () => { + if (!deferredPrompt) return; + + try { + await deferredPrompt.prompt(); + const { outcome } = await deferredPrompt.userChoice; + + if (outcome === "accepted") { + setIsInstallable(false); + } + } catch (error) { + console.error("Erreur lors de l'installation:", error); + } + + setDeferredPrompt(null); + }; + + if (!isInstallable && !isIOS) return null; + + return ( +
+
+
+ +
+

Installer StripStream

+

+ {isIOS + ? "Ajoutez StripStream à votre écran d'accueil pour une meilleure expérience" + : "Installez StripStream pour un accès rapide et une meilleure expérience"} +

+ {isIOS ? ( +
+ Appuyez sur Partager puis{" "} + Sur l'écran d'accueil +
+ ) : ( + + )} +
+
+
+
+ ); +}