|
| 1 | +/* eslint-disable no-param-reassign */ |
| 2 | +import produce from 'immer'; |
| 3 | +import * as types from '../constants/actionTypes.ts'; |
| 4 | + |
| 5 | + |
| 6 | +export default (state, action) => produce(state, draft => { |
| 7 | + const { port, currentTab, tabs } = draft; |
| 8 | + const { |
| 9 | + hierarchy, |
| 10 | + snapshots, |
| 11 | + mode, |
| 12 | + intervalId, |
| 13 | + viewIndex, |
| 14 | + sliderIndex, |
| 15 | + } = tabs[currentTab] || {}; |
| 16 | + |
| 17 | + |
| 18 | + // gabi and nate :: function that find the index in the hierarchy and extract the name of the equivalent index to add to the post message |
| 19 | + const findName = (index, hierarchy) => { |
| 20 | + if (hierarchy.index == index) { |
| 21 | + return hierarchy.name; |
| 22 | + } |
| 23 | + |
| 24 | + const hierarchyChildArray = []; |
| 25 | + for (const hierarchyChild of hierarchy.children) { |
| 26 | + hierarchyChildArray.push(findName(index, hierarchyChild)); |
| 27 | + } |
| 28 | + for (const hierarchyChildName of hierarchyChildArray) { |
| 29 | + if (hierarchyChildName) { |
| 30 | + return hierarchyChildName; |
| 31 | + } |
| 32 | + } |
| 33 | + }; |
| 34 | + |
| 35 | + switch (action.type) { |
| 36 | + case types.MOVE_BACKWARD: { |
| 37 | + if (snapshots.length > 0 && sliderIndex > 0) { |
| 38 | + const newIndex = sliderIndex - 1; |
| 39 | + // gabi and nate :: find the name by the newIndex parsing through the hierarchy to send to background.js the current name in the jump action |
| 40 | + const nameFromIndex = findName(newIndex, hierarchy); |
| 41 | + |
| 42 | + port.postMessage({ |
| 43 | + action: 'jumpToSnap', |
| 44 | + payload: snapshots[newIndex], |
| 45 | + index: newIndex, |
| 46 | + name: nameFromIndex, |
| 47 | + tabId: currentTab, |
| 48 | + }); |
| 49 | + clearInterval(intervalId); |
| 50 | + |
| 51 | + tabs[currentTab].sliderIndex = newIndex; |
| 52 | + tabs[currentTab].playing = false; |
| 53 | + } |
| 54 | + break; |
| 55 | + } |
| 56 | + case types.MOVE_FORWARD: { |
| 57 | + if (sliderIndex < snapshots.length - 1) { |
| 58 | + const newIndex = sliderIndex + 1; |
| 59 | + // gabi and nate :: find the name by the newIndex parsing through the hierarchy to send to background.js the current name in the jump action |
| 60 | + const nameFromIndex = findName(newIndex, hierarchy); |
| 61 | + |
| 62 | + port.postMessage({ |
| 63 | + action: 'jumpToSnap', |
| 64 | + index: newIndex, |
| 65 | + name: nameFromIndex, |
| 66 | + payload: snapshots[newIndex], |
| 67 | + tabId: currentTab, |
| 68 | + }); |
| 69 | + |
| 70 | + tabs[currentTab].sliderIndex = newIndex; |
| 71 | + |
| 72 | + // message is coming from the user |
| 73 | + if (!action.payload) { |
| 74 | + clearInterval(intervalId); |
| 75 | + tabs[currentTab].playing = false; |
| 76 | + } |
| 77 | + } |
| 78 | + break; |
| 79 | + } |
| 80 | + case types.SLIDER_ZERO: { |
| 81 | + // gabi and nate :: reset name to 0 to send to background.js the current name in the jump action |
| 82 | + port.postMessage({ |
| 83 | + action: 'jumpToSnap', |
| 84 | + index: 0, |
| 85 | + name: 0, |
| 86 | + payload: snapshots[0], |
| 87 | + tabId: currentTab, |
| 88 | + }); |
| 89 | + tabs[currentTab].sliderIndex = 0; |
| 90 | + break; |
| 91 | + } |
| 92 | + case types.CHANGE_VIEW: { |
| 93 | + // unselect view if same index was selected |
| 94 | + if (viewIndex === action.payload) tabs[currentTab].viewIndex = -1; |
| 95 | + else tabs[currentTab].viewIndex = action.payload; |
| 96 | + break; |
| 97 | + } |
| 98 | + case types.CHANGE_SLIDER: { |
| 99 | + // gabi and nate :: finds the name by the action.payload, parsing through the hierarchy to send to background.js the current name in the jump action |
| 100 | + const nameFromIndex = findName(action.payload, hierarchy); |
| 101 | + |
| 102 | + port.postMessage({ |
| 103 | + action: 'jumpToSnap', |
| 104 | + payload: snapshots[action.payload], |
| 105 | + index: action.payload, |
| 106 | + name: nameFromIndex, |
| 107 | + tabId: currentTab, |
| 108 | + }); |
| 109 | + tabs[currentTab].sliderIndex = action.payload; |
| 110 | + break; |
| 111 | + } |
| 112 | + case types.EMPTY: { |
| 113 | + port.postMessage({ action: 'emptySnap', tabId: currentTab }); |
| 114 | + tabs[currentTab].sliderIndex = 0; |
| 115 | + tabs[currentTab].viewIndex = -1; |
| 116 | + tabs[currentTab].playing = false; |
| 117 | + // gabi :: activate empty mode |
| 118 | + tabs[currentTab].mode.empty = true; |
| 119 | + // gabi :: record snapshot of page initial state |
| 120 | + tabs[currentTab].initialSnapshot.push(tabs[currentTab].snapshots[0]); |
| 121 | + // gabi :: reset snapshots to page last state recorded |
| 122 | + tabs[currentTab].snapshots = [tabs[currentTab].snapshots[tabs[currentTab].snapshots.length - 1]]; |
| 123 | + // gabi :: record hierarchy of page initial state |
| 124 | + tabs[currentTab].initialHierarchy = { ...tabs[currentTab].hierarchy }; |
| 125 | + tabs[currentTab].initialHierarchy.children = []; |
| 126 | + // gabi :: reset hierarchy |
| 127 | + tabs[currentTab].hierarchy.children = []; |
| 128 | + // gabi :: reset hierarchy to page last state recorded |
| 129 | + tabs[currentTab].hierarchy.stateSnapshot = tabs[currentTab].snapshots[0]; |
| 130 | + // gabi :: reset currLocation to page last state recorded |
| 131 | + tabs[currentTab].currLocation = tabs[currentTab].hierarchy; |
| 132 | + // gabi :: reset index |
| 133 | + tabs[currentTab].index = 0; |
| 134 | + // gabi :: reset currParent plus current state |
| 135 | + tabs[currentTab].currParent = 1; |
| 136 | + // gabi :: reset currBranch |
| 137 | + tabs[currentTab].currBranch = 0; |
| 138 | + break; |
| 139 | + } |
| 140 | + case types.SET_PORT: { |
| 141 | + draft.port = action.payload; |
| 142 | + break; |
| 143 | + } |
| 144 | + case types.IMPORT: { |
| 145 | + port.postMessage({ action: 'import', payload: action.payload, tabId: currentTab }); |
| 146 | + tabs[currentTab].snapshots = action.payload; |
| 147 | + break; |
| 148 | + } |
| 149 | + case types.TOGGLE_MODE: { |
| 150 | + mode[action.payload] = !mode[action.payload]; |
| 151 | + const newMode = mode[action.payload]; |
| 152 | + let actionText; |
| 153 | + switch (action.payload) { |
| 154 | + case 'paused': |
| 155 | + actionText = 'setPause'; |
| 156 | + break; |
| 157 | + case 'locked': |
| 158 | + actionText = 'setLock'; |
| 159 | + break; |
| 160 | + case 'persist': |
| 161 | + actionText = 'setPersist'; |
| 162 | + break; |
| 163 | + default: |
| 164 | + break; |
| 165 | + } |
| 166 | + port.postMessage({ action: actionText, payload: newMode, tabId: currentTab }); |
| 167 | + break; |
| 168 | + } |
| 169 | + case types.PAUSE: { |
| 170 | + clearInterval(intervalId); |
| 171 | + tabs[currentTab].playing = false; |
| 172 | + tabs[currentTab].intervalId = null; |
| 173 | + break; |
| 174 | + } |
| 175 | + case types.PLAY: { |
| 176 | + tabs[currentTab].playing = true; |
| 177 | + tabs[currentTab].intervalId = action.payload; |
| 178 | + break; |
| 179 | + } |
| 180 | + case types.INITIAL_CONNECT: { |
| 181 | + const { payload } = action; |
| 182 | + Object.keys(payload).forEach(tab => { |
| 183 | + // check if tab exists in memory |
| 184 | + // add new tab |
| 185 | + tabs[tab] = { |
| 186 | + ...payload[tab], |
| 187 | + sliderIndex: 0, |
| 188 | + viewIndex: -1, |
| 189 | + intervalId: null, |
| 190 | + playing: false, |
| 191 | + }; |
| 192 | + }); |
| 193 | + |
| 194 | + // only set first tab if current tab is non existent |
| 195 | + const firstTab = parseInt(Object.keys(payload)[0], 10); |
| 196 | + if (currentTab === undefined || currentTab === null) draft.currentTab = firstTab; |
| 197 | + break; |
| 198 | + } |
| 199 | + case types.NEW_SNAPSHOTS: { |
| 200 | + const { payload } = action; |
| 201 | + |
| 202 | + Object.keys(tabs).forEach(tab => { |
| 203 | + if (!payload[tab]) { |
| 204 | + delete tabs[tab]; |
| 205 | + } else { |
| 206 | + const { snapshots: newSnaps } = payload[tab]; |
| 207 | + tabs[tab] = { |
| 208 | + ...tabs[tab], |
| 209 | + ...payload[tab], |
| 210 | + sliderIndex: newSnaps.length - 1, |
| 211 | + }; |
| 212 | + } |
| 213 | + }); |
| 214 | + |
| 215 | + // only set first tab if current tab is non existent |
| 216 | + const firstTab = parseInt(Object.keys(payload)[0], 10); |
| 217 | + if (currentTab === undefined || currentTab === null) draft.currentTab = firstTab; |
| 218 | + break; |
| 219 | + } |
| 220 | + case types.SET_TAB: { |
| 221 | + if (typeof action.payload === 'number') { |
| 222 | + draft.currentTab = action.payload; |
| 223 | + break; |
| 224 | + } else if (typeof action.payload === 'object') { |
| 225 | + draft.currentTab = action.payload.tabId; |
| 226 | + break; |
| 227 | + } |
| 228 | + break; |
| 229 | + } |
| 230 | + case types.DELETE_TAB: { |
| 231 | + delete draft.tabs[action.payload]; |
| 232 | + if (draft.currentTab === action.payload) { |
| 233 | + // if the deleted tab was set to currentTab, replace currentTab with |
| 234 | + // the first tabId within tabs obj |
| 235 | + const newCurrentTab = parseInt(Object.keys(draft.tabs)[0], 10); |
| 236 | + draft.currentTab = newCurrentTab; |
| 237 | + } |
| 238 | + break; |
| 239 | + } |
| 240 | + default: |
| 241 | + throw new Error(`nonexistent action: ${action.type}`); |
| 242 | + } |
| 243 | +}); |
0 commit comments