/ src / modules / Dapps / Dapps.reducer.js
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)