Skip to content
This repository was archived by the owner on Nov 19, 2018. It is now read-only.

Commit 09cc16d

Browse files
author
rnicholus
committed
fix(PauseResumeButton): concurrent chunking & resume is not properly supported
fixes #151
1 parent 03983fb commit 09cc16d

File tree

3 files changed

+83
-42
lines changed

3 files changed

+83
-42
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-fine-uploader",
3-
"version": "1.0.6",
3+
"version": "1.0.7",
44
"license": "MIT",
55
"description": "React UI components for using Fine Uploader in a React-based project.",
66
"author": {

src/pause-resume-button.jsx

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class PauseResumeButton extends Component {
1818
super(props)
1919

2020
this.state = {
21+
atLeastOneChunkUploaded: false,
2122
pausable: false,
2223
resumable: false
2324
}
@@ -26,25 +27,24 @@ class PauseResumeButton extends Component {
2627

2728
this._onStatusChange = (id, oldStatus, newStatus) => {
2829
if (id === this.props.id && !this._unmounted) {
29-
const pausable = newStatus === statusEnum.UPLOADING
30-
const resumable = !pausable && newStatus === statusEnum.PAUSED
30+
const pausable = newStatus === statusEnum.UPLOADING && this.state.atLeastOneChunkUploaded
31+
const resumable = newStatus === statusEnum.PAUSED
3132

32-
if (!pausable && this.state.pausable) {
33-
this.setState({ pausable, resumable })
33+
if (pausable !== this.state.pausable) {
34+
this.setState({ pausable })
3435
}
35-
else if (resumable && !this.state.resumable) {
36-
this.setState({ pausable, resumable })
36+
if (resumable !== this.state.resumable) {
37+
this.setState({ resumable })
3738
}
38-
else if (!resumable && this.state.resumable) {
39-
this.setState({ pausable, resumable })
40-
}
41-
else if (
39+
40+
if (
4241
newStatus === statusEnum.DELETED
4342
|| newStatus === statusEnum.CANCELED
4443
|| newStatus === statusEnum.UPLOAD_SUCCESSFUL
4544
) {
46-
this._unregisterStatusChangeHandler()
47-
this._unregisterOnUploadChunkHandler()
45+
this._unregisterOnResumeHandler()
46+
this._unregisterOnStatusChangeHandler()
47+
this._unregisterOnUploadChunkSuccessHandler()
4848
}
4949
}
5050
}
@@ -58,34 +58,45 @@ class PauseResumeButton extends Component {
5858
}
5959
}
6060

61-
this._onUploadChunk = (id, name, chunkData) => {
62-
if (id === this.props.id && !this._unmounted) {
63-
if (chunkData.partIndex > 0 && !this.state.pausable) {
64-
this.setState({
65-
pausable: true,
66-
resumable: false
67-
})
68-
}
69-
else if (chunkData.partIndex === 0 && this.state.pausable) {
70-
this.setState({
71-
pausable: false,
72-
resumable: false
73-
})
74-
}
61+
this._onResume = id => {
62+
if (id === this.props.id
63+
&& !this._unmounted
64+
&& !this.state.atLeastOneChunkUploaded) {
65+
66+
this.setState({
67+
atLeastOneChunkUploaded: true,
68+
pausable: true,
69+
resumable: false
70+
})
71+
}
72+
}
73+
74+
this._onUploadChunkSuccess = id => {
75+
if (id === this.props.id
76+
&& !this._unmounted
77+
&& !this.state.atLeastOneChunkUploaded) {
78+
79+
this.setState({
80+
atLeastOneChunkUploaded: true,
81+
pausable: true,
82+
resumable: false
83+
})
7584
}
7685
}
7786
}
7887

7988

8089
componentDidMount() {
90+
this.props.uploader.on('resume', this._onResume)
8191
this.props.uploader.on('statusChange', this._onStatusChange)
82-
this.props.uploader.on('uploadChunk', this._onUploadChunk)
92+
this.props.uploader.on('uploadChunkSuccess', this._onUploadChunkSuccess)
8393
}
8494

8595
componentWillUnmount() {
8696
this._unmounted = true
97+
this._unregisterOnResumeHandler()
8798
this._unregisterOnStatusChangeHandler()
88-
this._unregisterOnUploadChunkHandler()
99+
this._unregisterOnUploadChunkSuccessHandler()
89100
}
90101

91102
render() {
@@ -108,16 +119,16 @@ class PauseResumeButton extends Component {
108119
return null
109120
}
110121

111-
_unregisterOnStatusChangeHandler() {
112-
this.props.uploader.off('statusChange', this._onStatusChange)
122+
_unregisterOnResumeHandler() {
123+
this.props.uploader.off('resume', this._onResume)
113124
}
114125

115-
_unregisterOnUploadChunkHandler() {
116-
this.props.uploader.off('uploadChunk', this._onUploadChunk)
126+
_unregisterOnStatusChangeHandler() {
127+
this.props.uploader.off('statusChange', this._onStatusChange)
117128
}
118129

119-
_unregisterStatusChangeHandler() {
120-
this.props.uploader.off('statusChange', this._onStatusChange)
130+
_unregisterOnUploadChunkSuccessHandler() {
131+
this.props.uploader.off('uploadChunkSuccess', this._onUploadChunkSuccess)
121132
}
122133
}
123134

src/test/unit/pause-resume-button.spec.jsx

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ import FineUploaderTraditional from 'fine-uploader-wrappers'
55
import PauseResumeButton from 'src/pause-resume-button'
66

77
describe('<PauseResumeButton />', () => {
8-
let statusChangeCallback, uploadChunkCallback, uploader
8+
let resumeCallback,
9+
statusChangeCallback,
10+
uploadChunkSuccessCallback,
11+
uploader
912

1013
beforeEach(() => {
1114
uploader = new FineUploaderTraditional({options: {}})
@@ -14,8 +17,11 @@ describe('<PauseResumeButton />', () => {
1417
if (type === 'statusChange') {
1518
statusChangeCallback = callback
1619
}
17-
else if (type === 'uploadChunk') {
18-
uploadChunkCallback = callback
20+
else if (type === 'uploadChunkSuccess') {
21+
uploadChunkSuccessCallback = callback
22+
}
23+
else if (type === 'resume') {
24+
resumeCallback = callback
1925
}
2026
})
2127
})
@@ -25,11 +31,10 @@ describe('<PauseResumeButton />', () => {
2531
<PauseResumeButton id={ 0 } uploader={ uploader } />
2632
)
2733

28-
uploadChunkCallback(0, 'foo.jpeg', { partIndex: 0 })
2934
let button = TestUtils.scryRenderedDOMComponentsWithClass(PauseResumeButtonComponent, 'react-fine-uploader-pause-button')[0]
3035
expect(button).toBeFalsy()
3136

32-
uploadChunkCallback(0, 'foo.jpeg', { partIndex: 1 })
37+
uploadChunkSuccessCallback(0, { partIndex: 3 })
3338
button = TestUtils.scryRenderedDOMComponentsWithClass(PauseResumeButtonComponent, 'react-fine-uploader-pause-button')[0]
3439
expect(button).toBeTruthy()
3540
})
@@ -39,9 +44,9 @@ describe('<PauseResumeButton />', () => {
3944
<PauseResumeButton id={ 0 } uploader={ uploader } />
4045
)
4146

42-
uploadChunkCallback(0, 'foo.jpeg', { partIndex: 1 })
47+
uploadChunkSuccessCallback(0, { partIndex: 1 })
4348
statusChangeCallback(0, null, 'deleted')
44-
let button = TestUtils.scryRenderedDOMComponentsWithClass(PauseResumeButtonComponent, 'react-fine-uploader-pause-button')[0]
49+
const button = TestUtils.scryRenderedDOMComponentsWithClass(PauseResumeButtonComponent, 'react-fine-uploader-pause-button')[0]
4550
expect(button).toBeFalsy()
4651
})
4752

@@ -50,17 +55,42 @@ describe('<PauseResumeButton />', () => {
5055
<PauseResumeButton id={ 0 } uploader={ uploader } />
5156
)
5257

58+
uploadChunkSuccessCallback(0, { partIndex: 7 })
59+
5360
statusChangeCallback(0, null, 'paused')
5461
let button = TestUtils.scryRenderedDOMComponentsWithClass(PauseResumeButtonComponent, 'react-fine-uploader-resume-button')[0]
5562
expect(button).toBeTruthy()
5663

5764
const resumeUploadMethod = spyOn(uploader.methods, 'continueUpload')
5865
TestUtils.Simulate.click(button)
5966
expect(resumeUploadMethod).toHaveBeenCalledWith(0)
67+
6068
statusChangeCallback(0, null, 'uploading')
6169
button = TestUtils.scryRenderedDOMComponentsWithClass(PauseResumeButtonComponent, 'react-fine-uploader-pause-resume-button')[0]
6270
expect(button).toBeTruthy()
6371
expect(button.className.indexOf('react-fine-uploader-pause-button')).not.toBe(-1)
6472
expect(button.className.indexOf('react-fine-uploader-resume-button')).toBe(-1)
73+
74+
const pauseUploadMethod = spyOn(uploader.methods, 'pauseUpload')
75+
TestUtils.Simulate.click(button)
76+
expect(pauseUploadMethod).toHaveBeenCalledWith(0)
77+
statusChangeCallback(0, null, 'paused')
78+
button = TestUtils.scryRenderedDOMComponentsWithClass(PauseResumeButtonComponent, 'react-fine-uploader-pause-resume-button')[0]
79+
expect(button).toBeTruthy()
80+
expect(button.className.indexOf('react-fine-uploader-pause-button')).toBe(-1)
81+
expect(button.className.indexOf('react-fine-uploader-resume-button')).not.toBe(-1)
82+
})
83+
84+
it('allows a resumed file to be paused immediately', () => {
85+
const PauseResumeButtonComponent = TestUtils.renderIntoDocument(
86+
<PauseResumeButton id={ 0 } uploader={ uploader } />
87+
)
88+
89+
resumeCallback(0, { partIndex: 3 })
90+
91+
let button = TestUtils.scryRenderedDOMComponentsWithClass(PauseResumeButtonComponent, 'react-fine-uploader-pause-button')[0]
92+
expect(button).toBeTruthy()
93+
expect(button.className.indexOf('react-fine-uploader-pause-button')).not.toBe(-1)
94+
expect(button.className.indexOf('react-fine-uploader-resume-button')).toBe(-1)
6595
})
6696
})

0 commit comments

Comments
 (0)