Skip to content

Commit 5f7298f

Browse files
committed
Use flatted instead of CircularJSON
- Will break old localStorage caches.
1 parent b794f98 commit 5f7298f

File tree

6 files changed

+78
-65
lines changed

6 files changed

+78
-65
lines changed

lib/schemas/schema.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import textSchema from './textSchema';
33
import optionsSchema from './optionsSchema';
44
import customSchema from './customSchema';
55
import updateSchema from './updateSchema';
6-
7-
const JSON = require('circular-json');
6+
import { stringify } from 'flatted';
87

98
const schema = {
109
parse(step) {
@@ -21,14 +20,14 @@ const schema = {
2120
} else if (step.update) {
2221
parser = updateSchema;
2322
} else {
24-
throw new Error(`The step ${JSON.stringify(step)} is invalid`);
23+
throw new Error(`The step ${stringify(step)} is invalid`);
2524
}
2625

2726
for (let i = 0, len = parser.length; i < len; i += 1) {
2827
const { key, types, required } = parser[i];
2928

3029
if (!step[key] && required) {
31-
throw new Error(`Key '${key}' is required in step ${JSON.stringify(step)}`);
30+
throw new Error(`Key '${key}' is required in step ${stringify(step)}`);
3231
} else if (step[key]) {
3332
if (types[0] !== 'any' && types.indexOf(typeof step[key]) < 0) {
3433
throw new Error(

lib/storage.js

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const JSON = require('circular-json');
1+
import { stringify, parse } from 'flatted';
22

33
/* istanbul ignore next */
44
const getData = (params, callback) => {
@@ -7,54 +7,59 @@ const getData = (params, callback) => {
77
const renderedSteps = [steps[currentStep.id]];
88
const previousSteps = [steps[currentStep.id]];
99
const previousStep = {};
10+
const unParsedCache = localStorage.getItem(cacheName);
1011

11-
if (cache && localStorage.getItem(cacheName)) {
12-
const data = JSON.parse(localStorage.getItem(cacheName));
13-
const lastStep = data.renderedSteps[data.renderedSteps.length - 1];
12+
if (cache && unParsedCache) {
13+
try {
14+
const data = parse(unParsedCache);
15+
const lastStep = data.renderedSteps[data.renderedSteps.length - 1];
1416

15-
if (lastStep && lastStep.end) {
16-
localStorage.removeItem(cacheName);
17-
} else {
18-
for (let i = 0, len = data.renderedSteps.length; i < len; i += 1) {
19-
const renderedStep = data.renderedSteps[i];
20-
// remove delay of cached rendered steps
21-
data.renderedSteps[i].delay = 0;
22-
// flag used to avoid call triggerNextStep in cached rendered steps
23-
data.renderedSteps[i].rendered = true;
17+
if (lastStep && lastStep.end) {
18+
localStorage.removeItem(cacheName);
19+
} else {
20+
for (let i = 0, len = data.renderedSteps.length; i < len; i += 1) {
21+
const renderedStep = data.renderedSteps[i];
22+
// remove delay of cached rendered steps
23+
data.renderedSteps[i].delay = 0;
24+
// flag used to avoid call triggerNextStep in cached rendered steps
25+
data.renderedSteps[i].rendered = true;
2426

25-
// an error is thrown when render a component from localStorage.
26-
// So it's necessary reassing the component
27-
if (renderedStep.component) {
28-
const { id } = renderedStep;
29-
data.renderedSteps[i].component = steps[id].component;
27+
// an error is thrown when render a component from localStorage.
28+
// So it's necessary reassing the component
29+
if (renderedStep.component) {
30+
const { id } = renderedStep;
31+
data.renderedSteps[i].component = steps[id].component;
32+
}
3033
}
31-
}
3234

33-
const { trigger, end, options } = data.currentStep;
34-
const { id } = data.currentStep;
35+
const { trigger, end, options } = data.currentStep;
36+
const { id } = data.currentStep;
3537

36-
if (options) {
37-
delete data.currentStep.rendered;
38-
}
39-
40-
// add trigger function to current step
41-
if (!trigger && !end) {
4238
if (options) {
43-
for (let i = 0; i < options.length; i += 1) {
44-
data.currentStep.options[i].trigger = steps[id].options[i].trigger;
39+
delete data.currentStep.rendered;
40+
}
41+
42+
// add trigger function to current step
43+
if (!trigger && !end) {
44+
if (options) {
45+
for (let i = 0; i < options.length; i += 1) {
46+
data.currentStep.options[i].trigger = steps[id].options[i].trigger;
47+
}
48+
} else {
49+
data.currentStep.trigger = steps[id].trigger;
4550
}
46-
} else {
47-
data.currentStep.trigger = steps[id].trigger;
4851
}
49-
}
5052

51-
// execute callback function to enable input if last step is
52-
// waiting user type
53-
if (data.currentStep.user) {
54-
callback();
55-
}
53+
// execute callback function to enable input if last step is
54+
// waiting user type
55+
if (data.currentStep.user) {
56+
callback();
57+
}
5658

57-
return data;
59+
return data;
60+
}
61+
} catch (error) {
62+
console.info(`Unable to parse cache named:${cacheName}. \nThe cache where probably created with an older version of react-simple-chatbot.\n`, error);
5863
}
5964
}
6065

@@ -68,7 +73,7 @@ const getData = (params, callback) => {
6873

6974
/* istanbul ignore next */
7075
const setData = (cacheName, cachedData) => {
71-
const data = JSON.parse(JSON.stringify(cachedData));
76+
const data = parse(stringify(cachedData));
7277
// clean components
7378
for (const key in data) {
7479
for (let i = 0, len = data[key].length; i < len; i += 1) {
@@ -78,7 +83,7 @@ const setData = (cacheName, cachedData) => {
7883
}
7984
}
8085

81-
localStorage.setItem(cacheName, JSON.stringify(data));
86+
localStorage.setItem(cacheName, stringify(data));
8287
};
8388

8489
export { getData, setData };

package-lock.json

Lines changed: 14 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@
8484
"webpack-dev-server": "^3.1.10"
8585
},
8686
"dependencies": {
87-
"circular-json": "^0.3.1",
8887
"eslint-config-prettier": "^4.1.0",
8988
"eslint-plugin-prettier": "^3.0.1",
9089
"onchange": "^5.2.0",
9190
"prettier": "^1.16.4",
91+
"flatted": "^2.0.0",
9292
"prop-types": "^15.6.0",
9393
"random-id": "0.0.2",
9494
"react": "^16.4.1",
@@ -99,4 +99,4 @@
9999
"react": "^16.3.0",
100100
"react-dom": "^16.3.0"
101101
}
102-
}
102+
}

tests/lib/ChatBot.spec.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
import { CloseIcon } from '../../lib/icons';
1313
import { TextStep } from '../../lib/steps_components';
1414

15+
import { parse } from 'flatted';
16+
1517
const CustomComponent = () => (
1618
<div />
1719
);
@@ -24,7 +26,7 @@ describe('ChatBot', () => {
2426
botDelay={0}
2527
userDelay={0}
2628
customDelay={0}
27-
handleEnd={() => {}}
29+
handleEnd={() => { }}
2830
steps={[
2931
{
3032
id: '1',
@@ -125,7 +127,7 @@ describe('ChatBot', () => {
125127
botDelay={0}
126128
userDelay={0}
127129
customDelay={0}
128-
handleEnd={() => {}}
130+
handleEnd={() => { }}
129131
steps={[
130132
{
131133
id: '1',
@@ -148,7 +150,7 @@ describe('ChatBot', () => {
148150
botDelay={0}
149151
userDelay={0}
150152
customDelay={0}
151-
handleEnd={() => {}}
153+
handleEnd={() => { }}
152154
steps={[
153155
{
154156
id: '1',
@@ -173,7 +175,7 @@ describe('ChatBot', () => {
173175
cache={true}
174176
userDelay={0}
175177
customDelay={0}
176-
handleEnd={() => {}}
178+
handleEnd={() => { }}
177179
steps={[
178180
{
179181
id: '1',
@@ -205,7 +207,7 @@ describe('ChatBot', () => {
205207
});
206208

207209
it('should cache the steps', () => {
208-
const data = JSON.parse(localStorage.getItem('rsc_cache'));
210+
const data = parse(localStorage.getItem('rsc_cache'));
209211
expect(data.renderedSteps.length).to.be.equal(2);
210212
});
211213
});
@@ -241,7 +243,7 @@ describe('ChatBot', () => {
241243
botDelay={0}
242244
userDelay={0}
243245
customDelay={0}
244-
handleEnd={() => {}}
246+
handleEnd={() => { }}
245247
steps={[
246248
{
247249
id: '1',

tests/lib/schema.spec.js

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,25 @@ import React from 'react';
22
import { describe, it } from 'mocha';
33
import { expect } from 'chai';
44
import schema from '../../lib/schemas/schema';
5-
6-
const JSON = require('circular-json');
5+
import { stringify } from 'flatted';
76

87
describe('schema', () => {
98
it('should throw a invalid step error', () => {
109
const step = { test: 'test' };
1110
expect(() => {
1211
schema.parse(step);
13-
}).to.throw(Error, `The step ${JSON.stringify(step)} is invalid`);
12+
}).to.throw(Error, `The step ${stringify(step)} is invalid`);
1413
});
1514

1615
it('should throw a key required error', () => {
1716
const step = { message: 'test' };
1817
expect(() => {
1918
schema.parse(step);
20-
}).to.throw(Error, `Key 'id' is required in step ${JSON.stringify(step)}`);
19+
}).to.throw(Error, `Key 'id' is required in step ${stringify(step)}`);
2120
});
2221

2322
it('should throw a key type error', () => {
24-
const step = { id: () => {}, options: [] };
23+
const step = { id: () => { }, options: [] };
2524
expect(() => {
2625
schema.parse(step);
2726
}).to.throw(Error, 'The type of \'id\' value must be string or number instead of function');
@@ -33,8 +32,8 @@ describe('schema', () => {
3332
message: 'test',
3433
test: 'test',
3534
});
36-
const resultStep = JSON.stringify({ id: '1', message: 'test' });
37-
expect(JSON.stringify(step)).to.be.equal(resultStep);
35+
const resultStep = stringify({ id: '1', message: 'test' });
36+
expect(stringify(step)).to.be.equal(resultStep);
3837
});
3938

4039
it('should not throw error to a user step', () => {

0 commit comments

Comments
 (0)