From 7e7dd5244e8d26485ad7950a89c04c98c4fef83f Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 11 Mar 2019 21:00:02 +0400 Subject: Initial commit/ --- frontend/app/components/avatar/index.js | 66 ++++++++ frontend/app/components/avatar/types.js | 49 ++++++ frontend/app/components/cardInput.js | 67 ++++++++ frontend/app/components/charts/areaChart.js | 120 +++++++++++++++ .../app/components/charts/areaSmoothedChart.js | 118 ++++++++++++++ frontend/app/components/charts/doughnutChart.js | 125 +++++++++++++++ frontend/app/components/charts/index.js | 4 + frontend/app/components/charts/progessChart.js | 96 ++++++++++++ frontend/app/components/ellipsis.js | 30 ++++ frontend/app/components/findFriends.js | 64 ++++++++ frontend/app/components/gallery.js | 78 ++++++++++ frontend/app/components/gradientButton/index.js | 45 ++++++ frontend/app/components/gradientButton/types.js | 48 ++++++ frontend/app/components/index.js | 14 ++ frontend/app/components/navBar.js | 145 +++++++++++++++++ frontend/app/components/paginationIndicator.js | 49 ++++++ frontend/app/components/passwordTextInput.js | 47 ++++++ frontend/app/components/picker/datePicker.js | 103 +++++++++++++ frontend/app/components/picker/index.js | 1 + frontend/app/components/progressBar.js | 63 ++++++++ frontend/app/components/socialBar/index.js | 61 ++++++++ frontend/app/components/socialBar/types.js | 43 ++++++ frontend/app/components/socialSetting.js | 66 ++++++++ frontend/app/components/switch/index.android.js | 171 +++++++++++++++++++++ frontend/app/components/switch/index.ios.js | 36 +++++ frontend/app/components/switch/types.js | 35 +++++ frontend/app/components/walkthrough.js | 63 ++++++++ 27 files changed, 1807 insertions(+) create mode 100644 frontend/app/components/avatar/index.js create mode 100644 frontend/app/components/avatar/types.js create mode 100644 frontend/app/components/cardInput.js create mode 100644 frontend/app/components/charts/areaChart.js create mode 100644 frontend/app/components/charts/areaSmoothedChart.js create mode 100644 frontend/app/components/charts/doughnutChart.js create mode 100644 frontend/app/components/charts/index.js create mode 100644 frontend/app/components/charts/progessChart.js create mode 100644 frontend/app/components/ellipsis.js create mode 100644 frontend/app/components/findFriends.js create mode 100644 frontend/app/components/gallery.js create mode 100644 frontend/app/components/gradientButton/index.js create mode 100644 frontend/app/components/gradientButton/types.js create mode 100644 frontend/app/components/index.js create mode 100644 frontend/app/components/navBar.js create mode 100644 frontend/app/components/paginationIndicator.js create mode 100644 frontend/app/components/passwordTextInput.js create mode 100644 frontend/app/components/picker/datePicker.js create mode 100644 frontend/app/components/picker/index.js create mode 100644 frontend/app/components/progressBar.js create mode 100644 frontend/app/components/socialBar/index.js create mode 100644 frontend/app/components/socialBar/types.js create mode 100644 frontend/app/components/socialSetting.js create mode 100644 frontend/app/components/switch/index.android.js create mode 100644 frontend/app/components/switch/index.ios.js create mode 100644 frontend/app/components/switch/types.js create mode 100644 frontend/app/components/walkthrough.js (limited to 'frontend/app/components') diff --git a/frontend/app/components/avatar/index.js b/frontend/app/components/avatar/index.js new file mode 100644 index 0000000..b01800a --- /dev/null +++ b/frontend/app/components/avatar/index.js @@ -0,0 +1,66 @@ +import React from 'react'; +import { + Image, + View, +} from 'react-native'; +import { + RkComponent, + RkText, + RkTheme, +} from 'react-native-ui-kitten'; +import { FontAwesome } from '../../assets/icons'; + +export class Avatar extends RkComponent { + componentName = 'Avatar'; + typeMapping = { + container: {}, + image: {}, + badge: {}, + badgeText: {}, + }; + + getBadgeStyle = (badgeProps) => { + switch (badgeProps) { + case 'like': + return { + symbol: FontAwesome.heart, + backgroundColor: RkTheme.current.colors.badge.likeBackground, + color: RkTheme.current.colors.badge.likeForeground, + }; + case 'follow': + return { + symbol: FontAwesome.plus, + backgroundColor: RkTheme.current.colors.badge.plusBackground, + color: RkTheme.current.colors.badge.plusForeground, + }; + default: return {}; + } + }; + + renderImg = (styles) => ( + + + { this.props.badge && this.renderBadge(styles.badge)} + + ); + + renderBadge = (style, textStyle) => { + const badgeStyle = this.getBadgeStyle(this.props.badge); + return ( + + + {badgeStyle.symbol} + + + ); + }; + + render() { + const { container, ...other } = this.defineStyles(); + return ( + + {this.renderImg(other)} + + ); + } +} diff --git a/frontend/app/components/avatar/types.js b/frontend/app/components/avatar/types.js new file mode 100644 index 0000000..2d732d8 --- /dev/null +++ b/frontend/app/components/avatar/types.js @@ -0,0 +1,49 @@ +export const AvatarTypes = () => ({ + _base: { + container: { + alignItems: 'center', + flexDirection: 'row', + }, + image: { + width: 40, + height: 40, + }, + badge: { + width: 15, + height: 15, + borderRadius: 7.5, + alignItems: 'center', + justifyContent: 'center', + position: 'absolute', + bottom: -2, + right: -2, + }, + badgeText: { + backgroundColor: 'transparent', + fontSize: 9, + }, + }, + big: { + image: { + width: 110, + height: 110, + borderRadius: 55, + marginBottom: 19, + }, + container: { + flexDirection: 'column', + }, + }, + small: { + image: { + width: 32, + height: 32, + borderRadius: 16, + }, + }, + circle: { + image: { + borderRadius: 20, + }, + }, +}); diff --git a/frontend/app/components/cardInput.js b/frontend/app/components/cardInput.js new file mode 100644 index 0000000..b286d56 --- /dev/null +++ b/frontend/app/components/cardInput.js @@ -0,0 +1,67 @@ +import React from 'react'; +import { + RkButton, + RkTextInput, + RkText, + RkStyleSheet, +} from 'react-native-ui-kitten'; +import { FontAwesome } from '../assets/icons'; + +export class CardInput extends React.Component { + state = { + hidden: true, + cardNumber: '', + }; + + formatCreditNumber = (number, isHidden) => ( + isHidden + ? number.replace(/\D/g, '') + : number.replace(/[^\dA-Z]/g, '').replace(/(.{4})/g, '$1 ').trim() + ); + + onInputLabelPressed = () => { + this.setState({ + hidden: !this.state.hidden, + cardNumber: this.formatCreditNumber(this.state.cardNumber, !this.state.hidden), + }); + }; + + onInputChanged = (text) => { + this.setState({ + cardNumber: this.formatCreditNumber(text, this.state.hidden), + }); + }; + + renderInputLabel = () => ( + + {FontAwesome.slashEye} + + ); + + render = () => ( + + ); +} + +let styles = RkStyleSheet.create({ + icon: { + fontSize: 24, + }, + button: { + right: 17, + }, +}); diff --git a/frontend/app/components/charts/areaChart.js b/frontend/app/components/charts/areaChart.js new file mode 100644 index 0000000..be40960 --- /dev/null +++ b/frontend/app/components/charts/areaChart.js @@ -0,0 +1,120 @@ +import React from 'react'; +import { + View, + Dimensions, +} from 'react-native'; +import { + RkComponent, + RkTheme, + RkText, +} from 'react-native-ui-kitten'; + +import { + VictoryChart, + VictoryAxis, + VictoryArea, + VictoryScatter, + VictoryGroup, +} from 'victory-native'; + + +export class AreaChart extends RkComponent { + state = { + data: [ + { x: 1, y: 1 }, + { x: 2, y: 2 }, + { x: 3, y: 1 }, + { x: 4, y: 2 }, + { x: 5, y: 3 }, + { x: 6, y: 3 }, + { x: 7, y: 4 }, + { x: 8, y: 3 }, + { x: 9, y: 2 }, + { x: 10, y: 4 }, + ], + }; + + componentWillMount() { + this.size = Dimensions.get('window').width; + } + + componentDidMount() { + this.setStateInterval = setInterval(() => { + let positive = Math.random() > 0.5; + let newValue = this.state.data[this.state.data.length - 1].y; + if (newValue > 3) { + positive = false; + } else if (newValue < 2) { + positive = true; + } + newValue = positive ? newValue + 1 : newValue - 1; + const newData = this.state.data.map((d, i) => ({ + x: d.x, + y: i === this.state.data.length - 1 ? newValue : this.state.data[i + 1].y, + })); + this.setState({ + data: newData, + }); + }, 3000); + } + + componentWillUnmount() { + clearInterval(this.setStateInterval); + } + + render = () => ( + + REAL TIME VISITORS + + + + + + + + + + ); +} diff --git a/frontend/app/components/charts/areaSmoothedChart.js b/frontend/app/components/charts/areaSmoothedChart.js new file mode 100644 index 0000000..54ca435 --- /dev/null +++ b/frontend/app/components/charts/areaSmoothedChart.js @@ -0,0 +1,118 @@ +import React from 'react'; +import { + View, + Dimensions, +} from 'react-native'; +import { + RkComponent, + RkText, + RkTheme, +} from 'react-native-ui-kitten'; +import { + VictoryChart, + VictoryAxis, + VictoryArea, +} from 'victory-native'; + +export class AreaSmoothedChart extends RkComponent { + state = { + data: [ + [ + { x: 1, y: 1.0, key: 0 }, + { x: 2, y: 1.5, key: 1 }, + { x: 3, y: 1.0, key: 2 }, + { x: 4, y: 0.5, key: 3 }, + { x: 5, y: 1.0, key: 4 }, + { x: 6, y: 2.0, key: 5 }, + { x: 7, y: 2.5, key: 6 }, + ], + [ + { x: 1, y: 1.5, key: 0 }, + { x: 2, y: 2.0, key: 1 }, + { x: 3, y: 1.5, key: 2 }, + { x: 4, y: 0.8, key: 3 }, + { x: 5, y: 1.5, key: 4 }, + { x: 6, y: 2.6, key: 5 }, + { x: 7, y: 3.3, key: 6 }, + ], + [ + { x: 1, y: 2.0, key: 0 }, + { x: 2, y: 2.5, key: 1 }, + { x: 3, y: 2.0, key: 2 }, + { x: 4, y: 1.1, key: 3 }, + { x: 5, y: 2.0, key: 4 }, + { x: 6, y: 3.2, key: 5 }, + { x: 7, y: 4.0, key: 6 }, + ], + [ + { x: 1, y: 2.5, key: 0 }, + { x: 2, y: 3.0, key: 1 }, + { x: 3, y: 2.5, key: 2 }, + { x: 4, y: 1.4, key: 3 }, + { x: 5, y: 2.5, key: 4 }, + { x: 6, y: 3.7, key: 5 }, + { x: 7, y: 4.7, key: 6 }, + ], + ], + }; + colors = RkTheme.current.colors.charts.followersArea; + + componentWillMount() { + this.size = Dimensions.get('window').width; + } + + renderChartAreas = () => this.state.data.reverse().map((area, index) => ( + + )); + + render = () => ( + + NEW FOLLOWERS + + + + {this.renderChartAreas()} + + + ) +} diff --git a/frontend/app/components/charts/doughnutChart.js b/frontend/app/components/charts/doughnutChart.js new file mode 100644 index 0000000..5b67ba7 --- /dev/null +++ b/frontend/app/components/charts/doughnutChart.js @@ -0,0 +1,125 @@ +import React from 'react'; +import { View } from 'react-native'; +import { + RkComponent, + RkText, + RkTheme, + RkStyleSheet, +} from 'react-native-ui-kitten'; +import { VictoryPie } from 'victory-native'; +import { Svg, Text as SvgText } from 'react-native-svg'; +import { scale } from '../../utils/scale'; + +export class DoughnutChart extends RkComponent { + state = { + selected: 0, + data: [ + { + x: 1, + y: 240, + title: '24%', + name: 'Likes', + color: RkTheme.current.colors.charts.doughnut[0], + }, + { + x: 2, + y: 270, + title: '27%', + name: 'Comments', + color: RkTheme.current.colors.charts.doughnut[1], + }, + { + x: 3, + y: 170, + title: '17%', + name: 'Shares', + color: RkTheme.current.colors.charts.doughnut[2], + }, + { + x: 4, + y: 320, + title: '32%', + name: 'People', + color: RkTheme.current.colors.charts.doughnut[3], + }, + ], + }; + size = 300; + fontSize = 40; + + computeColors = () => this.state.data.map(i => i.color); + + onPeopleChartPressed = (event, props) => { + this.setState({ + selected: props.index, + }); + }; + + renderMarkdown = () => this.state.data.map(this.renderMarkdownItem); + + renderMarkdownItem = (item) => ( + + + {item.name} + + ); + + render = () => ( + + AUDIENCE OVERVIEW + + + + + {this.state.data[this.state.selected].title} + + + + + {this.renderMarkdown()} + + + ); +} + +const styles = RkStyleSheet.create(() => ({ + legendContainer: { + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'space-around', + }, + legendItem: { + flexDirection: 'row', + alignItems: 'center', + }, + itemBadge: { + width: 10, + height: 10, + borderRadius: 5, + marginRight: 5, + }, +})); diff --git a/frontend/app/components/charts/index.js b/frontend/app/components/charts/index.js new file mode 100644 index 0000000..c4438e8 --- /dev/null +++ b/frontend/app/components/charts/index.js @@ -0,0 +1,4 @@ +export * from './progessChart'; +export * from './doughnutChart'; +export * from './areaChart'; +export * from './areaSmoothedChart'; diff --git a/frontend/app/components/charts/progessChart.js b/frontend/app/components/charts/progessChart.js new file mode 100644 index 0000000..bd70dcf --- /dev/null +++ b/frontend/app/components/charts/progessChart.js @@ -0,0 +1,96 @@ +import React from 'react'; +import { View } from 'react-native'; +import { + RkComponent, + RkText, + RkTheme, + RkStyleSheet, +} from 'react-native-ui-kitten'; +import { VictoryPie } from 'victory-native'; +import { Svg, Text as SvgText } from 'react-native-svg'; +import { scale } from '../../utils/scale'; + +export class ProgressChart extends RkComponent { + state = { + percents: 72, + }; + size = 120; + fontSize = 25; + + componentDidMount() { + this.setStateInterval = setInterval(this.updatePercent, 1500); + } + + componentWillUnmount() { + clearInterval(this.setStateInterval); + } + + updatePercent = () => { + let positive = Math.random() > 0.5; + if (this.state.percents > 95) { + positive = false; + } else if (this.state.percents < 60) { + positive = true; + } + this.setState({ + percents: positive ? this.state.percents + 1 : this.state.percents - 1, + }); + }; + + getChartData = () => [ + { x: 1, y: this.state.percents }, + { x: 2, y: 100 - this.state.percents }, + ]; + + onChartFill = (data) => { + const themeColor = RkTheme.current.colors.charts.followersProgress; + return data.x === 1 ? themeColor : 'transparent'; + }; + + render = () => ( + + FOLLOWERS + + + + + {`${this.state.percents}%`} + + + + REACH + 1 500 356 + +6 per day in average + + + + ); +} + +const styles = RkStyleSheet.create(() => ({ + chartContainer: { + flexDirection: 'row', + justifyContent: 'space-around', + alignItems: 'center', + marginTop: 10, + }, +})); diff --git a/frontend/app/components/ellipsis.js b/frontend/app/components/ellipsis.js new file mode 100644 index 0000000..1cdcae1 --- /dev/null +++ b/frontend/app/components/ellipsis.js @@ -0,0 +1,30 @@ +import React from 'react'; +import { View } from 'react-native'; +import { RkStyleSheet } from 'react-native-ui-kitten'; + +export class Ellipsis extends React.Component { + render() { + return ( + + + + + + ); + } +} + +let styles = RkStyleSheet.create(theme => ({ + container: { + flexDirection: 'row', + marginHorizontal: 5, + marginVertical: 10, + }, + dot: { + height: 5.5, + width: 5.5, + borderRadius: 3, + backgroundColor: theme.colors.text.base, + marginHorizontal: 2.5, + }, +})); diff --git a/frontend/app/components/findFriends.js b/frontend/app/components/findFriends.js new file mode 100644 index 0000000..0e54034 --- /dev/null +++ b/frontend/app/components/findFriends.js @@ -0,0 +1,64 @@ +import React from 'react'; +import { + StyleSheet, + TouchableOpacity, + View, + ViewPropTypes, +} from 'react-native'; +import { + RkText, + RkTheme, +} from 'react-native-ui-kitten'; +import PropTypes from 'prop-types'; +import { FontAwesome } from '../assets/icons'; + +export class FindFriends extends React.Component { + static propTypes = { + selected: PropTypes.bool, + color: PropTypes.string, + icon: PropTypes.node.isRequired, + text: PropTypes.string.isRequired, + onPress: PropTypes.func, + style: ViewPropTypes.style, + }; + static defaultProps = { + selected: false, + color: RkTheme.current.colors.text.base, + onPress: (() => null), + style: {}, + }; + + render = () => { + const color = this.props.selected ? this.props.color : RkTheme.current.colors.disabled; + return ( + + + + {this.props.icon} + {`Find Friends With ${this.props.text}`} + + {FontAwesome.chevronRight} + + + ); + }; +} + +let styles = StyleSheet.create({ + wrapper: { + flex: 1, + }, + container: { + flex: 1, + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingVertical: 18, + }, + text: { + flexDirection: 'row', + }, + icon: { + width: 35, + }, +}); diff --git a/frontend/app/components/gallery.js b/frontend/app/components/gallery.js new file mode 100644 index 0000000..bcc57ea --- /dev/null +++ b/frontend/app/components/gallery.js @@ -0,0 +1,78 @@ +import React from 'react'; +import { + View, + FlatList, + Dimensions, + StyleSheet, +} from 'react-native'; +import { + RkText, + RkButton, + RkModalImg, +} from 'react-native-ui-kitten'; +import PropTypes from 'prop-types'; +import { Ellipsis } from './ellipsis'; +import { SocialBar } from './socialBar'; + +export class Gallery extends React.Component { + static propTypes = { + items: PropTypes.arrayOf(PropTypes.node).isRequired, + }; + + constructor(props) { + super(props); + const itemSize = (Dimensions.get('window').width - 12) / 3; + this.state = { + data: this.props.items, + itemSize, + }; + } + + extractItemKey = (index) => `${index}`; + + renderHeader = (options) => ( + + Close + {`${options.pageNumber}/${options.totalPages}`} + + + + + ); + + renderFooter = () => ( + + ); + + renderItem = ({ index }) => ( + + ); + + render = () => ( + + + + ); +} + +const styles = StyleSheet.create({ + images: { + flexDirection: 'row', + paddingHorizontal: 0.5, + }, + header: { + flexDirection: 'row', + justifyContent: 'space-between', + }, +}); diff --git a/frontend/app/components/gradientButton/index.js b/frontend/app/components/gradientButton/index.js new file mode 100644 index 0000000..92ebd79 --- /dev/null +++ b/frontend/app/components/gradientButton/index.js @@ -0,0 +1,45 @@ +import React from 'react'; +import { LinearGradient } from 'expo'; +import { + RkButton, + RkText, + RkComponent, +} from 'react-native-ui-kitten'; + +export class GradientButton extends RkComponent { + componentName = 'GradientButton'; + typeMapping = { + button: {}, + gradient: {}, + text: {}, + }; + + renderContent = (textStyle) => { + const hasText = this.props.text === undefined; + return hasText ? this.props.children : this.renderText(textStyle); + }; + + renderText = (textStyle) => ( + {this.props.text} + ); + + render() { + const { button, gradient, text: textStyle } = this.defineStyles(); + const { style, rkType, ...restProps } = this.props; + const colors = this.props.colors || this.extractNonStyleValue(gradient, 'colors'); + return ( + + + {this.renderContent(textStyle)} + + + ); + } +} diff --git a/frontend/app/components/gradientButton/types.js b/frontend/app/components/gradientButton/types.js new file mode 100644 index 0000000..62bcf22 --- /dev/null +++ b/frontend/app/components/gradientButton/types.js @@ -0,0 +1,48 @@ +import { scaleVertical } from '../../utils/scale'; + +export const GradientButtonTypes = (theme) => ({ + _base: { + button: { + alignItems: 'stretch', + paddingVertical: 0, + paddingHorizontal: 0, + height: scaleVertical(40), + borderRadius: 20, + }, + gradient: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + borderRadius: 20, + colors: theme.colors.gradients.base, + }, + text: { + backgroundColor: 'transparent', + color: theme.colors.text.inverse, + }, + }, + large: { + button: { + alignSelf: 'stretch', + height: scaleVertical(56), + borderRadius: 28, + }, + gradient: { + borderRadius: 28, + }, + }, + statItem: { + button: { + flex: 1, + borderRadius: 5, + marginHorizontal: 10, + height: null, + alignSelf: 'auto', + }, + gradient: { + flex: 1, + borderRadius: 5, + padding: 10, + }, + }, +}); diff --git a/frontend/app/components/index.js b/frontend/app/components/index.js new file mode 100644 index 0000000..786a99b --- /dev/null +++ b/frontend/app/components/index.js @@ -0,0 +1,14 @@ +export * from './avatar'; +export * from './gradientButton'; +export * from './charts'; +export * from './socialBar'; +export * from './switch/index'; +export * from './ellipsis'; +export * from './gallery'; +export * from './socialSetting'; +export * from './findFriends'; +export * from './progressBar'; +export * from './navBar'; +export * from './paginationIndicator'; +export * from './passwordTextInput'; +export * from './cardInput'; diff --git a/frontend/app/components/navBar.js b/frontend/app/components/navBar.js new file mode 100644 index 0000000..2947209 --- /dev/null +++ b/frontend/app/components/navBar.js @@ -0,0 +1,145 @@ +import React from 'react'; +import { + StyleSheet, + View, +} from 'react-native'; +import { DrawerActions } from 'react-navigation'; +import PropTypes from 'prop-types'; +import _ from 'lodash'; +import { RkText, RkButton, RkStyleSheet } from 'react-native-ui-kitten'; +import { FontAwesome } from '../assets/icons'; +import { UIConstants } from '../config/appConstants'; +import NavigationType from '../config/navigation/propTypes'; + +export class NavBar extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + headerProps: PropTypes.shape().isRequired, + }; + + onNavigationLeftMenuButtonPressed = () => { + this.props.navigation.dispatch(DrawerActions.openDrawer()); + }; + + onNavigationLeftBackButtonPressed = () => { + this.props.navigation.goBack(); + }; + + renderTitleItem = (title, options) => { + const isCustom = options !== undefined; + return isCustom ? this.renderCustomTitleItem(options) : this.renderNavigationTitleItem(title); + }; + + renderLeftItem = (options) => { + const isCustom = options !== undefined; + return isCustom ? this.renderCustomLeftItem(options) : this.renderNavigationLeftItem(); + }; + + renderRightItem = (options) => { + const isCustom = options !== undefined; + return isCustom ? this.renderCustomRightItem(options) : this.renderNavigationRightItem(); + }; + + renderNavigationTitleItem = (title) => ( + + {title} + + ); + + renderNavigationLeftBackItem = () => ( + + {FontAwesome.chevronLeft} + + ); + + renderNavigationLeftMenuItem = () => ( + + {FontAwesome.bars} + + ); + + renderNavigationLeftItemContent = (sceneIndex) => { + const isFirstScene = sceneIndex === 0; + return isFirstScene ? this.renderNavigationLeftMenuItem() : this.renderNavigationLeftBackItem(); + }; + + renderNavigationLeftItem = () => { + const sceneIndex = _.findIndex(this.props.headerProps.scenes, { isActive: true }); + return ( + + {this.renderNavigationLeftItemContent(sceneIndex)} + + ); + }; + + renderNavigationRightItem = () => undefined; + + renderCustomTitleItem = (options) => ( + + {options} + + ); + + renderCustomLeftItem = (options) => ( + {options} + ); + + renderCustomRightItem = (options) => ( + {options} + ); + + render() { + const { options } = this.props.headerProps.scene.descriptor; + return ( + + + {this.renderTitleItem(options.title, options.headerTitle)} + {this.renderLeftItem(options.headerLeft)} + {this.renderRightItem(options.headerRight)} + + + ); + } +} + +const styles = RkStyleSheet.create(theme => ({ + layout: { + backgroundColor: theme.colors.screen.base, + paddingTop: UIConstants.StatusbarHeight, + borderBottomWidth: StyleSheet.hairlineWidth, + borderBottomColor: theme.colors.border.base, + }, + container: { + flexDirection: 'row', + height: UIConstants.AppbarHeight, + + }, + left: { + position: 'absolute', + top: 0, + bottom: 0, + justifyContent: 'center', + }, + right: { + position: 'absolute', + right: 0, + top: 0, + bottom: 0, + justifyContent: 'center', + }, + title: { + ...StyleSheet.absoluteFillObject, + justifyContent: 'center', + alignItems: 'center', + }, + menu: { + width: 40, + }, +})); diff --git a/frontend/app/components/paginationIndicator.js b/frontend/app/components/paginationIndicator.js new file mode 100644 index 0000000..6a043d0 --- /dev/null +++ b/frontend/app/components/paginationIndicator.js @@ -0,0 +1,49 @@ +import React from 'react'; +import { View } from 'react-native'; +import { RkStyleSheet } from 'react-native-ui-kitten'; +import PropTypes from 'prop-types'; + +export class PaginationIndicator extends React.Component { + static propTypes = { + current: PropTypes.number, + length: PropTypes.number.isRequired, + }; + static defaultProps = { + current: 0, + }; + + renderIndicatorItem = (index, selected) => ( + + ); + + renderIndicators = () => { + const indicators = []; + for (let i = 0; i < this.props.length; i += 1) { + indicators.push(this.renderIndicatorItem(i, i === this.props.current)); + } + return indicators; + }; + + render = () => ( + + {this.renderIndicators()} + + ); +} + +const styles = RkStyleSheet.create(theme => ({ + container: { + flexDirection: 'row', + }, + base: { + width: 8, + height: 8, + borderRadius: 5, + borderColor: theme.colors.brand, + borderWidth: 1, + marginHorizontal: 5, + }, + selected: { + backgroundColor: theme.colors.brand, + }, +})); diff --git a/frontend/app/components/passwordTextInput.js b/frontend/app/components/passwordTextInput.js new file mode 100644 index 0000000..5590caf --- /dev/null +++ b/frontend/app/components/passwordTextInput.js @@ -0,0 +1,47 @@ +import React from 'react'; +import { + RkButton, + RkTextInput, + RkText, + RkStyleSheet, +} from 'react-native-ui-kitten'; +import { FontAwesome } from '../assets/icons'; + +export class PasswordTextInput extends React.Component { + state = { + hidden: true, + }; + + onInputLabelPressed = () => { + this.setState({ hidden: !this.state.hidden }); + }; + + renderInputLabel = () => ( + + {FontAwesome.slashEye} + + ); + + render = () => ( + + ); +} + +const styles = RkStyleSheet.create({ + icon: { + fontSize: 24, + }, + button: { + right: 17, + }, +}); diff --git a/frontend/app/components/picker/datePicker.js b/frontend/app/components/picker/datePicker.js new file mode 100644 index 0000000..4225613 --- /dev/null +++ b/frontend/app/components/picker/datePicker.js @@ -0,0 +1,103 @@ +import React from 'react'; +import { RkPicker } from 'react-native-ui-kitten'; + +export const DatePart = Object.freeze({ YEAR: 1, MONTH: 2, DAY: 3 }); + +export class DatePicker extends React.Component { + componentName = 'DatePicker'; + + state = { + data: { + days: DatePicker.generateArrayFromRange(1, 31), + years: DatePicker.generateArrayFromRange(2000, 2030), + months: [ + { key: 1, value: 'Jun' }, { key: 2, value: 'Feb' }, + { key: 3, value: 'Mar' }, { key: 4, value: 'Apr' }, + { key: 5, value: 'May' }, { key: 6, value: 'Jun' }, + { key: 7, value: 'Jul' }, { key: 8, value: 'Aug' }, + { key: 9, value: 'Sep' }, { key: 10, value: 'Oct' }, + { key: 11, value: 'Nov' }, { key: 12, value: 'Dec' }, + ], + }, + }; + + onDatePickerConfirm = (date) => { + let resultDate = {}; + if (this.props.customDateParts) { + let i = 0; + if (this.props.customDateParts.includes(DatePart.MONTH)) { + resultDate.month = date[i += 1]; + } + if (this.props.customDateParts.includes(DatePart.DAY)) { + resultDate.day = date[i += 1]; + } + if (this.props.customDateParts.includes(DatePart.YEAR)) { + resultDate.year = date[i]; + } + } else { + resultDate = { month: date[0], day: date[1], year: date[2] }; + } + this.props.onConfirm(resultDate); + }; + + static generateArrayFromRange(start, finish) { + return Array(...Array((finish - start) + 1)).map((_, i) => start + i); + } + + findElementByKey(key, array) { + let element = array[0]; + array.forEach((value) => { + if (value.key === key) element = value; + }); + return element; + } + + render() { + const { + onConfirm, + selectedYear, + selectedMonth, + selectedDay, + customDateParts, + ...props + } = this.props; + + let data = [this.state.data.months, this.state.data.days, this.state.data.years]; + let selectedOptions = [ + this.findElementByKey(selectedMonth, this.state.data.months), + selectedDay || 1, + selectedYear || 2000, + ]; + if (customDateParts) { + selectedOptions = []; + data = []; + if (customDateParts.includes(DatePart.MONTH)) { + data.push(this.state.data.months); + selectedOptions.push(this.findElementByKey(selectedMonth, this.state.data.months)); + } + if (customDateParts.includes(DatePart.DAY)) { + data.push(this.state.data.days); + selectedOptions.push(selectedDay || 1); + } + if (customDateParts.includes(DatePart.YEAR)) { + data.push(this.state.data.years); + selectedOptions.push(selectedYear || 2000); + } + } + return ( + + ); + } +} diff --git a/frontend/app/components/picker/index.js b/frontend/app/components/picker/index.js new file mode 100644 index 0000000..6d1605a --- /dev/null +++ b/frontend/app/components/picker/index.js @@ -0,0 +1 @@ +export * from './datePicker'; diff --git a/frontend/app/components/progressBar.js b/frontend/app/components/progressBar.js new file mode 100644 index 0000000..dd3a3a1 --- /dev/null +++ b/frontend/app/components/progressBar.js @@ -0,0 +1,63 @@ +import React from 'react'; +import { + StyleSheet, + View, + Animated, + Easing, + ViewPropTypes, +} from 'react-native'; +import PropTypes from 'prop-types'; +import { RkTheme } from 'react-native-ui-kitten'; + +export class ProgressBar extends React.Component { + static propTypes = { + width: PropTypes.number.isRequired, + progress: PropTypes.number, + color: PropTypes.string, + style: ViewPropTypes.style, + }; + static defaultProps = { + progress: 0, + color: RkTheme.current.colors.accent, + style: {}, + }; + + state = { + progress: new Animated.Value(0), + }; + + componentDidUpdate(prevProps) { + if (this.props.progress >= 0 && this.props.progress !== prevProps.progress) { + this.animate(this.props.progress); + } + } + + animate = (endValue) => { + Animated.timing(this.state.progress, { + easing: Easing.inOut(Easing.ease), + duration: 500, + toValue: endValue, + }).start(); + }; + + render() { + const width = this.state.progress.interpolate({ + inputRange: [0, 1], + outputRange: [0, this.props.width], + }); + return ( + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + height: 3, + }, + value: { + height: 3, + }, +}); diff --git a/frontend/app/components/socialBar/index.js b/frontend/app/components/socialBar/index.js new file mode 100644 index 0000000..5cf3fc2 --- /dev/null +++ b/frontend/app/components/socialBar/index.js @@ -0,0 +1,61 @@ +import React from 'react'; +import { View } from 'react-native'; +import { + RkText, + RkButton, + RkComponent, +} from 'react-native-ui-kitten'; +import { FontAwesome } from '../../assets/icons'; + +export class SocialBar extends RkComponent { + componentName = 'SocialBar'; + typeMapping = { + container: {}, + section: {}, + icon: {}, + label: {}, + }; + static data = { + comments: '26', + is_solved: "Doesn't solved", + }; + + constructor(props) { + super(props); + this.state = { + comments: this.props.comments, + is_solved: this.props.is_solved || SocialBar.data.is_solved, + }; + } + + + onCommentButtonPressed = () => { + }; + + + render() { + const { + container, section, icon, label, + } = this.defineStyles(); + + const comments = this.state.comments + (this.props.showLabel ? ' Comments' : ''); + const is_solved = this.state.is_solved + (this.props.showLabel ? '' : ''); + + return ( + + + + {FontAwesome.comment} + {comments} + + + + + {FontAwesome.slashEye} + {is_solved} + + + + ); + } +} diff --git a/frontend/app/components/socialBar/types.js b/frontend/app/components/socialBar/types.js new file mode 100644 index 0000000..3aa638d --- /dev/null +++ b/frontend/app/components/socialBar/types.js @@ -0,0 +1,43 @@ +export const SocialBarTypes = (theme) => ({ + _base: { + container: { + justifyContent: 'center', + alignItems: 'center', + flexDirection: 'row', + flex: 1, + }, + section: { + justifyContent: 'center', + flexDirection: 'row', + flex: 1, + }, + icon: { + fontSize: 20, + }, + label: { + marginLeft: 8, + alignSelf: 'flex-end', + }, + }, + leftAligned: { + section: { + alignItems: 'flex-start', + justifyContent: 'flex-start', + }, + label: { + color: theme.colors.text.inverse, + }, + icon: { + color: theme.colors.text.inverse, + }, + }, + space: { + container: { + justifyContent: 'space-between', + paddingHorizontal: 10, + }, + section: { + flex: -1, + }, + }, +}); diff --git a/frontend/app/components/socialSetting.js b/frontend/app/components/socialSetting.js new file mode 100644 index 0000000..1c2f2a3 --- /dev/null +++ b/frontend/app/components/socialSetting.js @@ -0,0 +1,66 @@ +import React from 'react'; +import { + View, + StyleSheet, +} from 'react-native'; +import { + RkText, + RkTheme, +} from 'react-native-ui-kitten'; +import PropTypes from 'prop-types'; +import { RkSwitch } from './switch/index'; + +export class SocialSetting extends React.Component { + static propTypes = { + name: PropTypes.string.isRequired, + icon: PropTypes.node.isRequired, + selected: PropTypes.bool, + tintColor: PropTypes.string, + }; + static defaultProps = { + selected: true, + tintColor: RkTheme.current.colors.accent, + }; + + constructor(props) { + super(props); + this.state = { + selected: this.props.selected, + }; + } + + onSwitchValueChanged = (value) => { + this.setState({ selected: value }); + }; + + render() { + const color = this.state.selected ? this.props.tintColor : RkTheme.current.colors.disabled; + return ( + + + {this.props.icon} + {this.props.name} + + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + flexDirection: 'row', + flex: 1, + justifyContent: 'space-between', + alignItems: 'center', + paddingVertical: 14, + }, + left: { + flexDirection: 'row', + alignItems: 'center', + }, + icon: { + width: 35, + alignItems: 'center', + }, +}); diff --git a/frontend/app/components/switch/index.android.js b/frontend/app/components/switch/index.android.js new file mode 100644 index 0000000..2750545 --- /dev/null +++ b/frontend/app/components/switch/index.android.js @@ -0,0 +1,171 @@ +/* + * + * This is modified version of https://github.com/poberwong/react-native-switch-pro + * Copyright (c) 2016 PoberWong + * + */ +import React from 'react'; +import { + Animated, + Easing, + PanResponder, +} from 'react-native'; +import { RkComponent } from 'react-native-ui-kitten'; + +const width = 52; +const height = 32; +const animationDuration = 200; +const offLeftValue = -2; +const onLeftValue = 20; + +export class RkSwitch extends RkComponent { + componentName = 'RkSwitch'; + typeMapping = { + container: { + onColor: 'onColor', + offColor: 'offColor', + }, + thumb: {}, + }; + selectedType = 'selected'; + + constructor(props) { + super(props); + this.offset = width - height; + this.handlerSize = height; + this.state = { + name: this.props.name, + value: this.props.value, + toggleable: true, + alignItems: this.props.value ? 'flex-end' : 'flex-start', + left: this.props.value ? onLeftValue : offLeftValue, + handlerAnimation: new Animated.Value(this.handlerSize), + switchAnimation: new Animated.Value(this.props.value ? -1 : 1), + }; + } + + componentWillReceiveProps(nextProps) { + const { value } = this.state; + if (nextProps === this.props) { + return; + } + if (typeof nextProps.value !== 'undefined' && nextProps.value !== value) { + this.toggleSwitch(true); + } + } + + componentWillMount() { + this.panResponder = PanResponder.create({ + onStartShouldSetPanResponder: () => true, + onStartShouldSetPanResponderCapture: () => true, + onMoveShouldSetPanResponder: () => true, + onMoveShouldSetPanResponderCapture: () => true, + onPanResponderTerminationRequest: () => true, + onPanResponderGrant: this.onPanResponderGrant, + onPanResponderMove: this.onPanResponderMove, + onPanResponderRelease: this.onPanResponderRelease, + }); + } + + onPanResponderGrant = () => { + this.animateHandler(height * 0.9); + }; + + onPanResponderMove = (evt, gestureState) => { + const { value } = this.state; + + this.setState({ + toggleable: value ? (gestureState.dx < 10) : (gestureState.dx > -10), + }); + }; + + onPanResponderRelease = () => { + const { toggleable } = this.state; + const { disabled, onValueChange } = this.props; + + if (toggleable && !disabled) { + if (onValueChange) { + this.toggleSwitch(onValueChange); + } + } + }; + + toggleSwitch = (result, callback = () => null) => { + const { value, switchAnimation } = this.state; + const toValue = !value; + + this.animateHandler(this.handlerSize); + + this.animateSwitch(toValue, () => { + callback(toValue); + this.setState({ + value: toValue, + left: toValue ? onLeftValue : offLeftValue, + }); + switchAnimation.setValue(toValue ? -1 : 1); + }); + }; + + animateSwitch = (value, callback = () => null) => { + const { switchAnimation } = this.state; + + Animated.timing( + switchAnimation, + { + toValue: value ? this.offset : -this.offset, + duration: animationDuration, + easing: Easing.linear, + }, + ).start(callback); + }; + + animateHandler = (value, callback = () => null) => { + const { handlerAnimation } = this.state; + + Animated.timing( + handlerAnimation, + { + toValue: value, + duration: animationDuration, + easing: Easing.linear, + }, + ).start(callback); + }; + + render() { + const { + switchAnimation, handlerAnimation, left, value, + } = this.state; + const { + style, + ...rest + } = this.props; + + const type = value ? this.selectedType : ''; + const { container, thumb } = this.defineStyles(type); + const onColor = this.extractNonStyleValue(container, 'onColor'); + const offColor = this.extractNonStyleValue(container, 'offColor'); + + const interpolatedBackgroundColor = switchAnimation.interpolate({ + inputRange: value ? [-this.offset, -1] : [1, this.offset], + outputRange: [offColor, onColor], + }); + + return ( + + + + ); + } +} diff --git a/frontend/app/components/switch/index.ios.js b/frontend/app/components/switch/index.ios.js new file mode 100644 index 0000000..068cbfb --- /dev/null +++ b/frontend/app/components/switch/index.ios.js @@ -0,0 +1,36 @@ +import React from 'react'; +import { Switch } from 'react-native'; +import { RkComponent } from 'react-native-ui-kitten'; + + +export class RkSwitch extends RkComponent { + componentName = 'RkSwitch'; + typeMapping = { + container: { + onColor: 'onColor', + offColor: 'offColor', + }, + main: {}, + }; + selectedType = 'selected'; + + constructor(props) { + super(props); + this.onChange = this.props.onValueChange ? + this.props.onValueChange + : () => true; + } + + render() { + const { container } = this.defineStyles(); + const onColor = this.extractNonStyleValue(container, 'onColor'); + return ( + this.onChange(value)} + onTintColor={onColor} + /> + ); + } +} diff --git a/frontend/app/components/switch/types.js b/frontend/app/components/switch/types.js new file mode 100644 index 0000000..96b116e --- /dev/null +++ b/frontend/app/components/switch/types.js @@ -0,0 +1,35 @@ +export const SwitchTypes = (theme) => ({ + _base: { + container: { + width: 52, + height: 32, + overflow: 'hidden', + justifyContent: 'center', + borderRadius: 16, + borderWidth: 1, + borderColor: theme.colors.border.secondary, + onColor: theme.colors.primary, + offColor: { + android: theme.colors.screen.base, + ios: theme.colors.border.base, + }, + }, + thumb: { + position: 'absolute', + height: 32, + width: 32, + borderWidth: 1, + borderColor: theme.colors.border.secondary, + backgroundColor: theme.colors.screen.base, + borderRadius: 16, + }, + }, + selected: { + thumb: { + borderColor: theme.colors.primary, + }, + container: { + borderColor: theme.colors.primary, + }, + }, +}); diff --git a/frontend/app/components/walkthrough.js b/frontend/app/components/walkthrough.js new file mode 100644 index 0000000..7fdb247 --- /dev/null +++ b/frontend/app/components/walkthrough.js @@ -0,0 +1,63 @@ +import React from 'react'; +import { + View, + FlatList, + Dimensions, + StyleSheet, +} from 'react-native'; +import PropTypes from 'prop-types'; + +export class Walkthrough extends React.Component { + static propTypes = { + children: PropTypes.arrayOf(PropTypes.element).isRequired, + onChanged: PropTypes.func, + }; + static defaultProps = { + onChanged: (() => null), + }; + + constructor(props) { + super(props); + this.itemWidth = Dimensions.get('window').width; + } + + extractItemKey = (item) => `${this.props.children.indexOf(item)}`; + + onScrollEnd = (e) => { + const { contentOffset } = e.nativeEvent; + const viewSize = e.nativeEvent.layoutMeasurement; + const pageNum = Math.floor(contentOffset.x / viewSize.width); + this.props.onChanged(pageNum); + }; + + renderItem = ({ item }) => ( + + {item} + + ); + + render = () => ( + null} + showsHorizontalScrollIndicator={false} + showsVerticalScrollIndicator={false} + directionalLockEnabled + renderItem={this.renderItem} + /> + ); +} + +const styles = StyleSheet.create({ + list: { + flex: 1, + }, + item: { + flex: 1, + }, +}); -- cgit v1.2.3