pageSlice.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. import {createAction, createSlice} from "@reduxjs/toolkit";
  2. import {REQ_STATUS} from "@api/config";
  3. import {buildReqReducer, reqFavoGet, reqPageModules} from "@features/api";
  4. import {localStorageGet, localStorageRemove, localStorageSet} from "@utils/helpers";
  5. import {LOCAL_FAVO_DATA, LOCAL_FAVO_IDS} from "@constants/localkey";
  6. const addFavoriteKey=(state,key)=>{
  7. let favoriteKeys=state.favoriteKeys;
  8. if (!favoriteKeys.includes(key)){
  9. favoriteKeys.push(key);
  10. }
  11. state.favoriteKeys=favoriteKeys;
  12. }
  13. const parseModules=(data,key="",state)=>{
  14. if (data!=null){
  15. for (let i = 0; i <data.length ; i++) {
  16. let item=data[i];
  17. if (item!=null){
  18. let tkey=key+"_"+i;
  19. item["_mkey"]=tkey;
  20. if (item.data_key==="favorite"){
  21. addFavoriteKey(state,tkey);
  22. }
  23. let tdata=item.data;
  24. if (tdata!=null){
  25. tkey=tkey+"_data";
  26. item.data=parseModules(tdata,tkey,state);
  27. }
  28. }
  29. }
  30. }
  31. return data;
  32. }
  33. //mkey module key
  34. const getModuleData=(data,mkey)=>{
  35. let module=null;
  36. if (data!=null){
  37. let keys=mkey.split("_");
  38. let tdata=data;
  39. for (let i = 0; i < keys.length; i++) {
  40. let key=keys[i];
  41. let temp=tdata[key];
  42. if (temp==null)break;
  43. tdata=temp;
  44. }
  45. module=tdata;
  46. }
  47. return module;
  48. }
  49. //初始化favorite
  50. const getLocalFavorite=()=>{
  51. let localFIDS=localStorageGet(LOCAL_FAVO_IDS,"");
  52. let favoData=[];
  53. if (localFIDS!=null&&localFIDS!==""){
  54. let list=JSON.parse(localFIDS);
  55. if (list!=null){
  56. for (let i = 0; i < list.length; i++) {
  57. let key=LOCAL_FAVO_DATA+"_"+list[i];
  58. let temp=localStorageGet(key,"");
  59. if (temp!==""){
  60. favoData.push(JSON.parse(temp))
  61. }
  62. }
  63. }
  64. }
  65. return favoData;
  66. }
  67. const parseFavorite=(state)=>{
  68. let favoriteKeys=state.favoriteKeys;
  69. if (favoriteKeys!=null&&favoriteKeys.length>0){
  70. // let localFavorite=null;
  71. for (let i = 0; i < favoriteKeys.length; i++) {
  72. let module=getModuleData(state.pageData,favoriteKeys[i]);
  73. if (module!=null){
  74. let data=module.data;
  75. if (data==null||data.length===0){
  76. // if (localFavorite==null)localFavorite=getLocalFavorite();
  77. data=state.favoriteData;
  78. }
  79. module.data=data;
  80. }
  81. }
  82. }
  83. }
  84. //page modules
  85. const reducerPage=(state,action)=>{
  86. let json=action.payload;
  87. if (json&&json.code===0){
  88. let data=json.data==null?[]:json.data;
  89. let pageID=json.pageID;
  90. data=parseModules(data,pageID,state);
  91. state.pageData[pageID]=data;
  92. parseFavorite(state);
  93. }
  94. }
  95. const pageStatus=(state,status,arg)=>{
  96. state.status=status;
  97. }
  98. //page modules
  99. const reducerFavoGet=(state,action,arg)=>{
  100. let payload=action.payload;
  101. if (payload&&payload.code===200){
  102. state.favoriteData=payload.data;
  103. parseFavorite(state);
  104. }
  105. }
  106. export const Page=createSlice({
  107. name: 'page',
  108. initialState: {
  109. status:REQ_STATUS.NONE,
  110. pageData:{},
  111. favoriteKeys:[],
  112. favoriteData:[]
  113. },
  114. reducers: {
  115. updateGameList:((state, action) => {
  116. let payload=action.payload;
  117. let mkey=payload.mkey;
  118. let module=getModuleData(state.pageData,mkey);
  119. if (module!=null){
  120. let oldData=module.data;
  121. let data=payload.data;
  122. let newData=[];
  123. if (oldData!=null&&data!=null)newData=oldData.concat(data);
  124. let newObj={page:payload.page,totalPage:payload.totalPage,data:newData};
  125. Object.assign(module,newObj);
  126. }
  127. }),
  128. updatePageGameLike:((state, action) => {
  129. let {data,like}=action.payload;
  130. let favoriteKeys=state.favoriteKeys;
  131. if (favoriteKeys&&favoriteKeys.length>0){
  132. for (let i = 0; i < favoriteKeys.length; i++) {
  133. let mkey=favoriteKeys[i];
  134. let module=getModuleData(state.pageData,mkey);
  135. if (module!=null){
  136. let oldData=module.data;
  137. if (oldData==null)oldData=[];
  138. if (oldData.length===0){
  139. if (like===1)oldData.unshift(data);
  140. }
  141. else
  142. {
  143. let index=-1;
  144. for (let j = 0; j < oldData.length; j++) {
  145. let item=oldData[j];
  146. if (item!=null&&item.id===data.id){
  147. index=j;
  148. break;
  149. }
  150. }
  151. if (like===1){
  152. if (index===-1)oldData.unshift(data);
  153. }
  154. else {
  155. if (index>=0)oldData.splice(index,1);
  156. }
  157. }
  158. module.data=oldData;
  159. state.favoriteData=oldData;
  160. }
  161. }
  162. }
  163. // if (like===1)localStorageSet(LOCAL_FAVO_DATA+"_"+data.id,JSON.stringify(data));
  164. // else localStorageRemove(LOCAL_FAVO_DATA+"_"+data.id)
  165. }),
  166. onSliceChangeUser:(state,action)=>{
  167. state.favoriteData=[];
  168. let favoriteKeys=state.favoriteKeys;
  169. if (favoriteKeys&&favoriteKeys.length>0) {
  170. for (let i = 0; i < favoriteKeys.length; i++) {
  171. let mkey = favoriteKeys[i];
  172. let module = getModuleData(state.pageData, mkey);
  173. if (module != null) {
  174. module.data=[];
  175. }
  176. }
  177. }
  178. }
  179. },
  180. extraReducers: (builder) => {
  181. buildReqReducer(builder,reqPageModules,reducerPage,pageStatus);
  182. buildReqReducer(builder,reqFavoGet,reducerFavoGet,null);
  183. },
  184. })
  185. export const {updateGameList,updatePageGameLike} = Page.actions;
  186. export const getFetchStatus=(state)=>{
  187. return state.page.status;
  188. }
  189. export const getModules=(pageID)=>(state)=>{
  190. let pageData=state.page.pageData;
  191. if (pageData==null)return [];
  192. return pageData[pageID];
  193. }
  194. const collectGameItems=(pageData)=>{
  195. const list=[];
  196. const seen=new Set();
  197. const visit=(node)=>{
  198. if (node==null) return;
  199. if (Array.isArray(node)){ node.forEach(visit); return; }
  200. if (typeof node!=='object') return;
  201. if (node.id!=null&&node.img!=null&&node.link!=null){
  202. if (!seen.has(node.id)){ seen.add(node.id); list.push(node); }
  203. }
  204. if (node.data) visit(node.data);
  205. };
  206. if (pageData){ for (const k in pageData) visit(pageData[k]); }
  207. return list;
  208. }
  209. // memoize by pageData reference so useSelector won't see "new array" each call
  210. let _orderedGamesPageRef=null;
  211. let _orderedGamesCache=[];
  212. export const getOrderedGames=(state)=>{
  213. const pageData=state.page&&state.page.pageData;
  214. if (pageData===_orderedGamesPageRef) return _orderedGamesCache;
  215. _orderedGamesPageRef=pageData;
  216. _orderedGamesCache=collectGameItems(pageData);
  217. return _orderedGamesCache;
  218. }
  219. // 「热门游戏」= 页面里第一个含游戏卡片(id/img/link 完整)的模块。
  220. // 不依赖模块名字 / icon / data_key —— 不管叫什么,排第一就当热门。
  221. let _hotGamesPageRef=null;
  222. let _hotGamesCache=[];
  223. const collectFromNode=(node,out,seen)=>{
  224. if (node==null) return;
  225. if (Array.isArray(node)){ node.forEach(n=>collectFromNode(n,out,seen)); return; }
  226. if (typeof node!=='object') return;
  227. if (node.id!=null&&node.img!=null&&node.link!=null){
  228. if (!seen.has(node.id)){ seen.add(node.id); out.push(node); }
  229. }
  230. if (node.data) collectFromNode(node.data,out,seen);
  231. }
  232. // 模块自身就是数组 / 顶层就是模块列表 —— 找第一个能挖出游戏卡的模块
  233. const firstGameModule=(modules)=>{
  234. if (!modules) return null;
  235. if (!Array.isArray(modules)){
  236. // 不是数组,按对象遍历找第一个值
  237. for (const k in modules){
  238. const found=firstGameModule(modules[k]);
  239. if (found) return found;
  240. }
  241. return null;
  242. }
  243. for (let i=0;i<modules.length;i++){
  244. const m=modules[i];
  245. if (m==null) continue;
  246. const seen=new Set(); const out=[];
  247. collectFromNode(m,out,seen);
  248. if (out.length>0) return out;
  249. }
  250. return null;
  251. }
  252. export const getHotGames=(state)=>{
  253. const pageData=state.page&&state.page.pageData;
  254. if (pageData===_hotGamesPageRef) return _hotGamesCache;
  255. _hotGamesPageRef=pageData;
  256. let out=[];
  257. if (pageData){
  258. // pageData 是 { pageId: [modules...] } 结构,取第一个有游戏的模块
  259. for (const k in pageData){
  260. const list=firstGameModule(pageData[k]);
  261. if (list && list.length>0){ out=list; break; }
  262. }
  263. }
  264. if (out.length===0){
  265. // 兜底:orderedGames 全量,避免轮播完全空
  266. out=collectGameItems(pageData);
  267. }
  268. _hotGamesCache=out;
  269. return _hotGamesCache;
  270. }
  271. export const getPopularGames=(state)=>{
  272. const pageData=state.page&&state.page.pageData;
  273. const all=collectGameItems(pageData);
  274. if (all.length===0) return [];
  275. const indexs=all.map((_,i)=>i);
  276. const out=[];
  277. for (let i=0;i<6&&indexs.length>0;i++){
  278. const r=Math.floor(Math.random()*indexs.length);
  279. out.push(all[indexs[r]]);
  280. indexs.splice(r,1);
  281. }
  282. return out;
  283. }
  284. export default Page.reducer;