Skip to content

Commit e4c5c68

Browse files
committed
Fix: Keep typing scheduling invariant from version < 2
Before version 2, characters would be typed in the following order: 1. Update characters and lines state (i.e. render character in a line) 2. After being rendered, call onCharacterTyped 3. Generate a new delay for the given line and character that was just rendered 4. Set a timeout with the generated delay After the introduction of Backspace and Delay elements in v2, the order in which characters were typed was changed, changing the invariant of when the delayGenerator was called and how delays were scheduled. This was due to conflation between the newly introduced delays and the normal typing scheduling. This commit fixes that
1 parent 8e759d6 commit e4c5c68

File tree

3 files changed

+40
-34
lines changed

3 files changed

+40
-34
lines changed

src/Typist.jsx

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export default class Typist extends Component {
3939
super(props);
4040
this.mounted = false;
4141
this.linesToType = [];
42-
this.delay = null;
42+
this.introducedDelay = null;
4343

4444
if (props.children) {
4545
this.linesToType = utils.extractTextFromElement(props.children);
@@ -124,11 +124,11 @@ export default class Typist extends Component {
124124
if (isBackspaceOrDelayElement) {
125125
if (line.type && line.type.name === 'Backspace') {
126126
if (line.props.delay > 0) {
127-
this.delay = line.props.delay;
127+
this.introducedDelay = line.props.delay;
128128
}
129129
decoratedLine = String('🔙').repeat(line.props.count);
130130
} else if (line.type && line.type.name === 'Delay') {
131-
this.delay = line.props.ms;
131+
this.introducedDelay = line.props.ms;
132132
decoratedLine = '⏰';
133133
}
134134
}
@@ -150,36 +150,40 @@ export default class Typist extends Component {
150150
return new Promise((resolve) => {
151151
const textLines = this.state.textLines.slice();
152152

153-
const isBackspace = character === '🔙';
154-
const isDelay = character === '⏰';
155-
if (isDelay) {
156-
resolve();
157-
return;
158-
}
153+
utils.sleep(this.introducedDelay)
154+
.then(() => {
155+
this.introducedDelay = null;
156+
157+
const isBackspace = character === '🔙';
158+
const isDelay = character === '⏰';
159+
if (isDelay) {
160+
resolve();
161+
return;
162+
}
159163

160-
if (isBackspace && lineIdx > 0) {
161-
let prevLineIdx = lineIdx - 1;
162-
let prevLine = textLines[prevLineIdx];
164+
if (isBackspace && lineIdx > 0) {
165+
let prevLineIdx = lineIdx - 1;
166+
let prevLine = textLines[prevLineIdx];
163167

164-
for (let idx = prevLineIdx; idx >= 0; idx --) {
165-
if (prevLine.length > 0 && !ACTION_CHARS.includes(prevLine[0])) {
166-
break;
168+
for (let idx = prevLineIdx; idx >= 0; idx --) {
169+
if (prevLine.length > 0 && !ACTION_CHARS.includes(prevLine[0])) {
170+
break;
171+
}
172+
prevLineIdx = idx;
173+
prevLine = textLines[prevLineIdx];
167174
}
168-
prevLineIdx = idx;
169-
prevLine = textLines[prevLineIdx];
170-
}
171175

172-
textLines[prevLineIdx] = prevLine.substr(0, prevLine.length - 1);
173-
} else {
174-
textLines[lineIdx] += character;
175-
}
176+
textLines[prevLineIdx] = prevLine.substr(0, prevLine.length - 1);
177+
} else {
178+
textLines[lineIdx] += character;
179+
}
176180

177-
const delay = this.delay || this.delayGenerator(line, lineIdx, character, charIdx);
178-
setTimeout(() => this.setState({ textLines }, () => {
179-
this.delay = null;
180-
onCharacterTyped(character, charIdx);
181-
resolve();
182-
}), delay);
181+
this.setState({ textLines }, () => {
182+
const delay = this.delayGenerator(line, lineIdx, character, charIdx);
183+
onCharacterTyped(character, charIdx);
184+
setTimeout(resolve, delay);
185+
});
186+
});
183187
});
184188
}
185189

src/utils.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import React from 'react';
22

3-
export const sleep = (val) => new Promise((resolve) => setTimeout(resolve, val));
3+
export const sleep = (val) => new Promise((resolve) => (
4+
val != null ? setTimeout(resolve, val) : resolve()
5+
));
46

57
export function gaussianRnd(mean, std) {
68
const times = 12;

test/Typist.spec.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ describe('Typist', () => {
4949
describe('when children passed', () => {
5050
const assertLine = (inst, line, acum = '') => {
5151
for (let idx = 1; idx <= line.length; idx++) {
52+
expect(findDOMNode(inst).textContent).toEqual(`${acum}${line.slice(0, idx)}|`);
5253
jasmine.clock().tick(100);
5354
Promise.runAll();
54-
expect(findDOMNode(inst).textContent).toEqual(`${acum}${line.slice(0, idx)}|`);
5555
}
5656
};
5757

@@ -171,13 +171,13 @@ describe('Typist', () => {
171171

172172
assertLines(inst, strs);
173173

174+
expect(findDOMNode(inst).textContent).toEqual('Test1Test|');
174175
jasmine.clock().tick(100);
175176
Promise.runAll();
176-
expect(findDOMNode(inst).textContent).toEqual('Test1Test|');
177177

178+
expect(findDOMNode(inst).textContent).toEqual('Test1Tes|');
178179
jasmine.clock().tick(100);
179180
Promise.runAll();
180-
expect(findDOMNode(inst).textContent).toEqual('Test1Tes|');
181181

182182
const span = TestUtils.scryRenderedDOMComponentsWithTag(inst, 'span')[0];
183183
expect(findDOMNode(span).textContent).toEqual('Tes');
@@ -194,13 +194,13 @@ describe('Typist', () => {
194194

195195
assertLines(inst, strs);
196196

197+
expect(findDOMNode(inst).textContent).toEqual('Test1|');
197198
jasmine.clock().tick(100);
198199
Promise.runAll();
199-
expect(findDOMNode(inst).textContent).toEqual('Test1|');
200200

201+
expect(findDOMNode(inst).textContent).toEqual('Test|');
201202
jasmine.clock().tick(100);
202203
Promise.runAll();
203-
expect(findDOMNode(inst).textContent).toEqual('Test|');
204204
});
205205

206206
it('correctly uses a delay when backspacing if specified', () => {

0 commit comments

Comments
 (0)