-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a basic FTU #42
Add a basic FTU #42
Changes from all commits
a972900
d7f24eb
fa3d353
0e16276
1754f44
0033025
3e77587
39db7ce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import React, { PropTypes } from 'react'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm going to be a bit picky, could you add some documentation explaining what this component does? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It has now better documentation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, much better :) |
||
import { | ||
View, | ||
Animated, | ||
Dimensions, | ||
StyleSheet, | ||
} from 'react-native'; | ||
|
||
const { width } = Dimensions.get('window'); | ||
const DOT_SIZE = 8; | ||
const DOT_SPACING = 6; | ||
const DOT_FULL_WIDTH = DOT_SIZE + DOT_SPACING * 2; | ||
|
||
/** | ||
* This component is used to style the dots (or bullets) used by | ||
* react-native-viewpager. It's a purely stateless functional component that is | ||
* only concerned with rendering the dots and transitioning the active dot from | ||
* one page of the view pager to another. | ||
* | ||
* @param pageCount {number} The total number of pages. | ||
* @param activePage {number} The currently active page. | ||
* @param scrollValue {Animated.Value} Used to transition the active dot. | ||
* @param scrollOffset {number} Used to reset dot position after a transition. | ||
* @return {Component} | ||
*/ | ||
const ViewPagerIndicator = ({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand this a shorthand declaration, but I'd rather we just be consistent with how we define components (ie. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is more than just a shorthand. These stateless functional components are the recommended way to build components that are purely visual. They've been added in React 0.0.14 and they're likely to get an optimisation boost in the next versions. Ideally I'd like all the components in the You can read more in the announcement blog and this blog post. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Interesting thanks! |
||
pageCount, | ||
activePage, | ||
scrollValue, | ||
scrollOffset, | ||
}) => { | ||
// Initial left position of the active dot used during transition. | ||
const offsetX = (width - DOT_FULL_WIDTH * pageCount) / 2 + | ||
(activePage - scrollOffset) * DOT_FULL_WIDTH; | ||
|
||
// The CSS `left` property. | ||
const left = scrollValue.interpolate({ | ||
inputRange: [0, 1], | ||
outputRange: [offsetX, offsetX + DOT_FULL_WIDTH], | ||
}); | ||
|
||
// Create the elements for the inactive dots. | ||
const indicators = []; | ||
for (let i = 0; i < pageCount; i++) { | ||
indicators.push( | ||
<View key={i} style={styles.dot}/> | ||
); | ||
} | ||
|
||
return ( | ||
<View style={styles.container}> | ||
{indicators} | ||
<Animated.View style={[styles.dot, styles.activeDot, { left }]}/> | ||
</View> | ||
); | ||
}; | ||
|
||
ViewPagerIndicator.propTypes = { | ||
goToPage: PropTypes.func, | ||
pageCount: PropTypes.number, | ||
activePage: PropTypes.number, | ||
scrollValue: PropTypes.object, | ||
scrollOffset: PropTypes.number, | ||
}; | ||
|
||
const styles = StyleSheet.create({ | ||
container: { | ||
flexDirection: 'row', | ||
justifyContent: 'center', | ||
marginBottom: 20, | ||
}, | ||
dot: { | ||
width: DOT_SIZE, | ||
height: DOT_SIZE, | ||
borderRadius: DOT_SIZE / 2, | ||
backgroundColor: 'rgba(255,255,255,0.5)', | ||
marginHorizontal: DOT_SPACING, | ||
}, | ||
activeDot: { | ||
position: 'absolute', | ||
backgroundColor: '#fff', | ||
}, | ||
}); | ||
|
||
export default ViewPagerIndicator; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
import React, { Component, PropTypes } from 'react'; | ||
import { | ||
View, | ||
Image, | ||
Text, | ||
Button, | ||
Dimensions, | ||
StyleSheet, | ||
Platform, | ||
} from 'react-native'; | ||
import ViewPager from 'react-native-viewpager'; | ||
import ViewPagerIndicator from '../components/ViewPagerIndicator'; | ||
import { connect } from 'react-redux'; | ||
import { theme } from '../../config'; | ||
|
||
const CONTENT = [ | ||
{ | ||
title: 'Hear the story of London\'s vibrant street art', | ||
main: 'This month Project Magnet brings you a selection of amazing street art from around London.', | ||
footer: 'Not in London? Tell us where you\'d like to go next.', | ||
}, | ||
{ | ||
title: 'Turn on notifications?', | ||
main: 'Notifications can alert you when you\'re close to a point of interest, even when you\'re not inside the app.', | ||
}, | ||
]; | ||
|
||
export class FTU extends Component { | ||
constructor(props) { | ||
super(props); | ||
|
||
this.navigator = this.props.navigator; | ||
|
||
const PAGES = [ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we render the pages on demand? Or are we going to force to show always the two pages? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The pages are rendered on demand. The code used to build each page is only called when required (think of |
||
() => this.renderPage1(), | ||
() => this.renderPage2(), | ||
]; | ||
const dataSource = new ViewPager.DataSource({ | ||
pageHasChanged: (p1, p2) => p1 !== p2, | ||
}); | ||
|
||
this.state = { | ||
dataSource: dataSource.cloneWithPages(PAGES), | ||
}; | ||
} | ||
|
||
render() { | ||
return ( | ||
<Image | ||
source={require('../images/ftu/background.jpg')} | ||
resizeMode="cover" | ||
style={styles.container}> | ||
<View style={styles.scrim}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That's a word that came up when I was working on the settings drawer of Firefox OS. I think @djabberwocky was working on the spec back then so he may know more about this term. |
||
<ViewPager | ||
ref="viewPager" | ||
autoPlay={false} | ||
isLoop={false} | ||
locked={true} | ||
dataSource={this.state.dataSource} | ||
renderPage={(render) => render()} | ||
renderPageIndicator={() => <ViewPagerIndicator/>} | ||
style={styles.viewPager}/> | ||
</View> | ||
</Image> | ||
); | ||
} | ||
|
||
renderPage1() { | ||
const DATA = CONTENT[0]; | ||
|
||
return ( | ||
<View style={styles.screen}> | ||
<Text style={styles.title}>{DATA.title.toUpperCase()}</Text> | ||
<Text style={styles.text}>{DATA.main}</Text> | ||
<View style={styles.button}> | ||
<View style={styles.border}> | ||
<Button | ||
title="GET STARTED" | ||
accessibilityLabel="Continue to the next screen." | ||
onPress={() => this.refs.viewPager.goToPage(1)} | ||
color={Platform.OS === 'ios' ? 'white' : 'transparent'}/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm our current design we have a black button with white border as our primary-call-to-action button. Can we be consistent and have a single There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So after our UX discussions of yesterday, let's not focus too much on design. All will change. |
||
</View> | ||
</View> | ||
<Text style={styles.text}>{DATA.footer}</Text> | ||
</View> | ||
); | ||
} | ||
|
||
renderPage2() { | ||
const DATA = CONTENT[1]; | ||
|
||
return ( | ||
<View style={styles.screen}> | ||
<Text style={styles.title}>{DATA.title.toUpperCase()}</Text> | ||
<Text style={styles.text}>{DATA.main}</Text> | ||
<View style={styles.button}> | ||
<View style={styles.border}> | ||
<Button | ||
title="TURN ON" | ||
accessibilityLabel="Turn on notifications." | ||
onPress={() => this.navigator.push({ id: 'home' })} | ||
color={Platform.OS === 'ios' ? 'white' : 'transparent'}/> | ||
</View> | ||
<View style={styles.border}> | ||
<Button | ||
title="SKIP" | ||
accessibilityLabel="Skip this step." | ||
onPress={() => this.navigator.push({ id: 'home' })} | ||
color={Platform.OS === 'ios' ? 'white' : 'transparent'}/> | ||
</View> | ||
</View> | ||
</View> | ||
); | ||
} | ||
|
||
static propTypes = { | ||
navigator: PropTypes.object, | ||
} | ||
} | ||
|
||
const { width, height } = Dimensions.get('window'); | ||
|
||
const styles = StyleSheet.create({ | ||
container: { | ||
flex: 1, | ||
width, | ||
height, | ||
}, | ||
viewPager: { | ||
flex: 1, | ||
}, | ||
scrim: { | ||
width, | ||
height, | ||
backgroundColor: 'rgba(0,0,0,0.5)', | ||
}, | ||
screen: { | ||
width, | ||
padding: 30, | ||
flexDirection: 'column', | ||
justifyContent: 'space-around', | ||
}, | ||
title: { | ||
fontFamily: theme.fontBook, | ||
fontSize: 30, | ||
lineHeight: Math.round(30 * 1.5), | ||
color: 'white', | ||
}, | ||
text: { | ||
fontFamily: theme.fontBook, | ||
fontSize: 20, | ||
lineHeight: Math.round(20 * 1.5), | ||
color: 'white', | ||
}, | ||
button: { | ||
flexDirection: 'column', | ||
alignItems: 'center', | ||
}, | ||
border: { | ||
width: width / 2, | ||
borderRadius: 15, | ||
borderColor: 'white', | ||
borderWidth: 2, | ||
padding: 4, | ||
marginBottom: 10, | ||
}, | ||
}); | ||
|
||
export default connect()(FTU); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3501,6 +3501,13 @@ react-native-share@^1.0.18: | |
version "1.0.19" | ||
resolved "https://registry.yarnpkg.com/react-native-share/-/react-native-share-1.0.19.tgz#82c210efb156175d67ee271efee29ed9cf8e1449" | ||
|
||
react-native-viewpager@^0.2.13: | ||
version "0.2.13" | ||
resolved "https://registry.yarnpkg.com/react-native-viewpager/-/react-native-viewpager-0.2.13.tgz#75e421ae90e89efcd77d2c077c0525382b7c39c5" | ||
dependencies: | ||
react-timer-mixin "^0.13.3" | ||
warning "^2.1.0" | ||
|
||
[email protected]: | ||
version "0.40.0" | ||
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.40.0.tgz#ca7b86a8e8fbc7653634ad47ca2ffd69fdf18ad5" | ||
|
@@ -3606,7 +3613,7 @@ react-test-renderer@~15.4.0-rc.4: | |
fbjs "^0.8.4" | ||
object-assign "^4.1.0" | ||
|
||
react-timer-mixin@^0.13.2: | ||
react-timer-mixin@^0.13.2, react-timer-mixin@^0.13.3: | ||
version "0.13.3" | ||
resolved "https://registry.yarnpkg.com/react-timer-mixin/-/react-timer-mixin-0.13.3.tgz#0da8b9f807ec07dc3e854d082c737c65605b3d22" | ||
|
||
|
@@ -4422,6 +4429,12 @@ walker@~1.0.5: | |
dependencies: | ||
makeerror "1.0.x" | ||
|
||
warning@^2.1.0: | ||
version "2.1.0" | ||
resolved "https://registry.yarnpkg.com/warning/-/warning-2.1.0.tgz#21220d9c63afc77a8c92111e011af705ce0c6901" | ||
dependencies: | ||
loose-envify "^1.0.0" | ||
|
||
watch@~0.10.0: | ||
version "0.10.0" | ||
resolved "https://registry.yarnpkg.com/watch/-/watch-0.10.0.tgz#77798b2da0f9910d595f1ace5b0c2258521f21dc" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason for this reordering?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm really missing that we cannot do a dynamic import :( :( :( would love to load all this scenes like fte, debug, etc. on demand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new order reflects the order the routes are declared in the constructor below.
Also I know it's bad to import everything but I wouldn't worry too much as the components are not instantiated.
In the final product, I think it makes sense to be able to access the FTU (remember Firefox OS?)