Skip to content

Commit dcf7b03

Browse files
authored
Merge pull request #541 from oskarhane/3.0-maxFrames
Listen for config maxFrames and enforce that limit in stream
2 parents 4e0bd25 + 4f41722 commit dcf7b03

File tree

7 files changed

+227
-14
lines changed

7 files changed

+227
-14
lines changed

src/browser/modules/Sidebar/Settings.jsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ const visualSettings =
6565
{
6666
title: 'Result Frames',
6767
settings: [
68+
{
69+
maxFrames: {
70+
displayName: 'Maximum number of result frames',
71+
tooltip: 'Max number of result frames. When reached, old frames gets retired.'
72+
}
73+
},
6874
{
6975
maxHistory: {
7076
displayName: 'Max History',

src/shared/modules/settings/settingsDuck.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ const initialState = {
6565
maxRows: 1000,
6666
shouldReportUdc: true,
6767
autoComplete: true,
68-
scrollToTop: true
68+
scrollToTop: true,
69+
maxFrames: 30
6970
}
7071

7172
export default function settings (state = initialState, action) {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`streamDuck cuts the number of frames when config is set 1`] = `
4+
Array [
5+
1,
6+
]
7+
`;
8+
9+
exports[`streamDuck cuts the number of frames when config is set 2`] = `
10+
Object {
11+
"1": Object {
12+
"id": 1,
13+
},
14+
}
15+
`;
16+
17+
exports[`streamDuck dont remove pinned frames when cutting frames 1`] = `
18+
Array [
19+
1,
20+
2,
21+
]
22+
`;
23+
24+
exports[`streamDuck dont remove pinned frames when cutting frames 2`] = `
25+
Object {
26+
"1": Object {
27+
"isPinned": 1,
28+
},
29+
"2": Object {
30+
"isPinned": 1,
31+
},
32+
}
33+
`;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (c) 2002-2017 "Neo Technology,"
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Neo4j is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
/* global describe, afterEach, test, expect, beforeAll */
22+
import configureMockStore from 'redux-mock-store'
23+
import { createEpicMiddleware } from 'redux-observable'
24+
import { createBus, createReduxMiddleware } from 'suber'
25+
26+
import * as stream from './streamDuck'
27+
import { update as updateSettings } from 'shared/modules/settings/settingsDuck'
28+
29+
const bus = createBus()
30+
const epicMiddleware = createEpicMiddleware(stream.maxFramesConfigEpic)
31+
const mockStore = configureMockStore([epicMiddleware, createReduxMiddleware(bus)])
32+
33+
describe('streamDuckEpics', () => {
34+
let store
35+
beforeAll(() => {
36+
store = mockStore({
37+
settings: {
38+
cmdchar: ':',
39+
maxFrames: 50
40+
},
41+
frames: {
42+
allIds: [],
43+
byId: {},
44+
maxFrames: 50
45+
}
46+
})
47+
})
48+
afterEach(() => {
49+
store.clearActions()
50+
bus.reset()
51+
})
52+
53+
test('listens on UPDATE and sets new maxFrames', (done) => {
54+
// Given
55+
const action = updateSettings({maxFrames: 3})
56+
bus.take('NOOP', (currentAction) => {
57+
// Then
58+
expect(store.getActions()).toEqual([
59+
action,
60+
{ type: stream.SET_MAX_FRAMES, maxFrames: 3 },
61+
{ type: 'NOOP' }
62+
])
63+
done()
64+
})
65+
66+
// When
67+
store.dispatch(action)
68+
})
69+
})

src/shared/modules/stream/streamDuck.js

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@
1919
*/
2020

2121
import uuid from 'uuid'
22+
import 'rxjs/add/operator/do'
23+
import 'rxjs/add/operator/mapTo'
2224
import { moveInArray } from 'services/utils'
2325
import { hydrate } from 'services/duckUtils'
26+
import { UPDATE as SETTINGS_UPDATE } from '../settings/settingsDuck'
2427

2528
export const NAME = 'frames'
2629
export const ADD = 'frames/ADD'
@@ -31,12 +34,7 @@ export const FRAME_TYPE_FILTER_UPDATED = 'frames/FRAME_TYPE_FILTER_UPDATED'
3134
export const PIN = `${NAME}/PIN`
3235
export const UNPIN = `${NAME}/UNPIN`
3336
export const SET_RECENT_VIEW = 'frames/SET_RECENT_VIEW'
34-
35-
const initialState = {
36-
allIds: [],
37-
byId: {},
38-
recentView: null
39-
}
37+
export const SET_MAX_FRAMES = NAME + '/SET_MAX_FRAMES'
4038

4139
/**
4240
* Selectors
@@ -63,12 +61,11 @@ function addFrame (state, newState) {
6361
const pos = findFirstFreePos(state)
6462
allIds.splice(pos, 0, newState.id)
6563
}
66-
return Object.assign(
67-
{},
68-
state,
69-
{allIds: allIds},
70-
{byId: byId}
71-
)
64+
return ensureFrameLimit({
65+
...state,
66+
allIds,
67+
byId
68+
})
7269
}
7370

7471
function removeFrame (state, id) {
@@ -129,6 +126,28 @@ function setRecentViewHelper (state, recentView) {
129126
return Object.assign({}, state, {recentView})
130127
}
131128

129+
function ensureFrameLimit (state) {
130+
const limit = state.maxFrames || 1
131+
if (state.allIds.length <= limit) return state
132+
let numToRemove = state.allIds.length - limit
133+
let removeIds = state.allIds.slice(-1 * numToRemove).filter((id) => !state.byId[id].isPinned)
134+
let byId = {...state.byId}
135+
removeIds.forEach((id) => delete byId[id])
136+
return {
137+
...state,
138+
allIds: state.allIds.slice(0, state.allIds.length - removeIds.length),
139+
byId
140+
}
141+
}
142+
143+
/** Inital state */
144+
export const initialState = {
145+
allIds: [],
146+
byId: {},
147+
recentView: null,
148+
maxFrames: 30
149+
}
150+
132151
/**
133152
* Reducer
134153
*/
@@ -150,6 +169,9 @@ export default function reducer (state = initialState, action) {
150169
return unpinFrame(state, action.id)
151170
case SET_RECENT_VIEW:
152171
return setRecentViewHelper(state, action.view)
172+
case SET_MAX_FRAMES:
173+
const newState = {...state, maxFrames: action.maxFrames}
174+
return ensureFrameLimit(newState)
153175
default:
154176
return state
155177
}
@@ -203,3 +225,13 @@ export function setRecentView (view) {
203225
view
204226
}
205227
}
228+
229+
// Epics
230+
export const maxFramesConfigEpic = (action$, store) =>
231+
action$.ofType(SETTINGS_UPDATE)
232+
.do((action) => {
233+
const newMaxFrames = action.state.maxFrames
234+
if (!newMaxFrames) return
235+
store.dispatch({ type: SET_MAX_FRAMES, maxFrames: newMaxFrames })
236+
})
237+
.mapTo({ type: 'NOOP' })
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) 2002-2017 "Neo Technology,"
3+
* Network Engine for Objects in Lund AB [http://neotechnology.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Neo4j is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
/* global describe, test, expect */
22+
import reducer, { add, SET_MAX_FRAMES, initialState } from './streamDuck'
23+
24+
describe('streamDuck', () => {
25+
test('limits the number of frames in the reducer', () => {
26+
// Given
27+
const init = {...initialState, maxFrames: 1}
28+
const action = add({cmd: 'xxx', id: 1})
29+
const action2 = add({cmd: 'yyy', id: 2})
30+
31+
// When
32+
const newState = reducer(init, action)
33+
34+
// Then
35+
expect(newState.allIds.length).toBe(1)
36+
37+
// When
38+
const newState2 = reducer(newState, action2)
39+
40+
// Then
41+
expect(newState2.allIds.length).toBe(1)
42+
})
43+
test('cuts the number of frames when config is set', () => {
44+
// Given
45+
const init = {...initialState, maxFrames: 2, allIds: [1, 2], byId: {'1': {id: 1}, '2': {id: 2}}}
46+
const action = { type: SET_MAX_FRAMES, maxFrames: 1 }
47+
48+
// When
49+
const newState = reducer(init, action)
50+
51+
// Then
52+
expect(newState.allIds.length).toBe(1)
53+
expect(newState.allIds).toMatchSnapshot()
54+
expect(newState.byId).toMatchSnapshot()
55+
})
56+
test('dont remove pinned frames when cutting frames', () => {
57+
// Given
58+
const byId = {'1': {isPinned: 1}, '2': {isPinned: 1}}
59+
const init = {...initialState, maxFrames: 2, allIds: [1, 2], byId}
60+
const action = { type: SET_MAX_FRAMES, maxFrames: 1 }
61+
62+
// When
63+
const newState = reducer(init, action)
64+
65+
// Then
66+
expect(newState.allIds.length).toBe(2)
67+
expect(newState.allIds).toMatchSnapshot()
68+
expect(newState.byId).toMatchSnapshot()
69+
})
70+
})

src/shared/rootEpic.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { featuresDiscoveryEpic } from './modules/features/featuresDuck'
3131
import { syncItemsEpic, clearSyncEpic, syncFavoritesEpic, loadFavoritesFromSyncEpic, loadFoldersFromSyncEpic, syncFoldersEpic } from './modules/sync/syncDuck'
3232
import { credentialsTimeoutEpic } from './modules/credentialsPolicy/credentialsPolicyDuck'
3333
import { bootEpic, incrementEventEpic, udcStartupEpic, trackSyncLogoutEpic, trackConnectsEpic, eventFiredEpic } from './modules/udc/udcDuck'
34+
import { maxFramesConfigEpic } from './modules/stream/streamDuck'
3435

3536
export default combineEpics(
3637
handleCommandsEpic,
@@ -68,5 +69,6 @@ export default combineEpics(
6869
incrementEventEpic,
6970
trackSyncLogoutEpic,
7071
trackConnectsEpic,
71-
eventFiredEpic
72+
eventFiredEpic,
73+
maxFramesConfigEpic
7274
)

0 commit comments

Comments
 (0)