Skip to content

Commit d50a319

Browse files
committed
AddSpinnerLoader extension
1 parent c985c5d commit d50a319

File tree

5 files changed

+245
-0
lines changed

5 files changed

+245
-0
lines changed

App/Components/SpinnerLoader.js

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import React from 'react';
2+
import {
3+
StyleSheet,
4+
PanResponder,
5+
PixelRatio,
6+
View,
7+
PropTypes,
8+
} from 'react-native';
9+
10+
import cssVar from '../Lib/cssVar';
11+
12+
import ActivityIndicator from '../Platform/ActivityIndicator';
13+
14+
const SpinnerLoader = React.createClass({
15+
propTypes: {
16+
spinning: React.PropTypes.bool,
17+
},
18+
19+
getInitialState() {
20+
return {
21+
width: 0,
22+
height: 0,
23+
};
24+
},
25+
26+
componentWillMount() {
27+
this._panGesture = PanResponder.create({
28+
onStartShouldSetPanResponder: (evt, gestureState) => { return false },
29+
onStartShouldSetPanResponderCapture: (evt, gestureState) => { return false },
30+
onMoveShouldSetPanResponder: (evt, gestureState) => { return false },
31+
onMoveShouldSetPanResponderCapture: (evt, gestureState) => { return false },
32+
});
33+
},
34+
35+
overlayStyle() {
36+
return {
37+
top: 0,
38+
left: 0,
39+
height: this.state.height,
40+
width: this.state.width,
41+
backgroundColor: 'transparent',
42+
alignItems: 'center',
43+
justifyContent: 'center',
44+
position: 'absolute',
45+
};
46+
},
47+
48+
onChildrenLayout(event) {
49+
this.setState({
50+
height: event.nativeEvent.layout.height,
51+
width: event.nativeEvent.layout.width,
52+
});
53+
},
54+
55+
renderSpinner() {
56+
if (!this.props.spinning || this.state.height === 0) {
57+
return null;
58+
}
59+
60+
return (
61+
<View style={this.overlayStyle()} {...this._panGesture.panHandlers}>
62+
<View style={[styles.spinnerContainer, this.props.spinnerContainerStyles]}>
63+
<ActivityIndicator style={styles.spinner} size="large" color={cssVar('blue50')} />
64+
</View>
65+
</View>
66+
);
67+
},
68+
69+
render() {
70+
return (
71+
<View style={{flex: 1}} onLayout={this.onChildrenLayout}>
72+
{this.props.children}
73+
{this.renderSpinner()}
74+
</View>
75+
);
76+
},
77+
78+
});
79+
80+
var styles = StyleSheet.create({
81+
spinnerContainer: {
82+
width: 60,
83+
height: 60,
84+
backgroundColor: 'transparent',
85+
alignItems: 'center',
86+
justifyContent: 'center',
87+
borderRadius: 60 / PixelRatio.get(),
88+
opacity: 0.70,
89+
},
90+
spinner: {
91+
width: 32,
92+
height: 32,
93+
},
94+
});
95+
96+
export default SpinnerLoader;

App/Extensions/AddSpinnerLoader.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from "react";
2+
3+
import * as Extension from '../Extensions/Extension';
4+
5+
import SpinnerLoader from '../Components/SpinnerLoader';
6+
7+
var AddSpinnerLoader = {
8+
extensionName: 'AddSpinnerLoader',
9+
10+
optionalParams: {
11+
spinnerContainerStyles: 'styles for the spinner loader',
12+
},
13+
14+
exports: {
15+
methods: ['start', 'stop']
16+
},
17+
18+
getInitialState() {
19+
return {
20+
spinning: false,
21+
}
22+
},
23+
24+
start() {
25+
this.setState({spinning: true})
26+
},
27+
28+
stop() {
29+
this.setState({spinning: false})
30+
},
31+
32+
renderExtension() {
33+
return (
34+
<SpinnerLoader
35+
spinning={this.state.spinning}
36+
spinnerContainerStyles={this.params.spinnerContainerStyles}
37+
>
38+
{this.renderComponent()}
39+
</SpinnerLoader>
40+
)
41+
}
42+
};
43+
44+
export default Extension.create(AddSpinnerLoader);

