diff --git a/package.json b/package.json index 0a145c9..8a09873 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@mui/icons-material": "^5.11.0", "@mui/lab": "5.0.0-alpha.114", "@mui/material": "^5.11.3", + "@reduxjs/toolkit": "^1.9.1", "dayjs": "^1.11.7", "imagesloaded": "^5.0.0", "intersection-observer": "^0.12.2", @@ -32,6 +33,7 @@ "react-flip-toolkit": "^7.0.17", "react-intersection-observer": "^9.4.1", "react-photo-view": "^1.2.3", + "react-redux": "^8.0.5", "react-router-dom": "^6.6.1", "react-use": "^17.4.0", "whatwg-fetch": "^3.6.2" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 20f946a..dfbf289 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,6 +12,7 @@ specifiers: '@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/node': ^18.11.18 '@types/react': ^18.0.26 @@ -33,6 +34,7 @@ specifiers: react-flip-toolkit: ^7.0.17 react-intersection-observer: ^9.4.1 react-photo-view: ^1.2.3 + react-redux: ^8.0.5 react-router-dom: ^6.6.1 react-use: ^17.4.0 rollup-plugin-visualizer: ^5.9.0 @@ -52,6 +54,7 @@ dependencies: '@mui/icons-material': 5.11.0_oiuuhmk4wjjpe4qb2sby7usney '@mui/lab': 5.0.0-alpha.114_k43z3ghfbkgbsmpaivfebys3bq '@mui/material': 5.11.3_lskpmcsdi7ipu6qpuapyu56ihm + '@reduxjs/toolkit': 1.9.1_k4ae6lp43ej6mezo3ztvx6pykq dayjs: 1.11.7 imagesloaded: 5.0.0 intersection-observer: 0.12.2 @@ -65,6 +68,7 @@ dependencies: react-flip-toolkit: 7.0.17_biqbaboplfbrettd7655fr4n2y react-intersection-observer: 9.4.1_react@18.2.0 react-photo-view: 1.2.3_biqbaboplfbrettd7655fr4n2y + react-redux: 8.0.5_ie75ejlwqy5zh3tldgt7pftwcu react-router-dom: 6.6.1_biqbaboplfbrettd7655fr4n2y react-use: 17.4.0_biqbaboplfbrettd7655fr4n2y whatwg-fetch: 3.6.2 @@ -1018,6 +1022,25 @@ packages: react: 18.2.0 dev: false + /@reduxjs/toolkit/1.9.1_k4ae6lp43ej6mezo3ztvx6pykq: + resolution: {integrity: sha512-HikrdY+IDgRfRYlCTGUQaiCxxDDgM1mQrRbZ6S1HFZX5ZYuJ4o8EstNmhTwHdPl2rTmLxzwSu0b3AyeyTlR+RA==} + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 + react-redux: ^7.2.1 || ^8.0.2 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + dependencies: + immer: 9.0.17 + react: 18.2.0 + react-redux: 8.0.5_ie75ejlwqy5zh3tldgt7pftwcu + redux: 4.2.0 + redux-thunk: 2.4.2_redux@4.2.0 + reselect: 4.1.7 + dev: false + /@remix-run/router/1.2.1: resolution: {integrity: sha512-XiY0IsyHR+DXYS5vBxpoBe/8veTeoRpMHP+vDosLZxL5bnpetzI0igkxkLZS235ldLzyfkxF+2divEwWHP3vMQ==} engines: {node: '>=14'} @@ -1135,6 +1158,13 @@ packages: '@swc/core-win32-x64-msvc': 1.3.24 dev: true + /@types/hoist-non-react-statics/3.3.1: + resolution: {integrity: sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==} + dependencies: + '@types/react': 18.0.26 + hoist-non-react-statics: 3.3.2 + dev: false + /@types/imagesloaded/4.1.2: resolution: {integrity: sha512-QxpoYhaYgv1qXwwPd6EnQFLkzrRZaecIafPsY51fnrsr+qIh0wrPVaOQirBaFl+FjFHk5BMfpf+6QHy0lRrcNw==} dependencies: @@ -1170,7 +1200,6 @@ packages: resolution: {integrity: sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==} dependencies: '@types/react': 18.0.26 - dev: true /@types/react-is/17.0.3: resolution: {integrity: sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==} @@ -1205,6 +1234,10 @@ packages: resolution: {integrity: sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==} dev: true + /@types/use-sync-external-store/0.0.3: + resolution: {integrity: sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==} + dev: false + /@vitejs/plugin-legacy/3.0.1_terser@5.16.1+vite@4.0.3: resolution: {integrity: sha512-XCtEjxoR3rmy000ujYRBp5kggWqzHz9+F20/yIMUWOzbvu0+KW1e14Fvb8h7SpNn+bfjGW1RiAs1Vrgb7Js+iQ==} engines: {node: ^14.18.0 || >=16.0.0} @@ -1694,6 +1727,10 @@ packages: ev-emitter: 2.1.2 dev: false + /immer/9.0.17: + resolution: {integrity: sha512-+hBruaLSQvkPfxRiTLK/mi4vLH+/VQS6z2KJahdoxlleFOI8ARqzOF17uy12eFDlqWmPoygwc5evgwcp+dlHhg==} + dev: false + /import-fresh/3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -2156,6 +2193,39 @@ packages: react-dom: 18.2.0_react@18.2.0 dev: false + /react-redux/8.0.5_ie75ejlwqy5zh3tldgt7pftwcu: + resolution: {integrity: sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==} + peerDependencies: + '@types/react': ^16.8 || ^17.0 || ^18.0 + '@types/react-dom': ^16.8 || ^17.0 || ^18.0 + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + react-native: '>=0.59' + redux: ^4 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + react-dom: + optional: true + react-native: + optional: true + redux: + optional: true + dependencies: + '@babel/runtime': 7.20.7 + '@types/hoist-non-react-statics': 3.3.1 + '@types/react': 18.0.26 + '@types/react-dom': 18.0.10 + '@types/use-sync-external-store': 0.0.3 + hoist-non-react-statics: 3.3.2 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + react-is: 18.2.0 + use-sync-external-store: 1.2.0_react@18.2.0 + dev: false + /react-router-dom/6.6.1_biqbaboplfbrettd7655fr4n2y: resolution: {integrity: sha512-u+8BKUtelStKbZD5UcY0NY90WOzktrkJJhyhNg7L0APn9t1qJNLowzrM9CHdpB6+rcPt6qQrlkIXsTvhuXP68g==} engines: {node: '>=14'} @@ -2234,6 +2304,20 @@ packages: loose-envify: 1.4.0 dev: false + /redux-thunk/2.4.2_redux@4.2.0: + resolution: {integrity: sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==} + peerDependencies: + redux: ^4 + dependencies: + redux: 4.2.0 + dev: false + + /redux/4.2.0: + resolution: {integrity: sha512-oSBmcKKIuIR4ME29/AeNUnl5L+hvBq7OaJWzaptTQJAntaPvxIJqfnjbaEiCzzaIz+XmVILfqAM3Ob0aXLPfjA==} + dependencies: + '@babel/runtime': 7.20.7 + dev: false + /regenerator-runtime/0.13.11: resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} @@ -2246,6 +2330,10 @@ packages: engines: {node: '>=0.10.0'} dev: true + /reselect/4.1.7: + resolution: {integrity: sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==} + dev: false + /resize-observer-polyfill/1.5.1: resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} dev: false @@ -2522,6 +2610,14 @@ packages: escalade: 3.1.1 picocolors: 1.0.0 + /use-sync-external-store/1.2.0_react@18.2.0: + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + /vite-plugin-compression/0.5.1_vite@4.0.3: resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==} peerDependencies: diff --git a/src/components/proview/navShow.tsx b/src/components/proview/navShow.tsx deleted file mode 100644 index 832657e..0000000 --- a/src/components/proview/navShow.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { createContext, useContext, useState } from "react"; -import { Flipper } from "react-flip-toolkit"; -import { ReactChildrenType } from "./type"; - -const NavContext = createContext({ - showed: false, - handlerChangeShow: () => {}, -}); -const NavShowProview = ({ children }: ReactChildrenType) => { - const [showed, set] = useState(false), - handlerChangeShow = () => set((showed) => !showed); - return ( - - - {children} - - - ); -}; - -export const useNavShowed = () => useContext(NavContext); - -export default NavShowProview; diff --git a/src/components/proview/tagSelect.tsx b/src/components/proview/tagSelect.tsx deleted file mode 100644 index 0883347..0000000 --- a/src/components/proview/tagSelect.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { NavQueryItemType } from "@routers/layout/nav/tools"; -import { ReactChildrenType } from "./type"; -import { useContext, createContext, useReducer } from "react"; - -interface TagStates { - tags: NavQueryItemType[]; -} -enum TagsActionKind { - INCREASE = "INCREASE", - DECREASE = "DECREASE", -} -interface TagsAction { - type: TagsActionKind; - payload: NavQueryItemType; -} -const TagSelectContext = createContext< - TagStates & { - handerAddTag: (item: NavQueryItemType) => void; - handerDeleteTag: (item: NavQueryItemType) => void; - } ->({ - tags: [], - handerAddTag: () => {}, - handerDeleteTag: () => {}, -}); -function reducer(state: TagStates, action: TagsAction) { - const { type, payload } = action; - switch (type) { - case TagsActionKind.INCREASE: - return { tags: [...state.tags, payload] }; - case TagsActionKind.DECREASE: - return { tags: state.tags.filter((item) => item.id !== payload.id) }; - default: - return state; - } -} -const TagSelectProview = ({ children }: ReactChildrenType) => { - const [tags, tagsDispath] = useReducer(reducer, { - tags: [] as NavQueryItemType[], - }), - handerAddTag = (item: NavQueryItemType) => - tagsDispath({ type: TagsActionKind.INCREASE, payload: item }), - handerDeleteTag = (item: NavQueryItemType) => - tagsDispath({ type: TagsActionKind.DECREASE, payload: item }); - return ( - - {children} - - ); -}; - -export const useTagsSelected = () => useContext(TagSelectContext); - -export default TagSelectProview; diff --git a/src/main.tsx b/src/main.tsx index a113086..86b6142 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,20 +1,22 @@ import ReactDOM from "react-dom/client"; import { createBrowserRouter, RouterProvider } from "react-router-dom"; +//router page import VideoPage from "./routers/video"; -import "./index.less"; import Layout from "./routers/layout"; import PhotoPage from "./routers/photo"; import ErrorPage from "./routers/error"; +//preview import ScreenProview from "@components/proview/screenSize"; -import TagSelectProview from "@components/proview/tagSelect"; - +import MUIThemePreview from "@components/proview/themePreview"; +import { Provider } from "react-redux"; +import store from "@store/index"; //ployfill import "intersection-observer"; import "./normalize.css"; import "loading-attribute-polyfill"; import "whatwg-fetch"; -import NavShowProview from "@components/proview/navShow"; -import MUIThemePreview from "@components/proview/themePreview"; +import "./index.less"; + const router = createBrowserRouter([ { path: "/", @@ -51,12 +53,10 @@ const router = createBrowserRouter([ ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - - - - - - - + + + + + ); diff --git a/src/routers/layout/index.tsx b/src/routers/layout/index.tsx index 053dfd8..6a63ba7 100644 --- a/src/routers/layout/index.tsx +++ b/src/routers/layout/index.tsx @@ -1,23 +1,28 @@ -import { Flipped } from "react-flip-toolkit"; +import { Flipped, Flipper } from "react-flip-toolkit"; import { Outlet } from "react-router-dom"; import Header from "./header"; import Header_Nav from "./nav"; +import { useAppSelector } from "@store/hooks"; +import { selectNavMoreShowed } from "@store/device/index"; export default function Layout() { + const showed = useAppSelector(selectNavMoreShowed); return ( <> -
- - -
-
- -
-
-
+ +
+ + +
+
+ +
+
+
+ ); } diff --git a/src/routers/layout/nav/index.tsx b/src/routers/layout/nav/index.tsx index ac132a9..655c023 100644 --- a/src/routers/layout/nav/index.tsx +++ b/src/routers/layout/nav/index.tsx @@ -15,16 +15,21 @@ import { arrayMove, SortableContext, useSortable } from "@dnd-kit/sortable"; import { CSS } from "@dnd-kit/utilities"; import { restrictToParentElement } from "@dnd-kit/modifiers"; import styles from "./nav.module.less"; -import { FC, useState, useMemo, memo } from "react"; +import { FC, useMemo, memo } from "react"; import { Flipped } from "react-flip-toolkit"; import { NavQueryItemType, useNavList } from "./tools"; import { setLocalstorage } from "../tools"; -import { useTagsSelected } from "@components/proview/tagSelect"; -import { useNavShowed } from "@components/proview/navShow"; +import { useAppSelector, useAppDispatch } from "@store/hooks"; +import { changeNavMoreShowed, selectNavMoreShowed } from "@store/device/index"; +import { + handerAddTag, + handerDeleteTag, + selectActiveTags, +} from "@store/tags/index"; export default function Header_Nav() { const [navLists, setLists] = useNavList(); //tag区是否展开 - const { showed } = useNavShowed(); + const showed = useAppSelector(selectNavMoreShowed); //拖拽事件绑定 const sensors = useSensors( // 鼠标点击 @@ -96,8 +101,10 @@ export default function Header_Nav() { const NavInViewItem = () => { //最后一个span是否可见 - const { ref, inView } = useInView({ initialInView: true }); - const { handlerChangeShow, showed } = useNavShowed(); + const { ref, inView } = useInView({ initialInView: true }), + showed = useAppSelector(selectNavMoreShowed), + dispatch = useAppDispatch(); + return ( <> {
dispatch(changeNavMoreShowed())} style={{ display: inView ? "none" : "flex", }} @@ -143,25 +150,22 @@ const NavItem: FC = memo((props) => { }); const NavTagChipItem: FC = memo((props) => { - const [clicked, setClick] = useState(false), - { handerAddTag, handerDeleteTag } = useTagsSelected(); + const clicked = useAppSelector(selectActiveTags).some( + (item) => item.id === props.id + ), + dispatch = useAppDispatch(); return ( - setClick((clicked) => { - //注意这里是要改变点击状态,所以应该反着来 - //说明之前是点击状态,现在要取消点击 - if (clicked) { - handerDeleteTag(props); - } else { - handerAddTag(props); - } - return !clicked; - }) - } + onClick={() => { + if (clicked) { + dispatch(handerDeleteTag(props)); + } else { + dispatch(handerAddTag(props)); + } + }} /> ); }); diff --git a/src/routers/video/index.tsx b/src/routers/video/index.tsx index 5c9e7bf..cf93003 100644 --- a/src/routers/video/index.tsx +++ b/src/routers/video/index.tsx @@ -1,16 +1,9 @@ import VideoMasonry from "./masonry"; -import { useTagsSelected } from "@components/proview/tagSelect"; + export default function VideoPage() { - const { tags } = useTagsSelected(), - qlists = tags - .filter((item) => item.queryType === "q") - .reduceRight((pre, cur) => { - return `${cur.queryString}+${pre}`; - }, ""), - q = qlists.length < 1 ? undefined : `tag.${qlists}`; return ( <> - + ); } diff --git a/src/store/device/index.ts b/src/store/device/index.ts new file mode 100644 index 0000000..8a2f145 --- /dev/null +++ b/src/store/device/index.ts @@ -0,0 +1,34 @@ +import { createSlice } from "@reduxjs/toolkit"; + +import type { RootState } from ".."; + +interface DeviceState { + /** + * @description nav栏是否展开 + */ + navMoreShowed: boolean; +} +const initialState: DeviceState = { + navMoreShowed: false, +}; +export const DeviceSlice = createSlice({ + name: "device", + initialState, + reducers: { + /** + * @description 修改nav列表展开情况 + */ + changeNavMoreShowed: (state) => { + state.navMoreShowed = !state.navMoreShowed; + }, + }, +}); + +export const { changeNavMoreShowed } = DeviceSlice.actions; +/** + * @description 获取nav是否展开 + */ +export const selectNavMoreShowed = (state: RootState) => + state.device.navMoreShowed; + +export default DeviceSlice.reducer; diff --git a/src/store/hooks.ts b/src/store/hooks.ts new file mode 100644 index 0000000..fb559c3 --- /dev/null +++ b/src/store/hooks.ts @@ -0,0 +1,6 @@ +import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux"; +import type { RootState, AppDispatch } from "./index"; + +// 在整个应用程序中使用,而不是简单的 `useDispatch` 和 `useSelector` +export const useAppDispatch: () => AppDispatch = useDispatch; +export const useAppSelector: TypedUseSelectorHook = useSelector; diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000..7ab70f6 --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,12 @@ +import { configureStore } from "@reduxjs/toolkit"; +import device from "./device"; +import ActiveTags from "./tags"; +const store = configureStore({ + reducer: { + device, + ActiveTags, + }, +}); +export default store; +export type RootState = ReturnType; +export type AppDispatch = typeof store.dispatch; diff --git a/src/store/tags/index.ts b/src/store/tags/index.ts new file mode 100644 index 0000000..567581f --- /dev/null +++ b/src/store/tags/index.ts @@ -0,0 +1,51 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import { NavQueryItemType } from "@routers/layout/nav/tools"; + +import type { RootState } from ".."; + +interface TagStates { + /** + * @description 用户点击的tag栏 + */ + activeTags: NavQueryItemType[]; +} +const initialState: TagStates = { + activeTags: [], +}; +export const ActiveTagsSlice = createSlice({ + name: "activeTags", + initialState, + reducers: { + /** + * @description 添加tag + */ + handerAddTag: (state, action: PayloadAction) => { + //这里需要注意的是queryType有三种,只有q可以同存. + switch (action.payload.queryType) { + case "q": + break; + + default: + state.activeTags = state.activeTags.filter( + (item) => item.queryType !== action.payload.queryType + ); + break; + } + state.activeTags = [...state.activeTags, action.payload]; + }, + /** + * @description 删除tag + */ + handerDeleteTag: (state, action: PayloadAction) => { + state.activeTags = state.activeTags.filter( + (item) => item.id !== action.payload.id + ); + }, + }, +}); + +export const { handerAddTag, handerDeleteTag } = ActiveTagsSlice.actions; + +export const selectActiveTags = (state: RootState) => + state.ActiveTags.activeTags; +export default ActiveTagsSlice.reducer;