index.js
1 import {combineReducers} from 'redux'; 2 import {REQUEST, SUCCESS, FAILURE, CONTRACT_COMPILE, FILES, LOGOUT, AUTHENTICATE, 3 FETCH_CREDENTIALS, UPDATE_BASE_ETHER, CHANGE_THEME, FETCH_THEME, EXPLORER_SEARCH, DEBUGGER_INFO, 4 SIGN_MESSAGE, VERIFY_MESSAGE, TOGGLE_BREAKPOINT, 5 UPDATE_DEPLOYMENT_PIPELINE, WEB3_CONNECT, WEB3_DEPLOY, WEB3_ESTIMAGE_GAS, FETCH_EDITOR_TABS} from "../actions"; 6 import {EMBARK_PROCESS_NAME, DARK_THEME, DEPLOYMENT_PIPELINES, DEFAULT_HOST} from '../constants'; 7 8 const BN_FACTOR = 10000; 9 const VOID_ADDRESS = '0x0000000000000000000000000000000000000000'; 10 const MAX_ELEMENTS = 200; 11 12 const entitiesDefaultState = { 13 accounts: [], 14 blocks: [], 15 transactions: [], 16 processes: [], 17 services: [], 18 processLogs: [], 19 commandSuggestions: [], 20 contracts: [], 21 contractProfiles: [], 22 contractFunctions: [], 23 contractDeploys: [], 24 contractCompiles: [], 25 contractLogs: [], 26 contractEvents: [], 27 messages: [], 28 messageChannels: [], 29 versions: [], 30 plugins: [], 31 ensRecords: [], 32 files: [], 33 gasOracleStats: [], 34 }; 35 36 const sorter = { 37 blocks: function(a, b) { 38 return b.number - a.number; 39 }, 40 transactions: function(a, b) { 41 return ((BN_FACTOR * b.blockNumber) + b.transactionIndex) - ((BN_FACTOR * a.blockNumber) + a.transactionIndex); 42 }, 43 processes: function(a, b) { 44 if (a.name === EMBARK_PROCESS_NAME) return -1; 45 if (b.name === EMBARK_PROCESS_NAME) return 1; 46 return 0; 47 }, 48 commandSuggestions: function(a, b) { 49 if (a.value.indexOf('.').length > 0) { 50 let a_levels = a.value.split('.').length; 51 let b_levels = b.value.split('.').length; 52 let diff = b_levels - a_levels; 53 if (diff !== 0) return diff * -1; 54 } 55 let lengthDiff = b.value.length - a.value.length; 56 if (lengthDiff !== 0) return lengthDiff * -1; 57 return 0; 58 }, 59 processLogs: function(a, b) { 60 if (a.name !== b.name) { 61 if(a.name < b.name) return -1; 62 if(a.name > b.name) return 1; 63 return 0; 64 } 65 66 if (a.id === undefined && b.id === undefined) { 67 return b.timestamp - a.timestamp; 68 } 69 70 return b.id - a.id; 71 }, 72 contractLogs: function(a, b) { 73 return a.timestamp - b.timestamp; 74 }, 75 messages: function(a, b) { 76 return a.time - b.time; 77 }, 78 files: function(a, b) { 79 if (a.name < b.name) return -1; 80 if (a.name > b.name) return 1; 81 return 0; 82 }, 83 }; 84 85 const filtrer = { 86 processes: function(process, index, self) { 87 if (["embark", "blockchain"].indexOf(process.name) === -1) return false; 88 return index === self.findIndex((t) => t.name === process.name); 89 }, 90 services: function(process, index, self) { 91 return index === self.findIndex((t) => t.name === process.name); 92 }, 93 processLogs: function(processLog, index, self) { 94 if (processLog.id !== undefined) { 95 return index === self.findIndex((p) => p.id === processLog.id) && index <= MAX_ELEMENTS; 96 } 97 return true; 98 }, 99 contracts: function(contract, index, self) { 100 return index === self.findIndex((t) => t.className === contract.className); 101 }, 102 commandSuggestions: function(command, index, self) { 103 return index === self.findIndex((c) => ( 104 command.value === c.value 105 )); 106 }, 107 accounts: function(account, index, self) { 108 return index === self.findIndex((t) => t.address === account.address); 109 }, 110 blocks: function(block, index, self) { 111 if (index > MAX_ELEMENTS) { 112 return false; 113 } 114 115 return index === self.findIndex((t) => t.number === block.number); 116 }, 117 transactions: function(tx, index, self) { 118 if (index > MAX_ELEMENTS) { 119 return false; 120 } 121 return index === self.findIndex((t) => ( 122 t.blockNumber === tx.blockNumber && t.transactionIndex === tx.transactionIndex 123 )); 124 }, 125 ensRecords: function(record, index, self) { 126 return record.name && record.address && record.address !== VOID_ADDRESS && index === self.findIndex((r) => ( 127 r.address === record.address && r.name === record.name 128 )); 129 }, 130 files: function(file, index, self) { 131 return index === self.findIndex((f) => ( 132 file.name === f.name 133 )); 134 }, 135 gasOracleStats: function(stat, index, _self) { 136 return index === 0; // Only keep last one 137 }, 138 versions: function(version, index, self) { 139 return index === self.findIndex((v) => v.value === version.value && v.name === version.name); 140 } 141 }; 142 143 function entities(state = entitiesDefaultState, action) { 144 if (action.type === FILES[SUCCESS]) { 145 return {...state, files: action.files}; 146 } 147 for (let name of Object.keys(state)) { 148 let filter = filtrer[name] || (() => true); 149 let sort = sorter[name] || (() => true); 150 if (action[name] && action[name].length > 1) { 151 return {...state, [name]: [...action[name], ...state[name]].sort(sort).filter(filter)}; 152 } 153 if (action[name] && action[name].length === 1) { 154 let entity = action[name][0]; 155 let nested = Object.keys(state).reduce((acc, entityName) => { 156 if (entity && entity[entityName] && entity[entityName].length > 0) { 157 let entityFilter = filtrer[entityName] || (() => true); 158 let entitySort = sorter[entityName] || (() => true); 159 acc[entityName] = [...entity[entityName], ...state[entityName]].sort(entitySort).filter(entityFilter); 160 } 161 return acc; 162 }, {}); 163 return { 164 ...state, ...nested, [name]: [...action[name], ...state[name]].sort(sort).filter(filter) 165 }; 166 } 167 } 168 169 return state; 170 } 171 172 function errorMessage(_state = null, action) { 173 return action.error || null; 174 } 175 176 function errorEntities(state = {}, action) { 177 if (!action.type.endsWith(SUCCESS)) { 178 return state; 179 } 180 let newState = {}; 181 for (let name of Object.keys(entitiesDefaultState)) { 182 if (action[name] && action[name].length > 0 && action[name][0]) { 183 newState[name] = action[name][0].error; 184 } 185 } 186 return {...state, ...newState}; 187 } 188 189 function loading(_state = false, action) { 190 return action.type.endsWith(REQUEST); 191 } 192 193 function compilingContract(state = false, action) { 194 if (action.type === CONTRACT_COMPILE[REQUEST]) { 195 return true; 196 } else if (action.type === CONTRACT_COMPILE[FAILURE] || action.type === CONTRACT_COMPILE[SUCCESS]) { 197 return false; 198 } 199 200 return state; 201 } 202 203 const DEFAULT_CREDENTIALS_STATE = { 204 host: DEFAULT_HOST, 205 token: '', 206 authenticated: false, 207 authenticating: false, 208 error: null 209 }; 210 211 function credentials(state = DEFAULT_CREDENTIALS_STATE, action) { 212 if (action.type === LOGOUT[SUCCESS]) { 213 return DEFAULT_CREDENTIALS_STATE; 214 } 215 216 if (action.type === AUTHENTICATE[FAILURE]) { 217 return {error: action.error, ...DEFAULT_CREDENTIALS_STATE}; 218 } 219 220 if (action.type === AUTHENTICATE[SUCCESS]) { 221 return {...state, ...{authenticated: true, authenticating: false, token: action.token, host: action.host, error: null}}; 222 } 223 224 if (action.type === FETCH_CREDENTIALS[SUCCESS]) { 225 return {...state, ...{token: action.token, host: action.host}}; 226 } 227 228 if (action.type === AUTHENTICATE[REQUEST]) { 229 return {...state, ...{authenticating: true, error: null}}; 230 } 231 232 return state; 233 } 234 235 function baseEther(state = '1', action) { 236 if (action.type === UPDATE_BASE_ETHER) { 237 return action.payload; 238 } 239 return state; 240 } 241 242 function theme(state=DARK_THEME, action) { 243 if (action.type === CHANGE_THEME[REQUEST] || (action.type === FETCH_THEME[SUCCESS] && action.theme)) { 244 return action.theme; 245 } 246 return state 247 } 248 249 function deploymentPipeline(state = DEPLOYMENT_PIPELINES.embark, action) { 250 if (action.type === UPDATE_DEPLOYMENT_PIPELINE) { 251 return action.payload; 252 } 253 return state; 254 } 255 256 function searchResult(state = {}, action) { 257 if (action.type === EXPLORER_SEARCH[SUCCESS]) { 258 return action.searchResult; 259 } 260 if (action.type === EXPLORER_SEARCH[REQUEST]) { 261 return {}; 262 } 263 return state; 264 } 265 266 const DEFAULT_MESSAGE_SIGNATURE_STATE = { 267 pending: false, 268 error: null, 269 payload: null 270 }; 271 272 function messageSignature(state = DEFAULT_MESSAGE_SIGNATURE_STATE, action) { 273 274 if (action.type === SIGN_MESSAGE[REQUEST]) { 275 return {...state, pending: true, error: null, payload: null }; 276 } 277 278 if (action.type === SIGN_MESSAGE[FAILURE]) { 279 return {...state, pending: false, error: action.signMessageError }; 280 } 281 282 if (action.type === SIGN_MESSAGE[SUCCESS]) { 283 return {...state, ...{ 284 pending: false, 285 error: null, 286 payload: { 287 signature: action.signature, 288 message: action.message, 289 signer: action.signer 290 } 291 }}; 292 } 293 294 return state; 295 } 296 297 const DEFAULT_MESSAGE_VERIFICATION_STATE = { 298 pending: false, 299 error: null, 300 payload: null 301 }; 302 303 function messageVerification(state = DEFAULT_MESSAGE_VERIFICATION_STATE, action) { 304 if (action.type === VERIFY_MESSAGE[REQUEST]) { 305 return {...state, pending: true, error: null, payload: null }; 306 } 307 308 if (action.type === VERIFY_MESSAGE[FAILURE]) { 309 return {...state, pending: false, error: action.verifyMessageError }; 310 } 311 312 if (action.type === VERIFY_MESSAGE[SUCCESS]) { 313 return {...state, ...{ 314 pending: false, 315 error: null, 316 payload: { 317 verifiedAddress: action.address 318 } 319 }}; 320 } 321 return state; 322 } 323 324 function breakpoints(state = {}, action) { 325 if (action.type === TOGGLE_BREAKPOINT[SUCCESS]) { 326 const {filename, lineNumber} = action.payload; 327 let lineNumbers = state[filename] || []; 328 if (lineNumbers.includes(lineNumber)){ 329 lineNumbers = lineNumbers.filter(ln => ln !== lineNumber); 330 } else { 331 lineNumbers.push(lineNumber); 332 } 333 return {...state, [filename]: lineNumbers}; 334 } 335 336 return state; 337 } 338 339 function web3(state = {deployments: {}, gasEstimates: {}}, action) { 340 if (action.type === WEB3_CONNECT[SUCCESS]) { 341 return {...state, instance: action.web3}; 342 } else if (action.type === WEB3_DEPLOY[REQUEST]) { 343 return {...state, deployments: {...state['deployments'], [action.contract.className]: {running: true, error: null}}}; 344 } else if (action.type === WEB3_DEPLOY[SUCCESS]){ 345 return {...state, deployments: {...state['deployments'], [action.contract.className]: {...action.receipt, running: false, error: null}}}; 346 } else if (action.type === WEB3_DEPLOY[FAILURE]){ 347 return {...state, deployments: {...state['deployments'], [action.contract.className]: {error: action.web3Error, running: false}}}; 348 } else if (action.type === WEB3_ESTIMAGE_GAS[REQUEST]){ 349 return {...state, gasEstimates: {...state['gasEstimates'], [action.contract.className]: {running: true, error: null}}}; 350 } else if (action.type === WEB3_ESTIMAGE_GAS[SUCCESS]){ 351 return {...state, gasEstimates: {...state['gasEstimates'], [action.contract.className]: {gas: action.gas, running: false, error: null}}}; 352 } else if (action.type === WEB3_ESTIMAGE_GAS[FAILURE]){ 353 return {...state, gasEstimates: {...state['gasEstimates'], [action.contract.className]: {error: action.web3Error, running: false}}}; 354 } 355 356 return state 357 } 358 359 function debuggerInfo(state={}, action) { 360 if (action.type === DEBUGGER_INFO[SUCCESS]) { 361 return action.data; 362 } 363 return state; 364 } 365 366 function editorTabs(state = [], action) { 367 if (action.type === FETCH_EDITOR_TABS[SUCCESS] && action.editorTabs) { 368 return action.editorTabs; 369 } 370 return state; 371 } 372 373 const rootReducer = combineReducers({ 374 entities, 375 loading, 376 compilingContract, 377 errorMessage, 378 errorEntities, 379 credentials, 380 baseEther, 381 searchResult, 382 messageSignature, 383 messageVerification, 384 breakpoints, 385 deploymentPipeline, 386 web3, 387 debuggerInfo, 388 theme, 389 editorTabs 390 }); 391 392 export default rootReducer;