import {createAction, createSlice} from "@reduxjs/toolkit"; import {REQ_STATUS} from "@api/config"; import {buildReqReducer, reqFavoGet, reqPageModules} from "@features/api"; import {localStorageGet, localStorageRemove, localStorageSet} from "@utils/helpers"; import {LOCAL_FAVO_DATA, LOCAL_FAVO_IDS} from "@constants/localkey"; const addFavoriteKey=(state,key)=>{ let favoriteKeys=state.favoriteKeys; if (!favoriteKeys.includes(key)){ favoriteKeys.push(key); } state.favoriteKeys=favoriteKeys; } const parseModules=(data,key="",state)=>{ if (data!=null){ for (let i = 0; i { let module=null; if (data!=null){ let keys=mkey.split("_"); let tdata=data; for (let i = 0; i < keys.length; i++) { let key=keys[i]; let temp=tdata[key]; if (temp==null)break; tdata=temp; } module=tdata; } return module; } //初始化favorite const getLocalFavorite=()=>{ let localFIDS=localStorageGet(LOCAL_FAVO_IDS,""); let favoData=[]; if (localFIDS!=null&&localFIDS!==""){ let list=JSON.parse(localFIDS); if (list!=null){ for (let i = 0; i < list.length; i++) { let key=LOCAL_FAVO_DATA+"_"+list[i]; let temp=localStorageGet(key,""); if (temp!==""){ favoData.push(JSON.parse(temp)) } } } } return favoData; } const parseFavorite=(state)=>{ let favoriteKeys=state.favoriteKeys; if (favoriteKeys!=null&&favoriteKeys.length>0){ // let localFavorite=null; for (let i = 0; i < favoriteKeys.length; i++) { let module=getModuleData(state.pageData,favoriteKeys[i]); if (module!=null){ let data=module.data; if (data==null||data.length===0){ // if (localFavorite==null)localFavorite=getLocalFavorite(); data=state.favoriteData; } module.data=data; } } } } //page modules const reducerPage=(state,action)=>{ let json=action.payload; if (json&&json.code===0){ let data=json.data==null?[]:json.data; let pageID=json.pageID; data=parseModules(data,pageID,state); state.pageData[pageID]=data; parseFavorite(state); } } const pageStatus=(state,status,arg)=>{ state.status=status; } //page modules const reducerFavoGet=(state,action,arg)=>{ let payload=action.payload; if (payload&&payload.code===200){ state.favoriteData=payload.data; parseFavorite(state); } } export const Page=createSlice({ name: 'page', initialState: { status:REQ_STATUS.NONE, pageData:{}, favoriteKeys:[], favoriteData:[] }, reducers: { updateGameList:((state, action) => { let payload=action.payload; let mkey=payload.mkey; let module=getModuleData(state.pageData,mkey); if (module!=null){ let oldData=module.data; let data=payload.data; let newData=[]; if (oldData!=null&&data!=null)newData=oldData.concat(data); let newObj={page:payload.page,totalPage:payload.totalPage,data:newData}; Object.assign(module,newObj); } }), updatePageGameLike:((state, action) => { let {data,like}=action.payload; let favoriteKeys=state.favoriteKeys; if (favoriteKeys&&favoriteKeys.length>0){ for (let i = 0; i < favoriteKeys.length; i++) { let mkey=favoriteKeys[i]; let module=getModuleData(state.pageData,mkey); if (module!=null){ let oldData=module.data; if (oldData==null)oldData=[]; if (oldData.length===0){ if (like===1)oldData.unshift(data); } else { let index=-1; for (let j = 0; j < oldData.length; j++) { let item=oldData[j]; if (item!=null&&item.id===data.id){ index=j; break; } } if (like===1){ if (index===-1)oldData.unshift(data); } else { if (index>=0)oldData.splice(index,1); } } module.data=oldData; state.favoriteData=oldData; } } } // if (like===1)localStorageSet(LOCAL_FAVO_DATA+"_"+data.id,JSON.stringify(data)); // else localStorageRemove(LOCAL_FAVO_DATA+"_"+data.id) }), onSliceChangeUser:(state,action)=>{ state.favoriteData=[]; let favoriteKeys=state.favoriteKeys; if (favoriteKeys&&favoriteKeys.length>0) { for (let i = 0; i < favoriteKeys.length; i++) { let mkey = favoriteKeys[i]; let module = getModuleData(state.pageData, mkey); if (module != null) { module.data=[]; } } } } }, extraReducers: (builder) => { buildReqReducer(builder,reqPageModules,reducerPage,pageStatus); buildReqReducer(builder,reqFavoGet,reducerFavoGet,null); }, }) export const {updateGameList,updatePageGameLike} = Page.actions; export const getFetchStatus=(state)=>{ return state.page.status; } export const getModules=(pageID)=>(state)=>{ let pageData=state.page.pageData; if (pageData==null)return []; return pageData[pageID]; } const collectGameItems=(pageData)=>{ const list=[]; const seen=new Set(); const visit=(node)=>{ if (node==null) return; if (Array.isArray(node)){ node.forEach(visit); return; } if (typeof node!=='object') return; if (node.id!=null&&node.img!=null&&node.link!=null){ if (!seen.has(node.id)){ seen.add(node.id); list.push(node); } } if (node.data) visit(node.data); }; if (pageData){ for (const k in pageData) visit(pageData[k]); } return list; } // memoize by pageData reference so useSelector won't see "new array" each call let _orderedGamesPageRef=null; let _orderedGamesCache=[]; export const getOrderedGames=(state)=>{ const pageData=state.page&&state.page.pageData; if (pageData===_orderedGamesPageRef) return _orderedGamesCache; _orderedGamesPageRef=pageData; _orderedGamesCache=collectGameItems(pageData); return _orderedGamesCache; } // 「热门游戏」= 页面里第一个含游戏卡片(id/img/link 完整)的模块。 // 不依赖模块名字 / icon / data_key —— 不管叫什么,排第一就当热门。 let _hotGamesPageRef=null; let _hotGamesCache=[]; const collectFromNode=(node,out,seen)=>{ if (node==null) return; if (Array.isArray(node)){ node.forEach(n=>collectFromNode(n,out,seen)); return; } if (typeof node!=='object') return; if (node.id!=null&&node.img!=null&&node.link!=null){ if (!seen.has(node.id)){ seen.add(node.id); out.push(node); } } if (node.data) collectFromNode(node.data,out,seen); } // 模块自身就是数组 / 顶层就是模块列表 —— 找第一个能挖出游戏卡的模块 const firstGameModule=(modules)=>{ if (!modules) return null; if (!Array.isArray(modules)){ // 不是数组,按对象遍历找第一个值 for (const k in modules){ const found=firstGameModule(modules[k]); if (found) return found; } return null; } for (let i=0;i0) return out; } return null; } export const getHotGames=(state)=>{ const pageData=state.page&&state.page.pageData; if (pageData===_hotGamesPageRef) return _hotGamesCache; _hotGamesPageRef=pageData; let out=[]; if (pageData){ // pageData 是 { pageId: [modules...] } 结构,取第一个有游戏的模块 for (const k in pageData){ const list=firstGameModule(pageData[k]); if (list && list.length>0){ out=list; break; } } } if (out.length===0){ // 兜底:orderedGames 全量,避免轮播完全空 out=collectGameItems(pageData); } _hotGamesCache=out; return _hotGamesCache; } export const getPopularGames=(state)=>{ const pageData=state.page&&state.page.pageData; const all=collectGameItems(pageData); if (all.length===0) return []; const indexs=all.map((_,i)=>i); const out=[]; for (let i=0;i<6&&indexs.length>0;i++){ const r=Math.floor(Math.random()*indexs.length); out.push(all[indexs[r]]); indexs.splice(r,1); } return out; } export default Page.reducer;