Dapps.reducer.js
1 // import hardcodedDapps from '../../common/data/dapps' 2 import * as Categories from '../../common/data/categories' 3 import reducerUtil from '../../common/utils/reducer' 4 import { showAlertAction } from '../Alert/Alert.reducer' 5 import BlockchainSDK from '../../common/blockchain' 6 import { TYPE_SUBMIT } from '../TransactionStatus/TransactionStatus.utilities' 7 8 const ON_FINISH_FETCH_ALL_DAPPS_ACTION = 9 'DAPPS_ON_FINISH_FETCH_ALL_DAPPS_ACTION' 10 11 const ON_START_FETCH_HIGHEST_RANKED = 'DAPPS_ON_START_FETCH_HIGHEST_RANKED' 12 const ON_FINISH_FETCH_HIGHEST_RANKED = 'DAPPS_ON_FINISH_FETCH_HIGHEST_RANKED' 13 const ON_START_FETCH_RECENTLY_ADDED = 'DAPPS_ON_START_FETCH_RECENTLY_ADDED' 14 const ON_FINISH_FETCH_RECENTLY_ADDED = 'DAPPS_ON_FINISH_FETCH_RECENTLY_ADDED' 15 16 const ON_START_FETCH_BY_CATEGORY = 'DAPPS_ON_START_FETCH_BY_CATEGORY' 17 const ON_FINISH_FETCH_BY_CATEGORY = 'DAPPS_ON_FINISH_FETCH_BY_CATEGORY' 18 19 const ON_UPDATE_DAPP_DATA = 'DAPPS_ON_UPDATE_DAPP_DATA' 20 21 const RECENTLY_ADDED_SIZE = 50 22 const HIGHEST_RANKED_SIZE = 50 23 24 class DappsState { 25 constructor() { 26 this.items = [] 27 this.hasMore = true 28 this.fetched = null 29 } 30 31 canFetch() { 32 return this.hasMore && this.fetched !== true 33 } 34 35 setFetched(fetched) { 36 this.fetched = fetched 37 } 38 39 appendItems(items) { 40 const availableNames = new Set() 41 let addedItems = 0 42 for (let i = 0; i < this.items.length; i += 1) 43 availableNames.add(this.items[i].name) 44 for (let i = 0; i < items.length; i += 1) { 45 if (availableNames.has(items[i].name) === false) { 46 addedItems += 1 47 this.items.push(items[i]) 48 } 49 } 50 51 this.hasMore = addedItems !== 0 52 } 53 54 cloneWeakItems() { 55 this.items = [...this.items] 56 return this 57 } 58 } 59 60 export const onFinishFetchAllDappsAction = dapps => ({ 61 type: ON_FINISH_FETCH_ALL_DAPPS_ACTION, 62 payload: dapps, 63 }) 64 65 export const onStartFetchHighestRankedAction = () => ({ 66 type: ON_START_FETCH_HIGHEST_RANKED, 67 payload: null, 68 }) 69 70 export const onFinishFetchHighestRankedAction = highestRanked => ({ 71 type: ON_FINISH_FETCH_HIGHEST_RANKED, 72 payload: highestRanked, 73 }) 74 75 export const onStartFetchRecentlyAddedAction = () => ({ 76 type: ON_START_FETCH_RECENTLY_ADDED, 77 payload: null, 78 }) 79 80 export const onFinishFetchRecentlyAddedAction = recentlyAdded => ({ 81 type: ON_FINISH_FETCH_RECENTLY_ADDED, 82 payload: recentlyAdded, 83 }) 84 85 export const onStartFetchByCategoryAction = category => ({ 86 type: ON_START_FETCH_BY_CATEGORY, 87 payload: category, 88 }) 89 90 export const onFinishFetchByCategoryAction = (category, dapps) => ({ 91 type: ON_FINISH_FETCH_BY_CATEGORY, 92 payload: { category, dapps }, 93 }) 94 95 const fetchAllDappsInState = async (dispatch, getState) => { 96 const state = getState() 97 const { transactionStatus } = state 98 const stateDapps = state.dapps 99 if (stateDapps.dapps === null) { 100 try { 101 const blockchain = await BlockchainSDK.getInstance() 102 let dapps = await blockchain.DiscoverService.getDApps() 103 dapps = dapps.map(dapp => { 104 return Object.assign(dapp.metadata, { 105 id: dapp.id, 106 sntValue: parseInt(dapp.effectiveBalance, 10), 107 }) 108 }) 109 dapps.sort((a, b) => { 110 return b.sntValue - a.sntValue 111 }) 112 if (transactionStatus.type === TYPE_SUBMIT) { 113 for (let i = 0; i < dapps.length; i += 1) { 114 if (dapps[i].id === transactionStatus.dappId) { 115 dapps.splice(i, 1) 116 break 117 } 118 } 119 } 120 121 dispatch(onFinishFetchAllDappsAction(dapps)) 122 return dapps 123 } catch (e) { 124 dispatch(showAlertAction(e.message)) 125 dispatch(onFinishFetchAllDappsAction([])) 126 return [] 127 } 128 } 129 return stateDapps.dapps 130 } 131 132 export const fetchAllDappsAction = () => { 133 return async (dispatch, getState) => { 134 dispatch(onStartFetchHighestRankedAction()) 135 dispatch(onStartFetchRecentlyAddedAction()) 136 137 const dapps = await fetchAllDappsInState(dispatch, getState) 138 139 const highestRanked = dapps.slice(0, HIGHEST_RANKED_SIZE) 140 let recentlyAdded = [...dapps] 141 recentlyAdded.sort((a, b) => { 142 return new Date().getTime(b.dateAdded) - new Date(a.dateAdded).getTime() 143 }) 144 recentlyAdded = recentlyAdded.slice(0, RECENTLY_ADDED_SIZE) 145 146 dispatch(onFinishFetchHighestRankedAction(highestRanked)) 147 dispatch(onFinishFetchRecentlyAddedAction(recentlyAdded)) 148 } 149 } 150 151 export const fetchByCategoryAction = category => { 152 return async (dispatch, getState) => { 153 dispatch(onStartFetchByCategoryAction(category)) 154 155 const dapps = await fetchAllDappsInState(dispatch, getState) 156 const filteredByCategory = dapps.filter(dapp => dapp.category === category) 157 const dappsCategoryState = getState().dapps.dappsCategoryMap.get(category) 158 const from = dappsCategoryState.items.length 159 const to = Math.min(from + 5, filteredByCategory.length) 160 const dappsCategorySlice = filteredByCategory.slice(from, to) 161 162 dispatch(onFinishFetchByCategoryAction(category, dappsCategorySlice)) 163 } 164 } 165 166 export const onUpdateDappDataAction = dapp => ({ 167 type: ON_UPDATE_DAPP_DATA, 168 payload: dapp, 169 }) 170 171 const onFinishFetchAllDapps = (state, dapps) => { 172 return Object.assign({}, state, { dapps }) 173 } 174 175 const onStartFetchHightestRanked = state => { 176 return Object.assign({}, state, { 177 highestRankedFetched: false, 178 }) 179 } 180 181 const onFinishFetchHighestRanked = (state, payload) => { 182 return Object.assign({}, state, { 183 highestRanked: payload, 184 highestRankedFetched: true, 185 }) 186 } 187 188 const onStartFetchRecentlyAdded = state => { 189 return Object.assign({}, state, { 190 recentlyAddedFetched: false, 191 }) 192 } 193 194 const onFinishFetchRecentlyAdded = (state, payload) => { 195 return Object.assign({}, state, { 196 recentlyAdded: payload, 197 recentlyAddedFetched: true, 198 }) 199 } 200 201 const onStartFetchByCategory = (state, payload) => { 202 const dappsCategoryMap = new Map() 203 state.dappsCategoryMap.forEach((dappState, category) => { 204 dappsCategoryMap.set(category, dappState.cloneWeakItems()) 205 if (category === payload) dappState.setFetched(true) 206 }) 207 return Object.assign({}, state, { 208 dappsCategoryMap, 209 }) 210 } 211 212 const onFinishFetchByCategory = (state, payload) => { 213 const { category, dapps } = payload 214 215 const dappsCategoryMap = new Map() 216 state.dappsCategoryMap.forEach((dappState, category_) => { 217 dappsCategoryMap.set(category_, dappState) 218 if (category_ === category) { 219 dappState.setFetched(false) 220 dappState.appendItems(dapps) 221 } 222 }) 223 return Object.assign({}, state, { 224 dappsCategoryMap, 225 }) 226 } 227 228 const insertDappIntoSortedArray = (source, dapp, cmp) => { 229 for (let i = 0; i < source.length; i += 1) { 230 if (cmp(source[i], dapp) === true) { 231 source.splice(i, 0, dapp) 232 break 233 } 234 } 235 } 236 237 const onUpdateDappData = (state, dapp) => { 238 const dappsCategoryMap = new Map() 239 const { dapps } = state 240 let { highestRanked, recentlyAdded } = state 241 let update = false 242 243 state.dappsCategoryMap.forEach((dappState, category_) => { 244 dappsCategoryMap.set(category_, dappState.cloneWeakItems()) 245 }) 246 247 for (let i = 0; i < dapps.length; i += 1) { 248 if (dapps[i].id === dapp.id) { 249 dapps[i] = dapp 250 update = true 251 break 252 } 253 } 254 255 if (update === false) { 256 insertDappIntoSortedArray(dapps, dapp, (target, dappItem) => { 257 return target.sntValue < dappItem.sntValue 258 }) 259 insertDappIntoSortedArray(highestRanked, dapp, (target, dappItem) => { 260 return target.sntValue < dappItem.sntValue 261 }) 262 highestRanked = state.highestRanked.splice(0, HIGHEST_RANKED_SIZE) 263 insertDappIntoSortedArray(recentlyAdded, dapp, (target, dappItem) => { 264 return ( 265 new Date().getTime(target.dateAdded) < 266 new Date(dappItem.dateAdded).getTime() 267 ) 268 }) 269 recentlyAdded = recentlyAdded.splice(0, RECENTLY_ADDED_SIZE) 270 271 const dappState = dappsCategoryMap.get(dapp.category) 272 insertDappIntoSortedArray(dappState.items, dapp, (target, dappItem) => { 273 return target.sntValue < dappItem.sntValue 274 }) 275 } else { 276 for (let i = 0; i < highestRanked.length; i += 1) { 277 if (highestRanked[i].id === dapp.id) { 278 highestRanked[i] = dapp 279 break 280 } 281 } 282 for (let i = 0; i < recentlyAdded.length; i += 1) { 283 if (recentlyAdded[i].id === dapp.id) { 284 recentlyAdded[i] = dapp 285 break 286 } 287 } 288 dappsCategoryMap.forEach(dappState => { 289 const dappStateRef = dappState 290 for (let i = 0; i < dappStateRef.items.length; i += 1) { 291 if (dappStateRef.items[i].id === dapp.id) { 292 dappStateRef.items[i] = dapp 293 break 294 } 295 } 296 }) 297 } 298 299 return Object.assign({}, state, { 300 dapps: [...dapps], 301 highestRanked: [...highestRanked], 302 recentlyAdded: [...recentlyAdded], 303 dappsCategoryMap, 304 }) 305 } 306 307 const map = { 308 [ON_FINISH_FETCH_ALL_DAPPS_ACTION]: onFinishFetchAllDapps, 309 [ON_START_FETCH_HIGHEST_RANKED]: onStartFetchHightestRanked, 310 [ON_FINISH_FETCH_HIGHEST_RANKED]: onFinishFetchHighestRanked, 311 [ON_START_FETCH_RECENTLY_ADDED]: onStartFetchRecentlyAdded, 312 [ON_FINISH_FETCH_RECENTLY_ADDED]: onFinishFetchRecentlyAdded, 313 [ON_START_FETCH_BY_CATEGORY]: onStartFetchByCategory, 314 [ON_FINISH_FETCH_BY_CATEGORY]: onFinishFetchByCategory, 315 [ON_UPDATE_DAPP_DATA]: onUpdateDappData, 316 } 317 318 const dappsCategoryMap = new Map() 319 dappsCategoryMap.set(Categories.EXCHANGES, new DappsState()) 320 dappsCategoryMap.set(Categories.MARKETPLACES, new DappsState()) 321 dappsCategoryMap.set(Categories.COLLECTIBLES, new DappsState()) 322 dappsCategoryMap.set(Categories.GAMES, new DappsState()) 323 dappsCategoryMap.set(Categories.SOCIAL_NETWORKS, new DappsState()) 324 dappsCategoryMap.set(Categories.UTILITIES, new DappsState()) 325 dappsCategoryMap.set(Categories.OTHER, new DappsState()) 326 327 const dappsInitialState = { 328 dapps: null, 329 highestRanked: [], 330 highestRankedFetched: null, 331 recentlyAdded: [], 332 recentlyAddedFetched: null, 333 dappsCategoryMap, 334 } 335 336 export default reducerUtil(map, dappsInitialState)