diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..bde4b4f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "npm" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + day: "friday" + time: "09:00" + timezone: "Asia/Shanghai" + open-pull-requests-limit: 20 + reviewers: + - "EOEFANS/Web-dev" diff --git a/.github/workflows/azure-staticwebapp.yml b/.github/workflows/azure-staticwebapp.yml new file mode 100644 index 0000000..7b93c7b --- /dev/null +++ b/.github/workflows/azure-staticwebapp.yml @@ -0,0 +1,115 @@ +# This workflow will build and push a web application to an Azure Static Web App when you change your code. +# +# This workflow assumes you have already created the target Azure Static Web App. +# For instructions see https://docs.microsoft.com/azure/static-web-apps/get-started-portal?tabs=vanilla-javascript +# +# To configure this workflow: +# +# 1. Set up a secret in your repository named AZURE_STATIC_WEB_APPS_API_TOKEN with the value of your Static Web Apps deployment token. +# For instructions on obtaining the deployment token see: https://docs.microsoft.com/azure/static-web-apps/deployment-token-management +# +# 3. Change the values for the APP_LOCATION, API_LOCATION and APP_ARTIFACT_LOCATION, AZURE_STATIC_WEB_APPS_API_TOKEN environment variables (below). +# For instructions on setting up the appropriate configuration values go to https://docs.microsoft.com/azure/static-web-apps/front-end-frameworks +name: Deploy web app to Azure Static Web Apps + +on: + push: + branches: [ "main" ] + pull_request: + types: [synchronize, closed] + branches: [ "main" ] + +# Environment variables available to all jobs and steps in this workflow +env: + APP_LOCATION: "dist" # location of your client code + API_LOCATION: "" # location of your api source code - optional + APP_ARTIFACT_LOCATION: "" # location of client code build output + AZURE_STATIC_WEB_APPS_API_TOKEN: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }} # secret containing deployment token for your static web app + SKIP_APP_BUILD: true + +permissions: + contents: read + +jobs: + build_and_deploy_job: + permissions: + contents: read # for actions/checkout to fetch code + pull-requests: write # for Azure/static-web-apps-deploy to comment on PRs + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') + runs-on: [ self-hosted, Linux ] + + strategy: + matrix: + node-version: [16.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + name: Build and Deploy Job + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + + - uses: actions/setup-node@v3 + name: Use Node.js ${{ matrix.node-version }} + with: + node-version: ${{ matrix.node-version }} + + - uses: pnpm/action-setup@v2 + name: Install pnpm + id: pnpm-install + with: + version: 7 + run_install: false + + - name: Get pnpm store directory + id: pnpm-cache + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT + + - uses: actions/cache@v3 + name: Setup pnpm cache + with: + path: ${{ steps.pnpm-cache.outputs.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install + + - name: Use pnpm to build + run: pnpm run build + +# - name: post-build test +# run: pnpm test + + - name: Deploy build artifacts to Azure + id: builddeploy + uses: Azure/static-web-apps-deploy@v1 + with: + azure_static_web_apps_api_token: ${{ env.AZURE_STATIC_WEB_APPS_API_TOKEN }} # secret containing api token for app + repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) + action: "upload" + ###### Repository/Build Configurations - These values can be configured to match you app requirements. ###### + # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig + app_location: ${{ env.APP_LOCATION }} + api_location: ${{ env.API_LOCATION }} + app_artifact_location: ${{ env.APP_ARTIFACT_LOCATION }} + skip_app_build: ${{ env.SKIP_APP_BUILD }} #comment this out to use azure oryx build + ###### End of Repository/Build Configurations ###### + + close_pull_request_job: + permissions: + contents: none + if: github.event_name == 'pull_request' && github.event.action == 'closed' + runs-on: [ self-hosted, Linux ] + name: Close Pull Request Job + steps: + - name: Close Pull Request + id: closepullrequest + uses: Azure/static-web-apps-deploy@v1 + with: + azure_static_web_apps_api_token: ${{ env.AZURE_STATIC_WEB_APPS_API_TOKEN }} # secret containing api token for app + action: "close" diff --git a/.gitignore b/.gitignore index e59eaab..c0fbe82 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ node_modules dist dist-ssr *.local -pnpm-lock.yaml + # Editor directories and files .vscode/* !.vscode/extensions.json diff --git a/index.html b/index.html index 312c597..776bc55 100644 --- a/index.html +++ b/index.html @@ -6,6 +6,7 @@ + diff --git a/package.json b/package.json index 81b07d8..2d25c75 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,6 @@ "version": "0.0.0", "type": "module", "scripts": { - "postinstall": "patch-package", "dev": "vite", "build": "tsc && vite build", "preview": "vite preview", @@ -22,13 +21,13 @@ "@reduxjs/toolkit": "^1.9.1", "axios": "^1.2.3", "dayjs": "^1.11.7", - "imagesloaded": "^5.0.0", "intersection-observer": "^0.12.2", + "json-bigint": "^1.0.0", "loading-attribute-polyfill": "^2.1.0", "masonic": "^3.7.0", + "material-ui-popup-state": "^5.0.4", "nanoid": "^4.0.0", "normalize.css": "^8.0.1", - "patch-package": "^6.5.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-flip-toolkit": "^7.0.17", @@ -40,8 +39,8 @@ }, "devDependencies": { "@babel/core": ">=7.0.0 <8.0.0", - "@faker-js/faker": "^7.6.0", "@types/imagesloaded": "^4.1.2", + "@types/json-bigint": "^1.0.1", "@types/node": "^18.11.18", "@types/react": "^18.0.26", "@types/react-dom": "^18.0.9", @@ -52,7 +51,7 @@ "rollup-plugin-visualizer": "^5.9.0", "terser": ">=5.4.0 <6.0.0", "typescript": "^4.9.3", - "vite": "4.0.3", + "vite": "4.0.4", "vite-plugin-compression": "^0.5.1" } -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 078b737..9a7f0da 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,12 @@ specifiers: '@dnd-kit/utilities': ^3.2.1 '@emotion/react': ^11.10.5 '@emotion/styled': ^11.10.5 - '@faker-js/faker': ^7.6.0 '@mui/icons-material': ^5.11.0 '@mui/lab': 5.0.0-alpha.114 '@mui/material': ^5.11.3 '@reduxjs/toolkit': ^1.9.1 '@types/imagesloaded': ^4.1.2 + '@types/json-bigint': ^1.0.1 '@types/node': ^18.11.18 '@types/react': ^18.0.26 '@types/react-dom': ^18.0.9 @@ -22,14 +22,14 @@ specifiers: '@vitejs/plugin-react-swc': ^3.0.0 axios: ^1.2.3 dayjs: ^1.11.7 - imagesloaded: ^5.0.0 intersection-observer: ^0.12.2 + json-bigint: ^1.0.0 less: ^4.1.3 loading-attribute-polyfill: ^2.1.0 masonic: ^3.7.0 + material-ui-popup-state: ^5.0.4 nanoid: ^4.0.0 normalize.css: ^8.0.1 - patch-package: ^6.5.1 react: ^18.2.0 react-dom: ^18.2.0 react-flip-toolkit: ^7.0.17 @@ -41,7 +41,7 @@ specifiers: rollup-plugin-visualizer: ^5.9.0 terser: '>=5.4.0 <6.0.0' typescript: ^4.9.3 - vite: 4.0.3 + vite: 4.0.4 vite-plugin-compression: ^0.5.1 dependencies: @@ -57,13 +57,13 @@ dependencies: '@reduxjs/toolkit': 1.9.1_k4ae6lp43ej6mezo3ztvx6pykq axios: 1.2.3 dayjs: 1.11.7 - imagesloaded: 5.0.0 intersection-observer: 0.12.2 + json-bigint: 1.0.0 loading-attribute-polyfill: 2.1.0 masonic: 3.7.0_react@18.2.0 + material-ui-popup-state: 5.0.4_lskpmcsdi7ipu6qpuapyu56ihm nanoid: 4.0.0 normalize.css: 8.0.1 - patch-package: 6.5.1 react: 18.2.0 react-dom: 18.2.0_react@18.2.0 react-flip-toolkit: 7.0.17_biqbaboplfbrettd7655fr4n2y @@ -75,20 +75,20 @@ dependencies: devDependencies: '@babel/core': 7.20.7 - '@faker-js/faker': 7.6.0 '@types/imagesloaded': 4.1.2 + '@types/json-bigint': 1.0.1 '@types/node': 18.11.18 '@types/react': 18.0.26 '@types/react-dom': 18.0.10 '@types/react-lazy-load-image-component': 1.5.2 - '@vitejs/plugin-legacy': 3.0.1_terser@5.16.1+vite@4.0.3 - '@vitejs/plugin-react-swc': 3.0.1_vite@4.0.3 + '@vitejs/plugin-legacy': 3.0.1_terser@5.16.1+vite@4.0.4 + '@vitejs/plugin-react-swc': 3.0.1_vite@4.0.4 less: 4.1.3 rollup-plugin-visualizer: 5.9.0 terser: 5.16.1 typescript: 4.9.4 - vite: 4.0.3_25ccpcpgpj6lg6lpj5keiqybuq - vite-plugin-compression: 0.5.1_vite@4.0.3 + vite: 4.0.4_25ccpcpgpj6lg6lpj5keiqybuq + vite-plugin-compression: 0.5.1_vite@4.0.4 packages: @@ -698,11 +698,6 @@ packages: '@essentials/raf': 1.2.0 dev: false - /@faker-js/faker/7.6.0: - resolution: {integrity: sha512-XK6BTq1NDMo9Xqw/YkYyGjSsg44fbNwYRx7QK2CuoQgyy+f1rrTDHoExVM5PsyXCtfl2vs2vVJ0MN0yN6LppRw==} - engines: {node: '>=14.0.0', npm: '>=6.0.0'} - dev: true - /@jridgewell/gen-mapping/0.1.1: resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==} engines: {node: '>=6.0.0'} @@ -1181,6 +1176,10 @@ packages: resolution: {integrity: sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==} dev: false + /@types/json-bigint/1.0.1: + resolution: {integrity: sha512-zpchZLNsNuzJHi6v64UBoFWAvQlPhch7XAi36FkH6tL1bbbmimIF+cS7vwkzY4u5RaSWMoflQfu+TshMPPw8uw==} + dev: true + /@types/node/18.11.18: resolution: {integrity: sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==} dev: true @@ -1238,7 +1237,7 @@ packages: resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==} dev: false - /@vitejs/plugin-legacy/3.0.1_terser@5.16.1+vite@4.0.3: + /@vitejs/plugin-legacy/3.0.1_terser@5.16.1+vite@4.0.4: resolution: {integrity: sha512-XCtEjxoR3rmy000ujYRBp5kggWqzHz9+F20/yIMUWOzbvu0+KW1e14Fvb8h7SpNn+bfjGW1RiAs1Vrgb7Js+iQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -1251,26 +1250,22 @@ packages: regenerator-runtime: 0.13.11 systemjs: 6.13.0 terser: 5.16.1 - vite: 4.0.3_25ccpcpgpj6lg6lpj5keiqybuq + vite: 4.0.4_25ccpcpgpj6lg6lpj5keiqybuq dev: true - /@vitejs/plugin-react-swc/3.0.1_vite@4.0.3: + /@vitejs/plugin-react-swc/3.0.1_vite@4.0.4: resolution: {integrity: sha512-3GQ2oruZO9j8dSHcI0MUeOZQBhjYyDQsF/pKY4Px+CJxn0M16OhgFeEzUjeuwci4zhhjoNIDE9aFNaV5GMQ09g==} peerDependencies: vite: ^4 dependencies: '@swc/core': 1.3.24 - vite: 4.0.3_25ccpcpgpj6lg6lpj5keiqybuq + vite: 4.0.4_25ccpcpgpj6lg6lpj5keiqybuq dev: true /@xobotyi/scrollbar-width/1.9.5: resolution: {integrity: sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==} dev: false - /@yarnpkg/lockfile/1.1.0: - resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} - dev: false - /acorn/8.8.1: resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} engines: {node: '>=0.4.0'} @@ -1293,16 +1288,12 @@ packages: engines: {node: '>=8'} dependencies: color-convert: 2.0.1 + dev: true /asynckit/0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: false - /at-least-node/1.0.0: - resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} - engines: {node: '>= 4.0.0'} - dev: false - /axios/1.2.3: resolution: {integrity: sha512-pdDkMYJeuXLZ6Xj/Q5J3Phpe+jbGdsSzlQaFVkMQzRUL05+6+tetX8TV3p4HrU4kzuO9bt+io/yGQxuyxA/xcw==} dependencies: @@ -1322,22 +1313,8 @@ packages: resolve: 1.22.1 dev: false - /balanced-match/1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - dev: false - - /brace-expansion/1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - dev: false - - /braces/3.0.2: - resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} - engines: {node: '>=8'} - dependencies: - fill-range: 7.0.1 + /bignumber.js/9.1.1: + resolution: {integrity: sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==} dev: false /browserslist/4.21.4: @@ -1376,9 +1353,10 @@ packages: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 + dev: true - /ci-info/2.0.0: - resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==} + /classnames/2.3.2: + resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==} dev: false /cliui/8.0.1: @@ -1405,12 +1383,14 @@ packages: engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 + dev: true /color-name/1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} /color-name/1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true /combined-stream/1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} @@ -1423,10 +1403,6 @@ packages: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} dev: true - /concat-map/0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - dev: false - /convert-source-map/1.9.0: resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} @@ -1458,17 +1434,6 @@ packages: yaml: 1.10.2 dev: false - /cross-spawn/6.0.5: - resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} - engines: {node: '>=4.8'} - dependencies: - nice-try: 1.0.5 - path-key: 2.0.1 - semver: 5.7.1 - shebang-command: 1.2.0 - which: 1.3.1 - dev: false - /css-in-js-utils/3.1.0: resolution: {integrity: sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==} dependencies: @@ -1601,10 +1566,6 @@ packages: engines: {node: '>=10'} dev: false - /ev-emitter/2.1.2: - resolution: {integrity: sha512-jQ5Ql18hdCQ4qS+RCrbLfz1n+Pags27q5TwMKvZyhp5hh2UULUYZUy1keqj6k6SYsdqIYjnmz7xyyEY0V67B8Q==} - dev: false - /fast-deep-equal/3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: false @@ -1621,23 +1582,10 @@ packages: resolution: {integrity: sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==} dev: false - /fill-range/7.0.1: - resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} - engines: {node: '>=8'} - dependencies: - to-regex-range: 5.0.1 - dev: false - /find-root/1.1.0: resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} dev: false - /find-yarn-workspace-root/2.0.0: - resolution: {integrity: sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==} - dependencies: - micromatch: 4.0.5 - dev: false - /flip-toolkit/7.0.17: resolution: {integrity: sha512-bybUdLNbC80F0+v+t3TBfxEAMVGhvXLi3pZ+qTtr4aCu/Y/5YQimMij0xJKT1qtz4SO31V4zftIgaTJdCCFiHA==} engines: {node: '>=8', npm: '>=5'} @@ -1673,20 +1621,6 @@ packages: universalify: 2.0.0 dev: true - /fs-extra/9.1.0: - resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} - engines: {node: '>=10'} - dependencies: - at-least-node: 1.0.0 - graceful-fs: 4.2.10 - jsonfile: 6.1.0 - universalify: 2.0.0 - dev: false - - /fs.realpath/1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - dev: false - /fsevents/2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1707,17 +1641,6 @@ packages: engines: {node: 6.* || 8.* || >= 10.*} dev: true - /glob/7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - dev: false - /globals/11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -1725,6 +1648,7 @@ packages: /graceful-fs/4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} requiresBuild: true + dev: true /has-flag/3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} @@ -1733,6 +1657,7 @@ packages: /has-flag/4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + dev: true /has/1.0.3: resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} @@ -1766,12 +1691,6 @@ packages: dev: true optional: true - /imagesloaded/5.0.0: - resolution: {integrity: sha512-/0JGSubc1MTCoDKVmonLHgbifBWHdyLkun+R/151E1c5n79hiSxcd7cB7mPXFgojYu8xnRZv7GYxzKoxW8BetQ==} - dependencies: - ev-emitter: 2.1.2 - dev: false - /immer/9.0.17: resolution: {integrity: sha512-+hBruaLSQvkPfxRiTLK/mi4vLH+/VQS6z2KJahdoxlleFOI8ARqzOF17uy12eFDlqWmPoygwc5evgwcp+dlHhg==} dev: false @@ -1784,17 +1703,6 @@ packages: resolve-from: 4.0.0 dev: false - /inflight/1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - dev: false - - /inherits/2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - dev: false - /inline-style-prefixer/6.0.4: resolution: {integrity: sha512-FwXmZC2zbeeS7NzGjJ6pAiqRhXR0ugUShSNb6GApMl6da0/XGc4MOJsoWAywia52EEWbXNSy0pzkwz/+Y+swSg==} dependencies: @@ -1810,13 +1718,6 @@ packages: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} dev: false - /is-ci/2.0.0: - resolution: {integrity: sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==} - hasBin: true - dependencies: - ci-info: 2.0.0 - dev: false - /is-core-module/2.11.0: resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==} dependencies: @@ -1826,17 +1727,13 @@ packages: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} hasBin: true + dev: true /is-fullwidth-code-point/3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} dev: true - /is-number/7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - dev: false - /is-what/3.14.1: resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} dev: true @@ -1846,10 +1743,7 @@ packages: engines: {node: '>=8'} dependencies: is-docker: 2.2.1 - - /isexe/2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: false + dev: true /js-cookie/2.2.1: resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} @@ -1863,6 +1757,12 @@ packages: engines: {node: '>=4'} hasBin: true + /json-bigint/1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + dependencies: + bignumber.js: 9.1.1 + dev: false + /json-parse-even-better-errors/2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: false @@ -1878,12 +1778,7 @@ packages: universalify: 2.0.0 optionalDependencies: graceful-fs: 4.2.10 - - /klaw-sync/6.0.0: - resolution: {integrity: sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==} - dependencies: - graceful-fs: 4.2.10 - dev: false + dev: true /less/4.1.3: resolution: {integrity: sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==} @@ -1963,16 +1858,25 @@ packages: trie-memoize: 1.2.0 dev: false - /mdn-data/2.0.14: - resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + /material-ui-popup-state/5.0.4_lskpmcsdi7ipu6qpuapyu56ihm: + resolution: {integrity: sha512-QdlKHiKv498UNsH3zpQNJ7SN9RYiEjR5MSJIg+a9gqCp43G0A8fo1r0kmX1qFTLGTfYKgXix5RANJkNTujWtxA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + '@babel/runtime': 7.20.7 + '@mui/material': 5.11.3_lskpmcsdi7ipu6qpuapyu56ihm + classnames: 2.3.2 + prop-types: 15.8.1 + react: 18.2.0 + transitivePeerDependencies: + - '@emotion/react' + - '@emotion/styled' + - '@types/react' + - react-dom dev: false - /micromatch/4.0.5: - resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} - engines: {node: '>=8.6'} - dependencies: - braces: 3.0.2 - picomatch: 2.3.1 + /mdn-data/2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} dev: false /mime-db/1.52.0: @@ -1995,16 +1899,6 @@ packages: dev: true optional: true - /minimatch/3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - dependencies: - brace-expansion: 1.1.11 - dev: false - - /minimist/1.2.7: - resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} - dev: false - /ms/2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} @@ -2057,10 +1951,6 @@ packages: dev: true optional: true - /nice-try/1.0.5: - resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} - dev: false - /node-releases/2.0.8: resolution: {integrity: sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==} @@ -2073,20 +1963,6 @@ packages: engines: {node: '>=0.10.0'} dev: false - /once/1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - dependencies: - wrappy: 1.0.2 - dev: false - - /open/7.4.2: - resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} - engines: {node: '>=8'} - dependencies: - is-docker: 2.2.1 - is-wsl: 2.2.0 - dev: false - /open/8.4.0: resolution: {integrity: sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==} engines: {node: '>=12'} @@ -2096,11 +1972,6 @@ packages: is-wsl: 2.2.0 dev: true - /os-tmpdir/1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} - dev: false - /parent-module/1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -2123,37 +1994,6 @@ packages: engines: {node: '>= 0.10'} dev: true - /patch-package/6.5.1: - resolution: {integrity: sha512-I/4Zsalfhc6bphmJTlrLoOcAF87jcxko4q0qsv4bGcurbr8IskEOtdnt9iCmsQVGL1B+iUhSQqweyTLJfCF9rA==} - engines: {node: '>=10', npm: '>5'} - hasBin: true - dependencies: - '@yarnpkg/lockfile': 1.1.0 - chalk: 4.1.2 - cross-spawn: 6.0.5 - find-yarn-workspace-root: 2.0.0 - fs-extra: 9.1.0 - is-ci: 2.0.0 - klaw-sync: 6.0.0 - minimist: 1.2.7 - open: 7.4.2 - rimraf: 2.7.1 - semver: 5.7.1 - slash: 2.0.0 - tmp: 0.0.33 - yaml: 1.10.2 - dev: false - - /path-is-absolute/1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - dev: false - - /path-key/2.0.1: - resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} - engines: {node: '>=4'} - dev: false - /path-parse/1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} @@ -2168,6 +2008,7 @@ packages: /picomatch/2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + dev: true /pify/4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} @@ -2412,13 +2253,6 @@ packages: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - /rimraf/2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - hasBin: true - dependencies: - glob: 7.2.3 - dev: false - /rollup-plugin-visualizer/5.9.0: resolution: {integrity: sha512-bbDOv47+Bw4C/cgs0czZqfm8L82xOZssk4ayZjG40y9zbXclNk7YikrZTDao6p7+HDiGxrN0b65SgZiVm9k1Cg==} engines: {node: '>=14'} @@ -2473,6 +2307,8 @@ packages: /semver/5.7.1: resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} hasBin: true + dev: true + optional: true /semver/6.3.0: resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} @@ -2483,23 +2319,6 @@ packages: engines: {node: '>=6.9'} dev: false - /shebang-command/1.2.0: - resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} - engines: {node: '>=0.10.0'} - dependencies: - shebang-regex: 1.0.0 - dev: false - - /shebang-regex/1.0.0: - resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} - engines: {node: '>=0.10.0'} - dev: false - - /slash/2.0.0: - resolution: {integrity: sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==} - engines: {node: '>=6'} - dev: false - /source-map-js/1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} @@ -2593,6 +2412,7 @@ packages: engines: {node: '>=8'} dependencies: has-flag: 4.0.0 + dev: true /supports-preserve-symlinks-flag/1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} @@ -2618,24 +2438,10 @@ packages: engines: {node: '>=10'} dev: false - /tmp/0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} - dependencies: - os-tmpdir: 1.0.2 - dev: false - /to-fast-properties/2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} - /to-regex-range/5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - dependencies: - is-number: 7.0.0 - dev: false - /toggle-selection/1.0.6: resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} dev: false @@ -2660,6 +2466,7 @@ packages: /universalify/2.0.0: resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} engines: {node: '>= 10.0.0'} + dev: true /update-browserslist-db/1.0.10_browserslist@4.21.4: resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==} @@ -2679,7 +2486,7 @@ packages: react: 18.2.0 dev: false - /vite-plugin-compression/0.5.1_vite@4.0.3: + /vite-plugin-compression/0.5.1_vite@4.0.4: resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==} peerDependencies: vite: '>=2.0.0' @@ -2687,13 +2494,13 @@ packages: chalk: 4.1.2 debug: 4.3.4 fs-extra: 10.1.0 - vite: 4.0.3_25ccpcpgpj6lg6lpj5keiqybuq + vite: 4.0.4_25ccpcpgpj6lg6lpj5keiqybuq transitivePeerDependencies: - supports-color dev: true - /vite/4.0.3_25ccpcpgpj6lg6lpj5keiqybuq: - resolution: {integrity: sha512-HvuNv1RdE7deIfQb8mPk51UKjqptO/4RXZ5yXSAvurd5xOckwS/gg8h9Tky3uSbnjYTgUm0hVCet1cyhKd73ZA==} + /vite/4.0.4_25ccpcpgpj6lg6lpj5keiqybuq: + resolution: {integrity: sha512-xevPU7M8FU0i/80DMR+YhgrzR5KS2ORy1B4xcX/cXLsvnUWvfHuqMmVU6N0YiJ4JWGRJJsLCgjEzKjG9/GKoSw==} engines: {node: ^14.18.0 || >=16.0.0} hasBin: true peerDependencies: @@ -2728,13 +2535,6 @@ packages: fsevents: 2.3.2 dev: true - /which/1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true - dependencies: - isexe: 2.0.0 - dev: false - /wrap-ansi/7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -2744,10 +2544,6 @@ packages: strip-ansi: 6.0.1 dev: true - /wrappy/1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - dev: false - /y18n/5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} diff --git a/public/staticwebapp.config.json b/public/staticwebapp.config.json new file mode 100644 index 0000000..89219cd --- /dev/null +++ b/public/staticwebapp.config.json @@ -0,0 +1,9 @@ +{ + "navigationFallback": { + "rewrite": "index.html", + "exclude": ["/assets/*"] + }, + "mimeTypes": { + ".json": "text/json" + } +} diff --git a/src/components/image/image.module.less b/src/components/image/image.module.less index 33ed9ed..1ca36d4 100644 --- a/src/components/image/image.module.less +++ b/src/components/image/image.module.less @@ -8,12 +8,11 @@ &>img { width: 100%; - height: 100%; + height: auto; display: inline-block; object-fit: cover; transition: opacity .5s linear; transition: transform .3s linear; - aspect-ratio: 16/9; &:hover { transition: filter .3s ease-in-out; diff --git a/src/components/image/index.tsx b/src/components/image/index.tsx index 70d1da3..b8e753c 100644 --- a/src/components/image/index.tsx +++ b/src/components/image/index.tsx @@ -1,10 +1,8 @@ import { useCallback, useMemo, useState, memo, ReactElement } from "react"; import { InView } from "react-intersection-observer"; -import { useImageShouldResize } from "@components/proview/imageSize"; import { Once } from "@utils/index"; import { ImageProps } from "./imagetype"; import styles from "./image.module.less"; -import { useScreenSize } from "../proview/screenSize"; import { getImageSize, getResizeHeight, @@ -53,14 +51,13 @@ export default memo(function Image({ real_height = height || getResizeHeight(res, real_width), real_fallback_url = fallbackUrl || DefaultFallbackUrl; const once_callback = useCallback(Once(callback!!), []); - const { isShouldchangeSize } = useImageShouldResize(); return ( {({ inView, ref, entry }) => ( -
+ <>{observer && inView && once_callback(inView)} {children} -
+ )}
); @@ -81,25 +78,17 @@ export function ImageBasic({ observer, callback, children, -}: Omit & { + ...resProps +}: ImageProps & { children?: ReactElement; + [k: string]: any; }) { const once_callback = useCallback(Once(callback!!), []); - const { md } = useScreenSize(); return ( {({ inView, ref, entry }) => ( - + <>{observer && inView && once_callback(inView)} {children} diff --git a/src/components/masonry/index.tsx b/src/components/masonry/index.tsx index 391b16a..6583a97 100644 --- a/src/components/masonry/index.tsx +++ b/src/components/masonry/index.tsx @@ -2,7 +2,7 @@ import { FC, useState, useEffect, useCallback } from "react"; import { Masonry as Masonic_masonry } from "masonic"; import Image from "@components/image"; import { SingleRun, concurrencyRequest } from "@utils/index"; -import { fetchVideos } from "@utils/fetch"; +import { fetchPhotos, fetchVideos } from "@utils/fetch"; import { nanoid } from "nanoid"; import { getImageSize } from "../image/tool"; export default function Masonry() { @@ -12,45 +12,19 @@ export default function Masonry() { stopIndex: number, currentItems: any[] ) => { - const res = await fetchVideos({ - order: "view", - page: 1, + const res = await fetchPhotos({ + type: "recommend", + page: 0, + topic_id: 0, }), - data = res.data.result, - urls = data.map((item) => item.face); - const fetchimagres = await concurrencyRequest(urls, getImageSize, 6); - setLists((lists) => { - return [ - ...lists, - ...data.map((item, index) => { - // if (index !== data.length - 6) { - return { - image: item.face, - name: item.name, - id: nanoid(10), - }; - // } - return { - image: item.face, - name: item.name, - id: nanoid(10), - observer: true, - callback: (inView: boolean) => { - //@ts-ignore - fetchMoreItems(); - console.log("fetching!"); - }, - }; - }), - ]; - }); + data = res.data.result; }; const fetchMoreItemsHandler = useCallback(SingleRun(fetchMoreItems), [ setLists, ]); useEffect(() => { fetchMoreItemsHandler(0, 20, []); - console.log("effect"); + // console.log("effect"); }, [fetchMoreItemsHandler]); return (
void; + resProps: Object; +}; + +export const __Click_Modal__ = (props: __Click_modal__Type) => { + const { children, visible, closeModal, ...resProps } = props; + function handleClick(event: { + stopPropagation: () => void; + target: any; + currentTarget: any; + }) { + //点击蒙层本身时关闭模态框,点击模态框内容时不关闭 + event.stopPropagation(); + // event.nativeEvent.stopImmediatePropagation(); + if (event.target === event.currentTarget) { + closeModal(event); + } + } + let modal; + typeof document !== "undefined" && + (modal = createPortal( +
+ {children} +
, + document.body + )); + return <>{visible && modal}; +}; + +type ModalType = { + isNoScroll: boolean; + children: ReactElement; + visible: boolean; + closeModal: (event: any) => void; +}; +export default memo(function Modal(props: ModalType) { + const { isNoScroll = false, children, ...resProps } = props; + useEffect(() => { + if (isNoScroll) { + disableScroll(); + return enableScroll; + } + }, [isNoScroll]); + //@ts-ignore + return <__Click_Modal__ {...resProps}>{children}; +}); + +type useModalConfigOutputType = { + visible: boolean; + isNoScroll: boolean; + closeModal: () => void; +}; + +export const useModalConfig = ( + _isvisible: boolean, + _isNoScroll: boolean +): useModalConfigOutputType => { + const [isvisible, setVisible] = useState(_isvisible || false); + const [isNoScroll, setScroll] = useState(_isNoScroll || false); + const clickFunc = () => { + setScroll(!isNoScroll); + setVisible(!isvisible); + }; + return { visible: isvisible, isNoScroll, closeModal: clickFunc }; +}; diff --git a/src/components/modal/modal.module.less b/src/components/modal/modal.module.less new file mode 100644 index 0000000..5801b68 --- /dev/null +++ b/src/components/modal/modal.module.less @@ -0,0 +1,11 @@ +/* modal.less */ +.modal { + position: fixed; + top: 0; + left: 0; + bottom: 0; + right: 0; + z-index: 1; + min-height: 100vh; + background-color: rgba(0, 0, 0, 0.7); +} \ No newline at end of file diff --git a/src/components/modal/scrollFn.ts b/src/components/modal/scrollFn.ts new file mode 100644 index 0000000..5524a8b --- /dev/null +++ b/src/components/modal/scrollFn.ts @@ -0,0 +1,22 @@ +//@ts-nocheck +//阻止默认行为 +export function preventDefault(e) { + // console.log({ e }); + e.preventDefault(); +} + +export function enableScroll() { + const wheelEvent = + "onwheel" in document.createElement("div") ? "wheel" : "mousewheel"; + window.removeEventListener("DOMMouseScroll", preventDefault, false); + window.removeEventListener(wheelEvent, preventDefault, { passive: false }); + window.removeEventListener("touchmove", preventDefault, { passive: false }); +} + +export function disableScroll() { + const wheelEvent = + "onwheel" in document.createElement("div") ? "wheel" : "mousewheel"; + window.addEventListener("DOMMouseScroll", preventDefault, false); // older FF + window.addEventListener(wheelEvent, preventDefault, { passive: false }); // modern desktop + window.addEventListener("touchmove", preventDefault, { passive: false }); // mobile +} diff --git a/src/components/proview/imageSize.tsx b/src/components/proview/imageSize.tsx deleted file mode 100644 index 70c6ed2..0000000 --- a/src/components/proview/imageSize.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useContext, useState, createContext, useEffect } from "react"; -import { thorttleFn } from "@utils/index"; -import { ReactChildrenType } from "./type"; -export const image_order_width = 200; - -const ImageContext = createContext<{ isShouldchangeSize: boolean }>({ - isShouldchangeSize: false, -}); - -const ImageShouldResizeProview = ({ children }: ReactChildrenType) => { - const [isShouldchangeSize, setIs] = useState(false); - useEffect(() => { - const handleWindowResize = () => { - setIs(() => window.innerWidth < image_order_width * 2.5); - }; - handleWindowResize(); - const handlerThrttleResize = thorttleFn(handleWindowResize, 10); - window.addEventListener("resize", handlerThrttleResize); - return () => window.removeEventListener("resize", handlerThrttleResize); - }, []); - return ( - - {children} - - ); -}; - -export const useImageShouldResize = () => { - return useContext(ImageContext); -}; - -export default ImageShouldResizeProview; diff --git a/src/components/proview/themePreview.tsx b/src/components/proview/themePreview.tsx index f8cc3a4..9f4886d 100644 --- a/src/components/proview/themePreview.tsx +++ b/src/components/proview/themePreview.tsx @@ -36,9 +36,6 @@ declare module "@mui/material/Button" { } } const theme = createTheme({ - typography: { - fontFamily: "Proxima Soft", - }, palette: { luzao: { main: "#3dff9e", contrastText: "#fff" }, luzaoRed: { main: "#A0191D", contrastText: "#fff" }, diff --git a/src/main.tsx b/src/main.tsx index 838daac..1c4256f 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -16,7 +16,6 @@ import "intersection-observer"; import "./normalize.css"; import "loading-attribute-polyfill"; import "./index.less"; - const router = createBrowserRouter([ { path: "/", diff --git a/src/routers/error.tsx b/src/routers/error.tsx index ca872ec..8eed330 100644 --- a/src/routers/error.tsx +++ b/src/routers/error.tsx @@ -1,3 +1,4 @@ +import message from "@components/message"; import { Button } from "@mui/material"; import { Link } from "react-router-dom"; @@ -24,11 +25,15 @@ export default function ErrorPage() { - 和 - + 、 + - 两个页面(虽然两个是同一个页面)。 + 和 + + + + 。

请尝试点击链接 @@ -49,4 +54,5 @@ export default function ErrorPage() { const deleteAllDataStorage = () => { localStorage.clear(); sessionStorage.clear(); + message.info("重置网站数据成功!"); }; diff --git a/src/routers/layout/header.tsx b/src/routers/layout/header.tsx index e9f5052..c153dfb 100644 --- a/src/routers/layout/header.tsx +++ b/src/routers/layout/header.tsx @@ -1,19 +1,37 @@ -import { useSearchFocus } from "@components/proview/searchFocus"; -import { Flipped } from "react-flip-toolkit"; import styles from "./layout.module.less"; import LOGO from "./logo"; -import RightSide from "./rightSide"; -import Search from "./search"; +import RouterNav from "./routernav"; +import { useAppDispatch } from "@store/hooks"; +import { useLocation } from "react-router-dom"; +import { useEffect, useMemo } from "react"; +import { changeLoadingCauseByUrl } from "@store/loading"; +import { routerNameToLoading } from "@utils/router"; +import { styled } from "@mui/material"; export default function Header() { - // const { focused } = useSearchFocus(); - return ( - // -

- {/* {!focused && } - - {!focused && } */} - -
- // + const dispatch = useAppDispatch(), + location = useLocation(); + useEffect(() => { + dispatch( + changeLoadingCauseByUrl({ + stateName: routerNameToLoading(location.pathname), + }) + ); + }, [location.pathname]); + const JSXRes = useMemo( + () => ( + + + + + ), + [] ); + return <>{JSXRes}; } +const Header_header = styled("header")(({ theme }) => ({ + flexDirection: "row", + [theme.breakpoints.down("sm")]: { + flexDirection: "row-reverse", + justifyContent: "flex-end", + }, +})); diff --git a/src/routers/layout/index.tsx b/src/routers/layout/index.tsx index 3f5ab14..78c1011 100644 --- a/src/routers/layout/index.tsx +++ b/src/routers/layout/index.tsx @@ -4,16 +4,13 @@ import Header from "./header"; import Header_Nav from "./nav"; import { useAppSelector } from "@store/hooks"; import { selectNavMoreShowed } from "@store/device/index"; -import { useSearchFocus } from "@components/proview/searchFocus"; import styles from "./layout.module.less"; export default function Layout() { - const showed = useAppSelector(selectNavMoreShowed), - { focused } = useSearchFocus(); - + const showed = useAppSelector(selectNavMoreShowed); return ( <> -
-
- -
+
+
diff --git a/src/routers/layout/layout.module.less b/src/routers/layout/layout.module.less index 851856c..06ea5b4 100644 --- a/src/routers/layout/layout.module.less +++ b/src/routers/layout/layout.module.less @@ -17,105 +17,4 @@ display: flex; align-items: center; background-color: #fff; -} - -.logo { - display: flex; - align-items: center; - - a { - color: inherit; - text-decoration: none; - } - - &>a:only-child { - display: flex; - position: relative; - flex-flow: row-reverse nowrap; - align-items: center; - padding: 5px 0; - - h1 { - margin: 0; - white-space: nowrap; - - button { - position: absolute; - transform: translate(-20px, -10px); - } - } - } -} - -.modal { - position: absolute; - left: 50%; - top: 50%; - transform: translate(-50%, -50%); - background-color: #fff; - border-radius: 4px; - padding: 15px 10px; - max-width: 400px; - width: 90%; - - - - - ul { - display: flex; - flex-flow: column nowrap; - max-height: 60vh; - overflow-y: auto; - overflow-x: hidden; - } - - li { - margin: 10px; - list-style: none; - box-sizing: border-box; - - &>p:not(:last-of-type) { - margin-bottom: 12px; - color: grey; - } - - &>p { - display: flex; - - strong { - flex: 0 0 25px; - } - } - - - } -} - - -.line_text { - width: 100%; - // color: #a2a9b6; - border: 0; - font-size: 14px; - padding: 1em 0; - position: relative; - -webkit-mask-image: linear-gradient(to right, - transparent, - black, - transparent); - mask-image: linear-gradient(to right, transparent, black, transparent); - - &::before { - content: attr(data-content); - position: absolute; - padding: 0 1ch; - line-height: 1px; - border: solid #d0d0d5; - border-width: 0 99vw; - width: fit-content; - /* for 不支持fit-content浏览器 */ - white-space: nowrap; - left: 50%; - transform: translateX(-50%); - } } \ No newline at end of file diff --git a/src/routers/layout/logo.tsx b/src/routers/layout/logo.tsx deleted file mode 100644 index b7e1f59..0000000 --- a/src/routers/layout/logo.tsx +++ /dev/null @@ -1,143 +0,0 @@ -import { Button, Modal, styled } from "@mui/material"; -import { FC, useState } from "react"; -import { Link } from "react-router-dom"; -import styles from "./layout.module.less"; - -export default function LOGO() { - const [open, set] = useState(false), - handlerClick = () => set((open) => !open); - return ( -
- -

- EOEfans-web端 - - -
-
-
    -
  • -

    - Q: - 为什么界面这么丑? -

    -

    - A: - web端没有UI捏🙇‍♂️🙇‍♂️🙇‍♂️果咩。 -

    -
  • -
  • -

    - Q: - 如何查找我想看的视频类型? -

    -

    - A: - - 😩目前只能通过点击下方tag栏进行查询,其中各分区、原创和转载、 - 最新发布和最多播放互斥外其他tag皆满足异或查询。 -
    - 🤔tag排序不是固定的,可以使用鼠标或者触摸按住tag半秒后进行移动。 -
    - 🤗可以自定义任何你想要的tag栏顺序,下一次访问也有效。 -
    -

    -
  • -
  • -

    - Q: - 露早tag为什么不是应援色? -

    -

    - A: - - 露早GOGO的应援色为 - - #3dff9e - - ,tag字面显示不明显,所以更换为黑露早形态的 - #A0191D - -

    -
  • -
- -
-
-

- - -
- ); -} -const H1 = styled("h1")(({ theme }) => ({ - fontSize: "24px", - [theme.breakpoints.down("sm")]: { - fontSize: "15px", - }, -})); - -//todo 修改logo - -const Yituo: FC<{ - height: number | string; - width: number | string; -}> = ({ height, width }) => ( - - - - - - - - - - - -); diff --git a/src/routers/layout/logo/index.tsx b/src/routers/layout/logo/index.tsx new file mode 100644 index 0000000..61129b2 --- /dev/null +++ b/src/routers/layout/logo/index.tsx @@ -0,0 +1,71 @@ +import { Button, Badge } from "@mui/material"; +import { useState, useMemo, FC } from "react"; +import { H1, Explain, Yituo } from "./modal"; +import styles from "./logo.module.less"; +import { Storage } from "../tools"; + +const QAStorage = new Storage("QAUpdate"); +//暂时先这样 +const _qa_update_time = `2023-2-2`; +const useCheckQANews = () => { + const local_qa_value = useMemo(() => { + return QAStorage.getLocalStorage(""); + }, []); + const [shouldShowNews, set] = useState(local_qa_value !== _qa_update_time), + handlerReadedNews = () => { + QAStorage.setLocalstorage(_qa_update_time); + set(() => false); + }; + return [shouldShowNews, handlerReadedNews] as [boolean, () => void]; +}; + +export default function LOGO() { + const [open, set] = useState(false); + const [shouldShowNews, handlerReadedNews] = useCheckQANews(); + const handlerClick = () => { + set((open) => !open); + handlerReadedNews(); + }; + return ( +
+

+ EOEfans-web端 + + +

+ +
+ ); +} + +const Show_news: FC<{ visible: boolean }> = ({ visible }) => ( + +
+
+); diff --git a/src/routers/layout/logo/logo.module.less b/src/routers/layout/logo/logo.module.less new file mode 100644 index 0000000..3beb0bc --- /dev/null +++ b/src/routers/layout/logo/logo.module.less @@ -0,0 +1,94 @@ +.logo { + display: flex; + align-items: center; + flex-flow: row-reverse nowrap; + padding: 5px 0; + + a { + color: inherit; + text-decoration: none; + } + + + + h1 { + margin: 0; + white-space: nowrap; + position: relative; + padding-right: 30px; + } + +} + +.modal { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + background-color: #fff; + border-radius: 4px; + padding: 15px 10px; + max-width: 400px; + width: 90%; + + + + + ul { + display: flex; + flex-flow: column nowrap; + max-height: 60vh; + overflow-y: auto; + overflow-x: hidden; + } + + li { + margin: 10px; + list-style: none; + box-sizing: border-box; + + &>p:not(:last-of-type) { + margin-bottom: 12px; + color: grey; + } + + &>p { + display: flex; + + strong { + flex: 0 0 25px; + } + } + + + } +} + + +.line_text { + width: 100%; + // color: #a2a9b6; + border: 0; + font-size: 14px; + padding: 1em 0; + position: relative; + -webkit-mask-image: linear-gradient(to right, + transparent, + black, + transparent); + mask-image: linear-gradient(to right, transparent, black, transparent); + + &::before { + content: attr(data-content); + position: absolute; + padding: 0 1ch; + line-height: 1px; + border: solid #d0d0d5; + border-width: 0 99vw; + width: fit-content; + /* for 不支持fit-content浏览器 */ + white-space: nowrap; + left: 50%; + transform: translateX(-50%); + } +} \ No newline at end of file diff --git a/src/routers/layout/logo/modal.tsx b/src/routers/layout/logo/modal.tsx new file mode 100644 index 0000000..135c03a --- /dev/null +++ b/src/routers/layout/logo/modal.tsx @@ -0,0 +1,166 @@ +import { Button, Link, Modal, styled } from "@mui/material"; +import { FC } from "react"; +import styles from "./logo.module.less"; + +export const Explain: FC<{ open: boolean; handlerClick: () => void }> = ( + props +) => ( + <> + +
+
+ + +
+
+ +); + +export const H1 = styled("h1")(({ theme }) => ({ + fontSize: "24px", + [theme.breakpoints.down("sm")]: { + fontSize: "15px", + }, +})); + +export const QA = () => ( +
    +
  • +

    + Q: + 为什么界面这么丑? +

    +

    + A: + web端没有UI捏🙇‍♂️🙇‍♂️🙇‍♂️果咩。 +

    +
  • +
  • +

    + Q: + 如何查找我想看的视频类型? +

    +

    + A: + + 😩目前只能通过点击下方tag栏进行查询,其中各分区、原创和转载、 + 最新发布和最多播放互斥外其他tag皆满足异或查询。 +
    + 🤔tag排序不是固定的,可以使用鼠标或者触摸按住tag半秒后进行移动。 +
    + 🤗可以自定义任何你想要的tag栏顺序,下一次访问也有效。 +
    +

    +
  • +
  • +

    + Q: + 露早tag为什么不是应援色? +

    +

    + A: + + 露早GOGO的应援色为 + + #3dff9e + + ,tag字面显示不明显,所以更换为黑露早形态的 + #A0191D + +

    +
  • +
  • +

    + Q: + 如何进行用户反馈? +

    +

    + A: + + 请前往 + + eoefans反馈 + + +

    +
  • +
  • +

    + Q: + 图片页图片太小了? +

    +

    + A: + + 图片页可点击图片进入放大镜模式,支持手势返回和空白处返回;可使用双指放大对图片大小进行调整。 + +

    +
  • +
+); + +export const Yituo: FC<{ + height: number | string; + width: number | string; +}> = ({ height, width }) => ( + + 芝士蛞蝓 + + + + + + + + + + +); diff --git a/src/routers/layout/nav/tools.ts b/src/routers/layout/nav/VideoTools.ts similarity index 78% rename from src/routers/layout/nav/tools.ts rename to src/routers/layout/nav/VideoTools.ts index 3de3859..4b8c25e 100644 --- a/src/routers/layout/nav/tools.ts +++ b/src/routers/layout/nav/VideoTools.ts @@ -3,35 +3,35 @@ import { useMemo, useState } from "react"; import { Storage } from "../tools"; import { getVersion } from "@utils/index"; -interface DnavStorage { +export interface VideoNavStorage { version: string; - res: NavQueryItemType[]; + res: VideoNavQueryItemType[]; } -export const NavStorage = new Storage("navTagLists"); +export const VideoNavStorage = new Storage("navTagLists"); -export function useNavList(): [ - NavQueryItemType[], - React.Dispatch> +export function useVideoNavList(): [ + VideoNavQueryItemType[], + React.Dispatch> ] { const nav_ok_lists = useMemo(() => { - const local_lists = NavStorage.getLocalStorage({ + const local_lists = VideoNavStorage.getLocalStorage({ version: getVersion(), res: [], - } as DnavStorage); + } as VideoNavStorage); if (!local_lists.version) { - return query_nav_list; + return video_query_nav_list; } //todo 修改逻辑 if (parseFloat(local_lists.version) < parseFloat(getVersion())) { - return query_nav_list; + return video_query_nav_list; } if (PassNavList(local_lists.res)) { return local_lists.res; } - return query_nav_list; + return video_query_nav_list; }, []); - const [navLists, setLists] = useState(nav_ok_lists); + const [navLists, setLists] = useState(nav_ok_lists); return [navLists, setLists]; } // 判断类型 @@ -49,7 +49,7 @@ function checkPropertyType( return input.hasOwnProperty(property) && typeof input[property] === type; } //检测从localstorage提取的querylist是否满足条件 -function PassNavList(lists: NavQueryItemType[]) { +function PassNavList(lists: VideoNavQueryItemType[]) { if ( lists.length > 0 && lists.every((item) => { @@ -102,7 +102,7 @@ export type NavRouterItemType = { name: string; cancelable: boolean; }; -export type NavQueryItemType = { +export type VideoNavQueryItemType = { id: string; type: "query"; query: string; @@ -110,8 +110,8 @@ export type NavQueryItemType = { queryString: string; cancelable: boolean; }; -export type NavListItemType = NavQueryItemType | NavRouterItemType; -const nav_tag_list_no_id: Omit[] = [ +export type NavListItemType = VideoNavQueryItemType | NavRouterItemType; +const nav_tag_list_no_id: Omit[] = [ { type: "query", query: "露早", @@ -197,25 +197,10 @@ const nav_tag_list_no_id: Omit[] = [ queryString: "view", }, ], - query_nav_list = nav_tag_list_no_id.map((item) => ({ + video_query_nav_list = nav_tag_list_no_id.map((item) => ({ ...item, id: nanoid(3), cancelable: false, - })) as NavQueryItemType[]; -const router_list: Omit[] = [ - { - type: "router", - pathname: "photo", - name: "图片", - }, - { - type: "router", - pathname: "video", - name: "视频", - }, -]; -export const router_nav_list = router_list.map((item) => ({ - ...item, - id: nanoid(3), - cancelable: false, -})) as NavRouterItemType[]; + })) as VideoNavQueryItemType[]; + +export const VideoQueryNavList = video_query_nav_list; diff --git a/src/routers/layout/nav/index.tsx b/src/routers/layout/nav/index.tsx index d17645d..1375210 100644 --- a/src/routers/layout/nav/index.tsx +++ b/src/routers/layout/nav/index.tsx @@ -18,18 +18,45 @@ import { restrictToParentElement } from "@dnd-kit/modifiers"; import styles from "./nav.module.less"; import { FC, useMemo, memo } from "react"; import { Flipped } from "react-flip-toolkit"; -import { NavQueryItemType, useNavList, NavStorage } from "./tools"; +import { + VideoNavQueryItemType, + useVideoNavList, + VideoNavStorage, +} from "./VideoTools"; import { useAppSelector, useAppDispatch } from "@store/hooks"; import { changeNavMoreShowed, selectNavMoreShowed } from "@store/device/index"; import { getVersion } from "@utils/index"; -import { selectVideoLoadingState } from "@store/loading/index"; import { - handerAddTag, - handerDeleteTag, - selectActiveTags, + selectPhotoLoadingState, + selectVideoLoadingState, +} from "@store/loading/index"; +import { useLocation } from "react-router-dom"; +import { + PhotoNavQueryItemType, + PhotoNavStorage, + usePhotoNavList, +} from "./photoTools"; +import { + handerPhotoAddTag, + handerPhotoDeleteTag, + handerVideoAddTag, + handerVideoDeleteTag, + selectPhotoActiveTags, + selectVideoActiveTags, } from "@store/tags/index"; + +function useSelectList() { + const { pathname } = useLocation(), + flag = pathname === "/photo", + storageSelect = flag ? PhotoNavStorage : VideoNavStorage; + const PhotoHook = usePhotoNavList(), + VideoHook = useVideoNavList(); + const [navLists, setLists] = flag ? PhotoHook : VideoHook; + return { navLists, setLists, storageSelect }; +} + export default function Header_Nav() { - const [navLists, setLists] = useNavList(); + const { navLists, setLists, storageSelect } = useSelectList(); //tag区是否展开 const showed = useAppSelector(selectNavMoreShowed); //拖拽事件绑定 @@ -53,11 +80,11 @@ export default function Header_Nav() { function handleDragEnd(event: DragEndEvent) { const { active, over } = event; if (over && active.id !== over.id) { - setLists((items) => { + setLists((items: any[]) => { const oldIndex = items.findIndex((item) => item.id === active.id), newIndex = items.findIndex((item) => item.id === over.id), newLists = arrayMove(items, oldIndex, newIndex); - NavStorage.setLocalstorage({ + storageSelect.setLocalstorage({ version: getVersion(), res: newLists, }); @@ -69,7 +96,7 @@ export default function Header_Nav() { () => ( <> {navLists.map((item) => ( - + ))} ), @@ -93,7 +120,9 @@ export default function Header_Nav() { }} data-showed={showed} > - {ComRes} + + {ComRes} + @@ -117,7 +146,7 @@ const NavInViewItem = () => { width: "1px", }} > - +
dispatch(changeNavMoreShowed())} @@ -136,58 +165,78 @@ const NavInViewItem = () => { ); }; -const NavItem: FC = memo((props) => { - const { attributes, listeners, setNodeRef, transform, transition } = - useSortable({ id: props.id }); - const style = { - transform: CSS.Translate.toString(transform), - transition, - }; - return ( - -
- -
-
- ); -}); +const NavItem: FC = memo( + (props) => { + const { attributes, listeners, setNodeRef, transform, transition } = + useSortable({ id: props.id as string }); + const style = { + transform: CSS.Translate.toString(transform), + transition, + }; + return ( + +
+ +
+
+ ); + } +); -const NavTagChipItem: FC = memo((props) => { - const clicked = useAppSelector(selectActiveTags).some( - (item) => item.id === props.id - ), +const useTagSelect = () => { + const { pathname } = useLocation(), + flag = pathname === "/photo", + tag_lists = flag + ? useAppSelector(selectPhotoActiveTags) + : useAppSelector(selectVideoActiveTags), + loading = flag + ? useAppSelector(selectPhotoLoadingState) + : useAppSelector(selectVideoLoadingState), dispatch = useAppDispatch(), - handerclick = () => { + addTagFunc = flag ? handerPhotoAddTag : handerVideoAddTag, + deleteFunc = flag ? handerPhotoDeleteTag : handerVideoDeleteTag; + + return { tag_lists, loading, dispatch, addTagFunc, deleteFunc }; +}; + +const NavTagChipItem: FC = memo( + (props) => { + const { tag_lists, loading, dispatch, addTagFunc, deleteFunc } = + useTagSelect(); + const clicked = tag_lists.some((item) => item.query === props.query); + const handerclick = () => { if (clicked) { - dispatch(handerDeleteTag(props)); + //@ts-ignore + dispatch(deleteFunc(props)); } else { - dispatch(handerAddTag(props)); + //@ts-ignore + dispatch(addTagFunc(props)); } - }, - isVideoFetching = useAppSelector(selectVideoLoadingState), + }; //@ts-ignore - color = nameToColor[props.query] || "info"; - return ( - - ); -}); + const color = nameToColor[props.query] || "info"; + return ( + + ); + } +); const nameToColor = { 露早: "luzaoRed", diff --git a/src/routers/layout/nav/photoTools.ts b/src/routers/layout/nav/photoTools.ts new file mode 100644 index 0000000..7b12cc4 --- /dev/null +++ b/src/routers/layout/nav/photoTools.ts @@ -0,0 +1,108 @@ +import { nanoid } from "nanoid"; +import { useMemo, useState } from "react"; +import { Storage } from "../tools"; +import { getVersion } from "@utils/index"; +import { IFetchPhotoParams } from "@utils/fetch/fetchtype"; + +export interface PhotoNavStorage { + version: string; + res: PhotoNavQueryItemType[]; +} +export const PhotoNavStorage = new Storage("photonavTagLists"); + +export function usePhotoNavList(): [ + PhotoNavQueryItemType[], + React.Dispatch> +] { + const nav_ok_lists = useMemo(() => { + const local_lists = PhotoNavStorage.getLocalStorage({ + version: getVersion(), + res: [], + } as PhotoNavStorage); + if (!local_lists.version || local_lists.res.length < 2) { + return photo_query_nav_list; + } + //todo change logi + if (parseFloat(local_lists.version) < parseFloat(getVersion())) { + return photo_query_nav_list; + } + return local_lists.res; + }, []); + const [navLists, setLists] = useState(nav_ok_lists); + return [navLists, setLists]; +} + +export type PhotoTopicType = { + id: String; + type: "photo-selected"; + query: string; + queryType: "topic_id"; + queryString: IFetchPhotoParams["topic_id"]; +}; +export type PhotoSearchType = { + id: String; + type: "photo-selected"; + query: string; + queryType: "type"; + queryString: IFetchPhotoParams["type"]; +}; + +export type PhotoNavQueryItemType = PhotoTopicType | PhotoSearchType; + +const nav_tag_list_no_id: Pick< + PhotoNavQueryItemType, + "query" | "queryString" | "queryType" + >[] = [ + { + query: "最新图片", + queryType: "type", + queryString: "latest", + }, + { + query: "推荐图片", + queryType: "type", + queryString: "recommend", + }, + { + query: "全部", + queryType: "topic_id", + queryString: 0, + }, + { + query: "露早", + queryType: "topic_id", + queryString: 29067608, + }, + { + query: "柚恩", + queryType: "topic_id", + queryString: 28950030, + }, + { + query: "莞儿", + queryType: "topic_id", + queryString: 28953983, + }, + { + query: "米诺", + queryType: "topic_id", + queryString: 29069147, + }, + { + query: "虞莫", + queryType: "topic_id", + queryString: 28948378, + }, + { + query: "EOE组合", + queryType: "topic_id", + queryString: 29156150, + }, + ], + photo_query_nav_list = nav_tag_list_no_id.map((item) => ({ + ...item, + id: nanoid(3), + type: "photo-selected", + })) as PhotoNavQueryItemType[]; + +export const PhotoQueryNavList = photo_query_nav_list; diff --git a/src/routers/layout/routernav/index.tsx b/src/routers/layout/routernav/index.tsx index 0aa1f0d..88172e4 100644 --- a/src/routers/layout/routernav/index.tsx +++ b/src/routers/layout/routernav/index.tsx @@ -1,29 +1,61 @@ -import { Button, ButtonGroup, Stack } from "@mui/material"; -import { useMemo } from "react"; -import { NavLink } from "react-router-dom"; -import { useRouterList } from "./tools"; +import { Tab, Tabs } from "@mui/material"; +import { useState, useEffect, useCallback, memo } from "react"; +import { Link, useLocation } from "react-router-dom"; +import { useScreenSize } from "@components/proview/screenSize"; +import MenuRouter from "./menu"; + +export const RouterList: TabProps[] = [ + { + label: "视频", + to: "/video", + }, + { + label: "图片", + to: "/photo", + }, +]; export default function RouterNav() { - const routerlists = useRouterList(), - routerRes = useMemo( - () => - routerlists.map((item) => ( - - - - )), - [routerlists] - ); + const { pathname } = useLocation(), + handler = () => (pathname === "/photo" ? 1 : 0); + const [value, set] = useState(handler), + changehandler = useCallback(() => set(handler), [handler]); + useEffect(() => { + changehandler(); + }, [pathname]); + const { sm } = useScreenSize(); + return ( + <> + {sm ? ( + + ) : ( + + {RouterList.map((item, index) => ( + + ))} + + )} + + ); +} +export type TabProps = { + label: string; + to: string; +}; +export function TabLink(props: TabProps) { + const { pathname } = useLocation(); return ( - - - {routerRes} - - + ); } diff --git a/src/routers/layout/routernav/menu.tsx b/src/routers/layout/routernav/menu.tsx new file mode 100644 index 0000000..3bb2b2d --- /dev/null +++ b/src/routers/layout/routernav/menu.tsx @@ -0,0 +1,41 @@ +import { Menu, Button, MenuItem } from "@mui/material"; +import { Omit } from "@utils/index"; +import PopupState, { bindTrigger, bindMenu } from "material-ui-popup-state"; +import { RouterList, TabProps, TabLink } from "."; +export default function MenuRouter() { + return ( + + {(popupState) => ( + <> + + + {RouterList.map((item, index) => ( + + ))} + + + )} + + ); +} +function RouterItem(props: TabProps & { onClick: () => void }) { + return ( + + + + ); +} diff --git a/src/routers/layout/routernav/tools.ts b/src/routers/layout/routernav/tools.ts deleted file mode 100644 index 401cab2..0000000 --- a/src/routers/layout/routernav/tools.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { NavRouterItemType, router_nav_list } from "../nav/tools"; -import { useState } from "react"; -export function useRouterList(): NavRouterItemType[] { - const [list, _] = useState(router_nav_list); - return list; -} diff --git a/src/routers/layout/tools.ts b/src/routers/layout/tools.ts index 6a3dfb3..66bc376 100644 --- a/src/routers/layout/tools.ts +++ b/src/routers/layout/tools.ts @@ -5,7 +5,7 @@ export class Storage { } private getStorage(Storagename: globalThis.Storage, defaultRes: T): T { if (!Storagename) { - return Storagename; + return defaultRes; } if (!Storagename.getItem(this.itemName)) { Storagename.setItem(this.itemName, JSON.stringify(defaultRes)); @@ -28,4 +28,7 @@ export class Storage { setSessionStorage(targetRes: T): void { this.setStorage(sessionStorage, targetRes); } + clearLocalstorage(): void { + localStorage.removeItem(this.itemName); + } } diff --git a/src/routers/photo.tsx b/src/routers/photo.tsx deleted file mode 100644 index c036aca..0000000 --- a/src/routers/photo.tsx +++ /dev/null @@ -1,11 +0,0 @@ -export default function PhotoPage() { - return ( -

- 🙇‍♂️🙇‍♂️🙇‍♂️二创图片页还在制作中,敬请期待! -

- ); -} diff --git a/src/routers/photo/index.tsx b/src/routers/photo/index.tsx new file mode 100644 index 0000000..38b5904 --- /dev/null +++ b/src/routers/photo/index.tsx @@ -0,0 +1,23 @@ +import Masonry from "./masonry"; +import { useAppSelector } from "@store/hooks"; +import { selectPhotoActiveTags } from "@store/tags"; +import { + PhotoSearchType, + PhotoTopicType, +} from "@routers/layout/nav/photoTools"; + +export default function PhotoPage() { + const activeTags = useAppSelector(selectPhotoActiveTags), + typeitem = activeTags.find( + (item) => item.queryType === "type" + )! as PhotoSearchType, + topicItem = activeTags.find( + (item) => item.queryType === "topic_id" + ) as PhotoTopicType; + + return ( + <> + + + ); +} diff --git a/src/routers/photo/item/index.tsx b/src/routers/photo/item/index.tsx new file mode 100644 index 0000000..d535e40 --- /dev/null +++ b/src/routers/photo/item/index.tsx @@ -0,0 +1,83 @@ +import { PhotoRouterImageCardType } from "../phototype"; +import { useState } from "react"; +import ImgModals from "./modal"; +import ChevronRightIcon from "@mui/icons-material/ChevronRight"; +import { Link, styled } from "@mui/material"; +import style from "./photo.module.less"; +import { ImageBasic } from "@components/image"; +import { Omit } from "@utils/index"; +import CollectionsIcon from "@mui/icons-material/Collections"; +type CardType = { + data: PhotoRouterImageCardType; +}; + +export default function PhotoCard(props: CardType) { + const { images, dynamic_id, ...resPorps } = props.data; + const [open, set] = useState(false), + handlerChangeOpen = () => set((open) => !open); + const modalPorps = { open, images, onClose: handlerChangeOpen }; + return ( +
+ + + + + + + + {images.length > 1 && ( + + + {images.length} + + )} +
+ ); +} + +const DivImgNum = styled("div")(({ theme }) => ({ + position: "absolute", + right: "0px", + top: "0px", + padding: "7px", + color: "white", + backgroundColor: "rgba(0, 0, 0, 0.4)", + display: "flex", + justifyContent: "center", + alignItems: "center", + columnGap: "2px", + pointerEvents: "none", + fontSize: "14px", +})); + +const DivJump = styled("div")(({ theme }) => ({ + borderBottomLeftRadius: "8px", + borderBottomRightRadius: "8px", + position: "absolute", + bottom: "0px", + width: "100%", + backgroundImage: `linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(90, 90,90, 0.8) 100%)`, + "&:hover": { + backgroundImage: `linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0,0, 0.8) 100%)`, + }, + [theme.breakpoints.down("sm")]: { + borderBottomLeftRadius: "4px", + borderBottomRightRadius: "4px", + }, +})); diff --git a/src/routers/photo/item/modal.tsx b/src/routers/photo/item/modal.tsx new file mode 100644 index 0000000..abc3f09 --- /dev/null +++ b/src/routers/photo/item/modal.tsx @@ -0,0 +1,39 @@ +import { basicImageType } from "../phototype"; +import { PhotoSlider } from "react-photo-view"; +import "react-photo-view/dist/react-photo-view.css"; +import { useEffect } from "react"; +import { useLocation } from "react-router-dom"; +type ModalType = { + images: basicImageType[]; + open: boolean; + onClose: () => void; +}; + +export default function ImgModals(props: ModalType) { + const { open, onClose, images } = props; + const location = useLocation(); + useEffect(() => { + let flag = false; + const handler = () => { + flag = true; + onClose(); + }; + if (open) { + history.pushState(null, "", location.pathname); + window.addEventListener("popstate", handler, { + once: true, + }); + return () => { + !flag && history.back(); + window.removeEventListener("popstate", handler); + }; + } + }, [open]); + return ( + ({ src: item.src, key: index }))} + visible={open} + onClose={onClose} + /> + ); +} diff --git a/src/routers/photo/item/photo.module.less b/src/routers/photo/item/photo.module.less new file mode 100644 index 0000000..06a560e --- /dev/null +++ b/src/routers/photo/item/photo.module.less @@ -0,0 +1,8 @@ +.card { + position: relative; + border-radius: 4px; + + .show-img { + min-height: 150px; + } +} \ No newline at end of file diff --git a/src/routers/photo/masonry.tsx b/src/routers/photo/masonry.tsx new file mode 100644 index 0000000..7cf9eed --- /dev/null +++ b/src/routers/photo/masonry.tsx @@ -0,0 +1,83 @@ +import { selectPhotoLoadingState } from "@store/loading"; +import { Masonry as Masonic_masonry } from "masonic"; +import { useState, useEffect } from "react"; +import { useAppSelector, useAppDispatch } from "@store/hooks"; + +import { changeLoading } from "@store/loading/index"; +import { fetchPhotoHandler } from "./tools"; +import { PhotoRouterImageCardType, PhotoRouterMasonryType } from "./phototype"; +import PhotoCard from "./item"; +import { useScreenSize } from "@components/proview/screenSize"; +import message from "@components/message"; +import styles from "./photo.module.less"; +//todo props type change +export default function Masonry(props: PhotoRouterMasonryType) { + const [lists, setList] = useState([]); + const isLoading = useAppSelector(selectPhotoLoadingState), + dispatch = useAppDispatch(), + handerChangeLoading = (state: boolean) => + dispatch( + changeLoading({ + stateName: "photoIsloading", + Tostate: state, + }) + ); + useEffect(() => { + const fetchHandler = async (page: number = 0) => { + const data = await fetchPhotoHandler({ + page, + ...props, + }); + setList((lists) => { + return [ + ...lists, + ...data.map((item, index) => { + const itemRes = { + dynamic_id: item.dynamic_id, + images: item.pictures.map((img) => ({ + src: img.img_src, + height: img.img_height, + width: img.img_width, + })), + }; + if (data.length < 3) { + message.info("没有更多图片了,尝试使用其他tag吧!"); + } + if (index === data.length - 3) { + return { + ...itemRes, + observer: true, + callback: (inView: boolean) => { + fetchHandler(page + 1); + }, + }; + } + return itemRes; + }), + ]; + }); + }; + setList(() => []); + handerChangeLoading(true); + fetchHandler(1).then(() => handerChangeLoading(false)); + }, [props]); + const { sm, md } = useScreenSize(), + minCount = sm ? { columnCount: 2 } : {}; + return ( +
+ {isLoading || ( + + )} +
+ ); +} diff --git a/src/routers/photo/photo.module.less b/src/routers/photo/photo.module.less new file mode 100644 index 0000000..2cf7492 --- /dev/null +++ b/src/routers/photo/photo.module.less @@ -0,0 +1,10 @@ +.container:empty { + display: flex; + justify-content: center; + + &::before { + margin-top: 50px; + content: "暂无数据"; + color: #c1c1c1; + } +} \ No newline at end of file diff --git a/src/routers/photo/phototype.ts b/src/routers/photo/phototype.ts new file mode 100644 index 0000000..7adbae2 --- /dev/null +++ b/src/routers/photo/phototype.ts @@ -0,0 +1,17 @@ +import { IFetchPhotoParams } from "@utils/fetch/fetchtype"; +export type basicImageType = { + src: string; + width: number; + height: number; +}; +/** + * 定义photo路由的类型 + */ +export type PhotoRouterImageCardType = { + images: basicImageType[]; + dynamic_id: number; + observer?: boolean; + callback?: (inView: boolean) => void; +}; + +export type PhotoRouterMasonryType = Omit; diff --git a/src/routers/photo/tools.ts b/src/routers/photo/tools.ts new file mode 100644 index 0000000..6de1a6b --- /dev/null +++ b/src/routers/photo/tools.ts @@ -0,0 +1,16 @@ +import message from "@components/message"; +import { fetchPhotos } from "@utils/fetch"; +import { IFetchPhotoParams } from "@utils/fetch/fetchtype"; + +export const fetchPhotoHandler = async (params: IFetchPhotoParams) => { + const res = await fetchPhotos(params); + if (res.code !== 0) { + res.code === 400 && message.info("参数错误,请尝试其他tag"); + res.code === 500 && message.info(res.message); + return []; + } else if (res.data.result == null || res.data.result.length < 1) { + message.info("没有更多数据了,请尝试其他tag"); + return []; + } + return res.data.result; +}; diff --git a/src/routers/video/index.tsx b/src/routers/video/index.tsx index a543f38..1ddc83d 100644 --- a/src/routers/video/index.tsx +++ b/src/routers/video/index.tsx @@ -1,12 +1,12 @@ import { useAppSelector } from "@store/hooks"; -import { selectActiveTags } from "@store/tags"; +import { selectVideoActiveTags } from "@store/tags"; import VideoMasonry from "./masonry"; import { VideoRouterMasonryType } from "./videotype"; type ValueOf = T[keyof T]; export default function VideoPage() { //处理搜索条件 - const activeTags = useAppSelector(selectActiveTags), + const activeTags = useAppSelector(selectVideoActiveTags), tname = activeTags.find((item) => item.queryType === "tname") ?.queryString as ValueOf>, copyright = activeTags.find((item) => item.queryType === "copyright") diff --git a/src/routers/video/item/index.tsx b/src/routers/video/item/index.tsx index 30efc7b..add8b4b 100644 --- a/src/routers/video/item/index.tsx +++ b/src/routers/video/item/index.tsx @@ -1,4 +1,5 @@ import { ImageBasic } from "@components/image"; +import { useScreenSize } from "@components/proview/screenSize"; import { FC } from "react"; import { Link } from "@mui/material"; import { VideoRouterImageCardType } from "../videotype"; @@ -11,6 +12,7 @@ export const VideoRouterImageCard: FC<{ data: VideoRouterImageCardType }> = ({ data, }) => { const { pic, bvid } = data; + const { md } = useScreenSize(); return (
= ({ color='inherit' > > = (props) => { diff --git a/src/routers/video/masonry.tsx b/src/routers/video/masonry.tsx index 93e45f9..5cf5f3f 100644 --- a/src/routers/video/masonry.tsx +++ b/src/routers/video/masonry.tsx @@ -1,12 +1,11 @@ import { useState, useEffect, FC, memo } from "react"; import { VideoRouterImageCardType, VideoRouterMasonryType } from "./videotype"; import { Unstable_Grid2 as Grid } from "@mui/material"; -import ImageShouldResizeProview from "@components/proview/imageSize"; import { VideoRouterImageCard } from "./item"; import { Skeleton } from "@mui/material"; import { nanoid } from "nanoid"; import styles from "./video.module.less"; -import { fetchVideohnadler, PickVideoRouterImageCardType } from "./tools"; +import { fetchVideohandler, PickVideoRouterImageCardType } from "./tools"; import { useAppSelector, useAppDispatch } from "@store/hooks"; import { changeLoading, selectVideoLoadingState } from "@store/loading/index"; /** @@ -23,7 +22,7 @@ export default function VideoMasonry(props: VideoRouterMasonryType) { useEffect(() => { // 在内部定义fetchHandler,保证拿到的是同步的 const fetchHandler = async (page: number = 1) => { - const data = await fetchVideohnadler(page, props); + const data = await fetchVideohandler(page, props); setLists((lists) => [ ...lists, ...data.map((item, index) => { @@ -48,30 +47,28 @@ export default function VideoMasonry(props: VideoRouterMasonryType) { }, [props]); return (
- - - {lists.map((item) => ( - - ))} - {isLoading && } - - + + {lists.map((item) => ( + + ))} + {isLoading && } +
); } diff --git a/src/routers/video/tools.ts b/src/routers/video/tools.ts index f69a641..2f3f79e 100644 --- a/src/routers/video/tools.ts +++ b/src/routers/video/tools.ts @@ -3,7 +3,7 @@ import { fetchVideos } from "@utils/fetch/index"; import message from "@components/message"; import { Pick } from "@utils/index"; -export const fetchVideohnadler = async ( +export const fetchVideohandler = async ( page: number = 1, props: VideoRouterMasonryType ) => { diff --git a/src/routers/video/videotype.ts b/src/routers/video/videotype.ts index ce56040..cb4f14f 100644 --- a/src/routers/video/videotype.ts +++ b/src/routers/video/videotype.ts @@ -3,7 +3,7 @@ */ import { ImageProps } from "@components/image/imagetype"; import { RFetchVideoRes } from "@utils/fetch/fetchtype"; -import { IFetchVideoParams } from "../../utils/fetch/fetchtype"; +import { IFetchVideoParams } from "@utils/fetch/fetchtype"; /** * @description video路由瀑布流卡片参数 diff --git a/src/store/loading/index.ts b/src/store/loading/index.ts index b220a74..e26d5c9 100644 --- a/src/store/loading/index.ts +++ b/src/store/loading/index.ts @@ -2,11 +2,15 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import type { RootState } from ".."; -interface LoadingState { +export interface LoadingState { /** * @description 视频接口是否正在获取 */ videoIsLoading: boolean; + /** + * @description 图片接口是否正在获取 + */ + photoIsloading: boolean; } interface IchangeLoadingItem { stateName: keyof LoadingState; @@ -15,13 +19,14 @@ interface IchangeLoadingItem { const initialState: LoadingState = { videoIsLoading: true, + photoIsloading: true, }; export const LoadingSlice = createSlice({ name: "loading", initialState, reducers: { /** - * + * @description 修改加载状态,注意因为spa的限制所有每个页面只有一种加载情况 */ changeLoading: (state, action: PayloadAction) => { const { stateName, Tostate } = action.payload; @@ -31,11 +36,28 @@ export const LoadingSlice = createSlice({ state[stateName] = !state[stateName]; } }, + /** + * @description 此reducer应该只在url修改时调用 + */ + changeLoadingCauseByUrl: ( + state, + action: PayloadAction + ) => { + Object.keys(state).map((name) => { + state[name as keyof LoadingState] = + name === action.payload.stateName ? true : false; + }); + }, }, }); -export const { changeLoading } = LoadingSlice.actions; +const selectLoadingState = (state: RootState, name: keyof LoadingState) => + state.loading[name]; + +export const { changeLoading, changeLoadingCauseByUrl } = LoadingSlice.actions; export const selectVideoLoadingState = (state: RootState) => - state.loading.videoIsLoading; + selectLoadingState(state, "videoIsLoading"), + selectPhotoLoadingState = (state: RootState) => + selectLoadingState(state, "photoIsloading"); export default LoadingSlice.reducer; diff --git a/src/store/tags/index.ts b/src/store/tags/index.ts index 567581f..803ef06 100644 --- a/src/store/tags/index.ts +++ b/src/store/tags/index.ts @@ -1,51 +1,124 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; -import { NavQueryItemType } from "@routers/layout/nav/tools"; - +import { + VideoNavQueryItemType, + VideoQueryNavList, +} from "@routers/layout/nav/VideoTools"; +import { + PhotoNavQueryItemType, + PhotoQueryNavList, +} from "@routers/layout/nav/photoTools"; import type { RootState } from ".."; +const Photo_Topic_id_all = PhotoQueryNavList.find( + (item) => item.queryString === 0 + )!, + Photo_type_default = PhotoQueryNavList.find( + (item) => item.queryString === "latest" + )!, + Video_default_show = VideoQueryNavList.find( + (item) => item.queryString === "pubdate" + )!; + interface TagStates { /** - * @description 用户点击的tag栏 + * @description video页面点击tags + */ + VideoActiveTags: VideoNavQueryItemType[]; + /** + * @description photo page active tags */ - activeTags: NavQueryItemType[]; + PhotoActivetags: PhotoNavQueryItemType[]; } const initialState: TagStates = { - activeTags: [], + VideoActiveTags: [Video_default_show], + PhotoActivetags: [Photo_Topic_id_all, Photo_type_default], }; export const ActiveTagsSlice = createSlice({ - name: "activeTags", + name: "VideoActiveTags", initialState, reducers: { /** - * @description 添加tag + * @description video页面添加tag */ - handerAddTag: (state, action: PayloadAction) => { - //这里需要注意的是queryType有三种,只有q可以同存. + handerVideoAddTag: ( + state, + action: PayloadAction + ) => { + //这里需要注意的是queryType多种,只有q可以同存. switch (action.payload.queryType) { case "q": break; default: - state.activeTags = state.activeTags.filter( + state.VideoActiveTags = state.VideoActiveTags.filter( (item) => item.queryType !== action.payload.queryType ); break; } - state.activeTags = [...state.activeTags, action.payload]; + state.VideoActiveTags = [...state.VideoActiveTags, action.payload]; + }, + /** + * @description photo page add activeTags + */ + handerPhotoAddTag: ( + state, + action: PayloadAction + ) => { + state.PhotoActivetags = state.PhotoActivetags.filter( + (item) => item.queryType !== action.payload.queryType + ); + state.PhotoActivetags.push(action.payload); }, /** - * @description 删除tag + * @description video页面删除tag */ - handerDeleteTag: (state, action: PayloadAction) => { - state.activeTags = state.activeTags.filter( + handerVideoDeleteTag: ( + state, + action: PayloadAction + ) => { + state.VideoActiveTags = state.VideoActiveTags.filter( (item) => item.id !== action.payload.id ); }, + /** + * @description photo page delete activetags + */ + handerPhotoDeleteTag: ( + state, + action: PayloadAction + ) => { + state.PhotoActivetags = state.PhotoActivetags.filter( + (item) => item.id !== action.payload.id + ); + if ( + !state.PhotoActivetags.some( + (item) => item.queryType === action.payload.queryType + ) + ) { + switch (action.payload.queryType) { + case "topic_id": { + state.PhotoActivetags.push(Photo_Topic_id_all); + break; + } + default: { + state.PhotoActivetags.push(Photo_type_default); + break; + } + } + } + }, }, }); -export const { handerAddTag, handerDeleteTag } = ActiveTagsSlice.actions; +export const { + handerVideoAddTag, + handerVideoDeleteTag, + handerPhotoAddTag, + handerPhotoDeleteTag, +} = ActiveTagsSlice.actions; -export const selectActiveTags = (state: RootState) => - state.ActiveTags.activeTags; +export const selectVideoActiveTags = (state: RootState) => + state.ActiveTags.VideoActiveTags, + selectPhotoActiveTags = (state: RootState) => + state.ActiveTags.PhotoActivetags; export default ActiveTagsSlice.reducer; diff --git a/src/utils/fetch/fetchtype.ts b/src/utils/fetch/fetchtype.ts index 90afbe4..6d05df3 100644 --- a/src/utils/fetch/fetchtype.ts +++ b/src/utils/fetch/fetchtype.ts @@ -165,10 +165,8 @@ interface RFetchVideoResData { numResults: number; result: RFetchVideoResResult[]; } -/** - * @description video接口的返回类型 - */ -export interface RFetchVideoRes { + +type CustomFetchRes = { /** * @example 0 */ @@ -184,5 +182,61 @@ export interface RFetchVideoRes { /** * @description 放数据的 */ - data: RFetchVideoResData; + data: T; +}; +/** + * @description video接口的返回类型 + */ +export type RFetchVideoRes = CustomFetchRes; + +interface RFetchPhotoResPicturesArr { + /** + * @description img url src + */ + img_src: string; + img_size: number; + /** + * @description img origin width + */ + img_width: number; + /** + * @description img origin height + */ + img_height: number; } + +interface RFetchPhotoResData { + page: number; + total: number; + result: { + dynamic_id: number; + sent_at: number; + pictures: RFetchPhotoResPicturesArr[]; + }[]; +} + +/** + * @description photo接口的返回类型 + */ +export type RFetchPhotoRes = CustomFetchRes; + +/** + * + */ +export type IFetchPhotoParams = { + /** + * @description photo display rules + */ + type: "recommend" | "latest"; + /** + * @description topic recommend:28953983->莞儿 + 28950030->柚恩 + 29067608->露早 + 28948378->虞莫 + 29069147->猴子 + 29156150->官号 + 0 -> default,all + */ + topic_id: 28953983 | 28950030 | 29067608 | 28948378 | 29069147 | 29156150 | 0; + page: number; +}; diff --git a/src/utils/fetch/index.ts b/src/utils/fetch/index.ts index 27d0582..30b2043 100644 --- a/src/utils/fetch/index.ts +++ b/src/utils/fetch/index.ts @@ -1,13 +1,29 @@ import axios from "axios"; +import JSONBigInt from "json-bigint"; /** * 类型文件导入 */ -import { IFetchVideoParams, RFetchVideoRes } from "./fetchtype"; +import { + IFetchVideoParams, + RFetchPhotoRes, + RFetchVideoRes, + IFetchPhotoParams, +} from "./fetchtype"; import { Host_Url } from "./tool"; +import { Omit } from "../index"; export const BackEndAxios = axios.create({ baseURL: Host_Url, - timeout: 2500, + timeout: 9000, + transformResponse: [ + (data) => { + try { + return JSONBigInt.parse(data); + } catch (err) { + return data; + } + }, + ], }); /** * 后端接口配置 @@ -28,7 +44,7 @@ export async function fetchVideos( params: IFetchVideoParams ): Promise { try { - const res = await BackEndAxios.get("/v1/video-interface/advanced-search", { + const res = await BackEndAxios.get("/video-interface/advanced-search", { params: { order: params.order || "score", page: params.page, @@ -52,3 +68,28 @@ export async function fetchVideos( }; } } +/** + * photo图片数据获取接口 + */ +export async function fetchPhotos( + params: IFetchPhotoParams +): Promise { + // console.log({ params }); + try { + const res = await BackEndAxios.get(`/pic/${params.type}`, { + params: Omit(params, "type"), + }); + return res.data; + } catch (e) { + return { + code: 500, + message: "网络请求错误,您似乎处于断网状态", + ttl: 0, + data: { + page: 0, + total: 0, + result: [], + }, + }; + } +} diff --git a/src/utils/fetch/tool.ts b/src/utils/fetch/tool.ts index 1e1d51b..ff6aa87 100644 --- a/src/utils/fetch/tool.ts +++ b/src/utils/fetch/tool.ts @@ -1 +1 @@ -export const Host_Url = isdev ? "" : `https://api.eoe.best/eoefans-api`; +export const Host_Url = isdev ? "/v1" : `https://api.eoe.best/eoefans-api/v1`; diff --git a/src/utils/router/index.tsx b/src/utils/router/index.tsx new file mode 100644 index 0000000..dbc45d7 --- /dev/null +++ b/src/utils/router/index.tsx @@ -0,0 +1,11 @@ +import type { LoadingState } from "@store/loading"; +type RouterConfig = { + [router: string]: keyof LoadingState; +}; +const RouterMap: RouterConfig = { + default: "videoIsLoading", + "/photo": "photoIsloading", + "/video": "videoIsLoading", +}; +export const routerNameToLoading = (pathname: string) => + RouterMap[pathname] || RouterMap["default"]; diff --git a/vite.config.ts b/vite.config.ts index 31cd616..9bb3ac2 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -8,7 +8,7 @@ import viteCompression from "vite-plugin-compression"; // polyfill import legacy from "@vitejs/plugin-legacy"; // https://vitejs.dev/config/ - +//@ts-ignore export default defineConfig(({ mode }) => { return { define: { @@ -45,6 +45,7 @@ export default defineConfig(({ mode }) => { react: ["react", "react-dom"], "react-router": ["react-router-dom"], "react-redux": ["@reduxjs/toolkit", "react-redux"], + axios: ["axios"], lib: [ "@mui/icons-material", "@mui/lab", @@ -52,6 +53,7 @@ export default defineConfig(({ mode }) => { "@emotion/react", "@emotion/styled", ], + transform: ["json-bigint"], "dnd-tool": [ "@dnd-kit/core", "@dnd-kit/modifiers",