App/Extensions/Extension.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import React from "react";
2+
import invariant from 'invariant';
3+
import _ from 'underscore';
4+
5+
const create = ({extensionName, requiredParams = {}, exports = {}, optionalParams, ...BaseLib}) => {
6+
invariant(
7+
extensionName,
8+
'extensionName required when defining an extension'
9+
);
10+
11+
const Extension = (Component, params = {}) => {
12+
13+
const componentName = Component.displayName || Component.name;
14+
const containerName = `${extensionName} (${componentName})`;
15+
16+
_.pairs(requiredParams).forEach(([key, text]) => {
17+
invariant(
18+
params[key],
19+
`Extension params required ${containerName} ${key}: ${text}`
20+
);
21+
});
22+
23+
var ComponentContainer = React.createClass(Object.assign(params, {
24+
mixins: [BaseLib],
25+
26+
displayName: containerName,
27+
28+
params: params,
29+
30+
getInitialState() {
31+
return {};
32+
},
33+
34+
originalComponent() {
35+
if (!this.refs._ExtensionComponent) { return null; }
36+
37+
if (this.refs._ExtensionComponent.originalComponent) {
38+
return this.refs._ExtensionComponent.originalComponent();
39+
}
40+
return this.refs._ExtensionComponent;
41+
},
42+
43+
// Implement getExtensionProps if you want to add more behavior passed to the Component
44+
// it will allow accessing in the Extended Component with this.props[ExtensionName]
45+
getExtensionProps() {
46+
return {
47+
[extensionName]: Object.assign({variables: this.getExportedVariables()}, this.getExportedMethods())
48+
}
49+
},
50+
51+
getExportedVariables() {
52+
var _variables = {};
53+
(exports.variables || []).forEach((variableName) => _variables[variableName] = this.state[variableName]);
54+
55+
return _variables;
56+
},
57+
58+
getExportedMethods() {
59+
var _methods = {};
60+
(exports.methods || []).forEach((methodName) => _methods[methodName] = this[methodName]);
61+
62+
return _methods;
63+
},
64+
65+
renderComponent() {
66+
return (
67+
<Component
68+
{...this.props}
69+
{...this.getExtensionProps()}
70+
ref="_ExtensionComponent"
71+
/>
72+
);
73+
},
74+
75+
render() {
76+
if (this.renderExtension) {
77+
return this.renderExtension();
78+
}
79+
80+
return this.renderComponent();
81+
},
82+
83+
}));
84+
85+
return ComponentContainer;
86+
};
87+
88+
return Extension;
89+
};
90+
91+
export {create};

App/Platform/ActivityIndicator.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from 'react';
2+
import {
3+
ActivityIndicatorIOS,
4+
} from 'react-native';
5+
6+
const ActivityIndicator = ActivityIndicatorIOS;
7+
8+
export default ActivityIndicator;

App/Screens/CreatePost.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import AppActions from '../Actions/AppActions';
1111

1212
import KeyboardListener from '../Mixins/KeyboardListener';
1313

14+
import AddSpinnerLoader from '../Extensions/AddSpinnerLoader';
15+
1416
var CreatePost = React.createClass({
1517
mixins: [KeyboardListener],
1618

@@ -21,7 +23,10 @@ var CreatePost = React.createClass({
2123
},
2224

2325
onSubmitButton: function() {
26+
this.props['AddSpinnerLoader'].start();
27+
2428
PostActions.createPost(this.state.content, function(error) {
29+
this.props['AddSpinnerLoader'].stop();
2530
if (error) {
2631
// TODO: better error handling
2732
alert(error.message);
@@ -76,4 +81,5 @@ var styles = StyleSheet.create({
7681
}
7782
});
7883

84+
CreatePost = AddSpinnerLoader(CreatePost);
7985
export default CreatePost;

0 commit comments

Comments
 (0)