diff options
Diffstat (limited to 'frontend/app')
197 files changed, 7713 insertions, 0 deletions
diff --git a/frontend/app/app.js b/frontend/app/app.js new file mode 100644 index 0000000..f469e15 --- /dev/null +++ b/frontend/app/app.js @@ -0,0 +1,92 @@ +import React from 'react'; +import { View } from 'react-native'; +import { + AppLoading, + Font, +} from 'expo'; +import { + createDrawerNavigator, + createStackNavigator, +} from 'react-navigation'; +import { withRkTheme } from 'react-native-ui-kitten'; +import { AppRoutes } from './config/navigation/routesBuilder'; +import * as Screens from './screens'; +import { bootstrap } from './config/bootstrap'; +import track from './config/analytics'; +import { data } from './data'; + +bootstrap(); +data.populateData(); + +const KittenApp = createStackNavigator({ + First: { + screen: Screens.SplashScreen, + }, + Home: { + screen: createDrawerNavigator( + { + ...AppRoutes, + }, + { + contentComponent: (props) => { + const SideMenu = withRkTheme(Screens.SideMenu); + return <SideMenu {...props} />; + }, + }, + ), + }, +}, { + headerMode: 'none', +}); + +export default class App extends React.Component { + state = { + isLoaded: false, + }; + + componentWillMount() { + this.loadAssets(); + } + + onNavigationStateChange = (previous, current) => { + const screen = { + current: this.getCurrentRouteName(current), + previous: this.getCurrentRouteName(previous), + }; + if (screen.previous !== screen.current) { + track(screen.current); + } + }; + + getCurrentRouteName = (navigation) => { + const route = navigation.routes[navigation.index]; + return route.routes ? this.getCurrentRouteName(route) : route.routeName; + }; + + loadAssets = async () => { + await Font.loadAsync({ + fontawesome: require('./assets/fonts/fontawesome.ttf'), + icomoon: require('./assets/fonts/icomoon.ttf'), + 'Righteous-Regular': require('./assets/fonts/Righteous-Regular.ttf'), + 'Roboto-Bold': require('./assets/fonts/Roboto-Bold.ttf'), + 'Roboto-Medium': require('./assets/fonts/Roboto-Medium.ttf'), + 'Roboto-Regular': require('./assets/fonts/Roboto-Regular.ttf'), + 'Roboto-Light': require('./assets/fonts/Roboto-Light.ttf'), + }); + this.setState({ isLoaded: true }); + }; + + renderLoading = () => ( + <AppLoading /> + ); + + renderApp = () => ( + <View style={{ flex: 1 }}> + <KittenApp onNavigationStateChange={this.onNavigationStateChange} /> + </View> + ); + + render = () => (this.state.isLoaded ? this.renderApp() : this.renderLoading()); +} + +Expo.registerRootComponent(App); diff --git a/frontend/app/assets/fonts/Righteous-Regular.ttf b/frontend/app/assets/fonts/Righteous-Regular.ttf Binary files differnew file mode 100755 index 0000000..07fc0b4 --- /dev/null +++ b/frontend/app/assets/fonts/Righteous-Regular.ttf diff --git a/frontend/app/assets/fonts/Roboto-Bold.ttf b/frontend/app/assets/fonts/Roboto-Bold.ttf Binary files differnew file mode 100755 index 0000000..d3f01ad --- /dev/null +++ b/frontend/app/assets/fonts/Roboto-Bold.ttf diff --git a/frontend/app/assets/fonts/Roboto-Light.ttf b/frontend/app/assets/fonts/Roboto-Light.ttf Binary files differnew file mode 100755 index 0000000..219063a --- /dev/null +++ b/frontend/app/assets/fonts/Roboto-Light.ttf diff --git a/frontend/app/assets/fonts/Roboto-Medium.ttf b/frontend/app/assets/fonts/Roboto-Medium.ttf Binary files differnew file mode 100755 index 0000000..1a7f3b0 --- /dev/null +++ b/frontend/app/assets/fonts/Roboto-Medium.ttf diff --git a/frontend/app/assets/fonts/Roboto-Regular.ttf b/frontend/app/assets/fonts/Roboto-Regular.ttf Binary files differnew file mode 100755 index 0000000..2c97eea --- /dev/null +++ b/frontend/app/assets/fonts/Roboto-Regular.ttf diff --git a/frontend/app/assets/fonts/fontawesome.ttf b/frontend/app/assets/fonts/fontawesome.ttf Binary files differnew file mode 100644 index 0000000..35acda2 --- /dev/null +++ b/frontend/app/assets/fonts/fontawesome.ttf diff --git a/frontend/app/assets/fonts/icomoon.ttf b/frontend/app/assets/fonts/icomoon.ttf Binary files differnew file mode 100755 index 0000000..8a05f73 --- /dev/null +++ b/frontend/app/assets/fonts/icomoon.ttf diff --git a/frontend/app/assets/icons.js b/frontend/app/assets/icons.js new file mode 100644 index 0000000..59871f8 --- /dev/null +++ b/frontend/app/assets/icons.js @@ -0,0 +1,30 @@ +export const FontIcons = { + login: '', + navigation: '', + article: '', + profile: '', + mail: '', + dashboard: '', + mobile: '', + other: '', + theme: '', + card: '', + addToCardForm: '', +}; + +export const FontAwesome = { + heart: String.fromCharCode(61444), + comment: String.fromCharCode(61669), + user: String.fromCharCode(62144), + twitter: String.fromCharCode(61593), + google: String.fromCharCode(61856), + facebook: String.fromCharCode(61594), + plus: String.fromCharCode(61543), + search: String.fromCharCode(61442), + smile: String.fromCharCode(61720), + chevronRight: String.fromCharCode(61524), + chevronLeft: String.fromCharCode(61700), + bars: String.fromCharCode(61641), + slashEye: String.fromCharCode(61552), + github: String.fromCharCode(61595), +}; diff --git a/frontend/app/assets/icons/americanExpressIcon.png b/frontend/app/assets/icons/americanExpressIcon.png Binary files differnew file mode 100644 index 0000000..7d69723 --- /dev/null +++ b/frontend/app/assets/icons/americanExpressIcon.png diff --git a/frontend/app/assets/icons/americanExpressIcon@2x.png b/frontend/app/assets/icons/americanExpressIcon@2x.png Binary files differnew file mode 100644 index 0000000..767407a --- /dev/null +++ b/frontend/app/assets/icons/americanExpressIcon@2x.png diff --git a/frontend/app/assets/icons/americanExpressIcon@3x.png b/frontend/app/assets/icons/americanExpressIcon@3x.png Binary files differnew file mode 100644 index 0000000..08426e5 --- /dev/null +++ b/frontend/app/assets/icons/americanExpressIcon@3x.png diff --git a/frontend/app/assets/icons/iconPlus.png b/frontend/app/assets/icons/iconPlus.png Binary files differnew file mode 100644 index 0000000..acc2bee --- /dev/null +++ b/frontend/app/assets/icons/iconPlus.png diff --git a/frontend/app/assets/icons/iconPlus@2x.png b/frontend/app/assets/icons/iconPlus@2x.png Binary files differnew file mode 100644 index 0000000..47e42bf --- /dev/null +++ b/frontend/app/assets/icons/iconPlus@2x.png diff --git a/frontend/app/assets/icons/iconPlus@3x.png b/frontend/app/assets/icons/iconPlus@3x.png Binary files differnew file mode 100644 index 0000000..9e81cb7 --- /dev/null +++ b/frontend/app/assets/icons/iconPlus@3x.png diff --git a/frontend/app/assets/icons/masterCardIcon.png b/frontend/app/assets/icons/masterCardIcon.png Binary files differnew file mode 100644 index 0000000..32d0cf0 --- /dev/null +++ b/frontend/app/assets/icons/masterCardIcon.png diff --git a/frontend/app/assets/icons/masterCardIcon@2x.png b/frontend/app/assets/icons/masterCardIcon@2x.png Binary files differnew file mode 100644 index 0000000..abbae18 --- /dev/null +++ b/frontend/app/assets/icons/masterCardIcon@2x.png diff --git a/frontend/app/assets/icons/masterCardIcon@3x.png b/frontend/app/assets/icons/masterCardIcon@3x.png Binary files differnew file mode 100644 index 0000000..f2656c0 --- /dev/null +++ b/frontend/app/assets/icons/masterCardIcon@3x.png diff --git a/frontend/app/assets/icons/sendIcon.png b/frontend/app/assets/icons/sendIcon.png Binary files differnew file mode 100644 index 0000000..59e98a1 --- /dev/null +++ b/frontend/app/assets/icons/sendIcon.png diff --git a/frontend/app/assets/icons/sendIcon@2x.png b/frontend/app/assets/icons/sendIcon@2x.png Binary files differnew file mode 100644 index 0000000..ccd42b6 --- /dev/null +++ b/frontend/app/assets/icons/sendIcon@2x.png diff --git a/frontend/app/assets/icons/sendIcon@3x.png b/frontend/app/assets/icons/sendIcon@3x.png Binary files differnew file mode 100644 index 0000000..0f51420 --- /dev/null +++ b/frontend/app/assets/icons/sendIcon@3x.png diff --git a/frontend/app/assets/icons/visaIcon.png b/frontend/app/assets/icons/visaIcon.png Binary files differnew file mode 100644 index 0000000..89b74da --- /dev/null +++ b/frontend/app/assets/icons/visaIcon.png diff --git a/frontend/app/assets/icons/visaIcon@2x.png b/frontend/app/assets/icons/visaIcon@2x.png Binary files differnew file mode 100644 index 0000000..2fd239c --- /dev/null +++ b/frontend/app/assets/icons/visaIcon@2x.png diff --git a/frontend/app/assets/icons/visaIcon@3x.png b/frontend/app/assets/icons/visaIcon@3x.png Binary files differnew file mode 100644 index 0000000..014fd20 --- /dev/null +++ b/frontend/app/assets/icons/visaIcon@3x.png diff --git a/frontend/app/assets/images/backgroundLoginV1.png b/frontend/app/assets/images/backgroundLoginV1.png Binary files differnew file mode 100644 index 0000000..fadd9c1 --- /dev/null +++ b/frontend/app/assets/images/backgroundLoginV1.png diff --git a/frontend/app/assets/images/backgroundLoginV1@2x.png b/frontend/app/assets/images/backgroundLoginV1@2x.png Binary files differnew file mode 100644 index 0000000..97ed4c8 --- /dev/null +++ b/frontend/app/assets/images/backgroundLoginV1@2x.png diff --git a/frontend/app/assets/images/backgroundLoginV1@3x.png b/frontend/app/assets/images/backgroundLoginV1@3x.png Binary files differnew file mode 100644 index 0000000..fce0f0d --- /dev/null +++ b/frontend/app/assets/images/backgroundLoginV1@3x.png diff --git a/frontend/app/assets/images/backgroundLoginV1DarkTheme.png b/frontend/app/assets/images/backgroundLoginV1DarkTheme.png Binary files differnew file mode 100644 index 0000000..bde640e --- /dev/null +++ b/frontend/app/assets/images/backgroundLoginV1DarkTheme.png diff --git a/frontend/app/assets/images/backgroundLoginV1DarkTheme@2x.png b/frontend/app/assets/images/backgroundLoginV1DarkTheme@2x.png Binary files differnew file mode 100644 index 0000000..2779ff4 --- /dev/null +++ b/frontend/app/assets/images/backgroundLoginV1DarkTheme@2x.png diff --git a/frontend/app/assets/images/backgroundLoginV1DarkTheme@3x.png b/frontend/app/assets/images/backgroundLoginV1DarkTheme@3x.png Binary files differnew file mode 100644 index 0000000..25b7678 --- /dev/null +++ b/frontend/app/assets/images/backgroundLoginV1DarkTheme@3x.png diff --git a/frontend/app/assets/images/darkThemeImage.png b/frontend/app/assets/images/darkThemeImage.png Binary files differnew file mode 100644 index 0000000..22a8aad --- /dev/null +++ b/frontend/app/assets/images/darkThemeImage.png diff --git a/frontend/app/assets/images/darkThemeImage@2x.png b/frontend/app/assets/images/darkThemeImage@2x.png Binary files differnew file mode 100644 index 0000000..5fd83c8 --- /dev/null +++ b/frontend/app/assets/images/darkThemeImage@2x.png diff --git a/frontend/app/assets/images/darkThemeImage@3x.png b/frontend/app/assets/images/darkThemeImage@3x.png Binary files differnew file mode 100644 index 0000000..97476a8 --- /dev/null +++ b/frontend/app/assets/images/darkThemeImage@3x.png diff --git a/frontend/app/assets/images/icon.png b/frontend/app/assets/images/icon.png Binary files differnew file mode 100644 index 0000000..69d250e --- /dev/null +++ b/frontend/app/assets/images/icon.png diff --git a/frontend/app/assets/images/install/mt86plus b/frontend/app/assets/images/install/mt86plus Binary files differnew file mode 100644 index 0000000..9ad8027 --- /dev/null +++ b/frontend/app/assets/images/install/mt86plus diff --git a/frontend/app/assets/images/kittenImage.png b/frontend/app/assets/images/kittenImage.png Binary files differnew file mode 100644 index 0000000..f69ba83 --- /dev/null +++ b/frontend/app/assets/images/kittenImage.png diff --git a/frontend/app/assets/images/kittenImage@2x.png b/frontend/app/assets/images/kittenImage@2x.png Binary files differnew file mode 100644 index 0000000..1603f06 --- /dev/null +++ b/frontend/app/assets/images/kittenImage@2x.png diff --git a/frontend/app/assets/images/kittenImage@3x.png b/frontend/app/assets/images/kittenImage@3x.png Binary files differnew file mode 100644 index 0000000..3e42333 --- /dev/null +++ b/frontend/app/assets/images/kittenImage@3x.png diff --git a/frontend/app/assets/images/kittenImageDark.png b/frontend/app/assets/images/kittenImageDark.png Binary files differnew file mode 100644 index 0000000..7dab278 --- /dev/null +++ b/frontend/app/assets/images/kittenImageDark.png diff --git a/frontend/app/assets/images/kittenImageDark@2x.png b/frontend/app/assets/images/kittenImageDark@2x.png Binary files differnew file mode 100644 index 0000000..9b154fe --- /dev/null +++ b/frontend/app/assets/images/kittenImageDark@2x.png diff --git a/frontend/app/assets/images/kittenImageDark@3x.png b/frontend/app/assets/images/kittenImageDark@3x.png Binary files differnew file mode 100644 index 0000000..98e2dab --- /dev/null +++ b/frontend/app/assets/images/kittenImageDark@3x.png diff --git a/frontend/app/assets/images/lightThemeImage.png b/frontend/app/assets/images/lightThemeImage.png Binary files differnew file mode 100644 index 0000000..36e8202 --- /dev/null +++ b/frontend/app/assets/images/lightThemeImage.png diff --git a/frontend/app/assets/images/lightThemeImage@2x.png b/frontend/app/assets/images/lightThemeImage@2x.png Binary files differnew file mode 100644 index 0000000..cc103fd --- /dev/null +++ b/frontend/app/assets/images/lightThemeImage@2x.png diff --git a/frontend/app/assets/images/lightThemeImage@3x.png b/frontend/app/assets/images/lightThemeImage@3x.png Binary files differnew file mode 100644 index 0000000..f743a07 --- /dev/null +++ b/frontend/app/assets/images/lightThemeImage@3x.png diff --git a/frontend/app/assets/images/logo.png b/frontend/app/assets/images/logo.png Binary files differnew file mode 100644 index 0000000..c671367 --- /dev/null +++ b/frontend/app/assets/images/logo.png diff --git a/frontend/app/assets/images/logo@2x.png b/frontend/app/assets/images/logo@2x.png Binary files differnew file mode 100644 index 0000000..9597747 --- /dev/null +++ b/frontend/app/assets/images/logo@2x.png diff --git a/frontend/app/assets/images/logo@3x.png b/frontend/app/assets/images/logo@3x.png Binary files differnew file mode 100644 index 0000000..e1fc224 --- /dev/null +++ b/frontend/app/assets/images/logo@3x.png diff --git a/frontend/app/assets/images/logoDark.png b/frontend/app/assets/images/logoDark.png Binary files differnew file mode 100644 index 0000000..744a239 --- /dev/null +++ b/frontend/app/assets/images/logoDark.png diff --git a/frontend/app/assets/images/logoDark@2x.png b/frontend/app/assets/images/logoDark@2x.png Binary files differnew file mode 100644 index 0000000..93e62f8 --- /dev/null +++ b/frontend/app/assets/images/logoDark@2x.png diff --git a/frontend/app/assets/images/logoDark@3x.png b/frontend/app/assets/images/logoDark@3x.png Binary files differnew file mode 100644 index 0000000..9de7bd5 --- /dev/null +++ b/frontend/app/assets/images/logoDark@3x.png diff --git a/frontend/app/assets/images/screensImage.png b/frontend/app/assets/images/screensImage.png Binary files differnew file mode 100644 index 0000000..4e672c6 --- /dev/null +++ b/frontend/app/assets/images/screensImage.png diff --git a/frontend/app/assets/images/screensImage@2x.png b/frontend/app/assets/images/screensImage@2x.png Binary files differnew file mode 100644 index 0000000..a1adefc --- /dev/null +++ b/frontend/app/assets/images/screensImage@2x.png diff --git a/frontend/app/assets/images/screensImage@3x.png b/frontend/app/assets/images/screensImage@3x.png Binary files differnew file mode 100644 index 0000000..c7c40cf --- /dev/null +++ b/frontend/app/assets/images/screensImage@3x.png diff --git a/frontend/app/assets/images/screensImageDark.png b/frontend/app/assets/images/screensImageDark.png Binary files differnew file mode 100644 index 0000000..1e4290d --- /dev/null +++ b/frontend/app/assets/images/screensImageDark.png diff --git a/frontend/app/assets/images/screensImageDark@2x.png b/frontend/app/assets/images/screensImageDark@2x.png Binary files differnew file mode 100644 index 0000000..d60ef34 --- /dev/null +++ b/frontend/app/assets/images/screensImageDark@2x.png diff --git a/frontend/app/assets/images/screensImageDark@3x.png b/frontend/app/assets/images/screensImageDark@3x.png Binary files differnew file mode 100644 index 0000000..7b6c2e9 --- /dev/null +++ b/frontend/app/assets/images/screensImageDark@3x.png diff --git a/frontend/app/assets/images/smallLogo.png b/frontend/app/assets/images/smallLogo.png Binary files differnew file mode 100644 index 0000000..934203f --- /dev/null +++ b/frontend/app/assets/images/smallLogo.png diff --git a/frontend/app/assets/images/smallLogo@2x.png b/frontend/app/assets/images/smallLogo@2x.png Binary files differnew file mode 100644 index 0000000..2256c76 --- /dev/null +++ b/frontend/app/assets/images/smallLogo@2x.png diff --git a/frontend/app/assets/images/smallLogo@3x.png b/frontend/app/assets/images/smallLogo@3x.png Binary files differnew file mode 100644 index 0000000..6b23ffa --- /dev/null +++ b/frontend/app/assets/images/smallLogo@3x.png diff --git a/frontend/app/assets/images/smallLogoDark.png b/frontend/app/assets/images/smallLogoDark.png Binary files differnew file mode 100644 index 0000000..2809ba7 --- /dev/null +++ b/frontend/app/assets/images/smallLogoDark.png diff --git a/frontend/app/assets/images/smallLogoDark@2x.png b/frontend/app/assets/images/smallLogoDark@2x.png Binary files differnew file mode 100644 index 0000000..769e6b5 --- /dev/null +++ b/frontend/app/assets/images/smallLogoDark@2x.png diff --git a/frontend/app/assets/images/smallLogoDark@3x.png b/frontend/app/assets/images/smallLogoDark@3x.png Binary files differnew file mode 100644 index 0000000..5af69c3 --- /dev/null +++ b/frontend/app/assets/images/smallLogoDark@3x.png diff --git a/frontend/app/assets/images/splashBack.png b/frontend/app/assets/images/splashBack.png Binary files differnew file mode 100644 index 0000000..f79f8e0 --- /dev/null +++ b/frontend/app/assets/images/splashBack.png diff --git a/frontend/app/assets/images/splashBack@2x.png b/frontend/app/assets/images/splashBack@2x.png Binary files differnew file mode 100644 index 0000000..c9dfe1f --- /dev/null +++ b/frontend/app/assets/images/splashBack@2x.png diff --git a/frontend/app/assets/images/splashBack@3x.png b/frontend/app/assets/images/splashBack@3x.png Binary files differnew file mode 100644 index 0000000..d163eca --- /dev/null +++ b/frontend/app/assets/images/splashBack@3x.png 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) => ( + <View> + <Image style={styles.image} source={this.props.img} /> + { this.props.badge && this.renderBadge(styles.badge)} + </View> + ); + + renderBadge = (style, textStyle) => { + const badgeStyle = this.getBadgeStyle(this.props.badge); + return ( + <View style={[style, { backgroundColor: badgeStyle.backgroundColor }]}> + <RkText rkType='awesome' style={[textStyle, { color: badgeStyle.color }]}> + {badgeStyle.symbol} + </RkText> + </View> + ); + }; + + render() { + const { container, ...other } = this.defineStyles(); + return ( + <View style={[container, this.props.style]}> + {this.renderImg(other)} + </View> + ); + } +} 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 = () => ( + <RkButton + style={styles.button} + rkType='clear' + onPress={this.onInputLabelPressed}> + <RkText style={styles.icon} rkType='awesome secondaryColor'>{FontAwesome.slashEye}</RkText> + </RkButton> + ); + + render = () => ( + <RkTextInput + autoCapitalize='none' + rkType='bordered rounded iconRight' + autoCorrect={false} + label={this.renderInputLabel()} + secureTextEntry={this.state.hidden} + onChangeText={this.onInputChanged} + value={this.state.cardNumber} + keyboardType='numeric' + maxLength={19} + {...this.props} + /> + ); +} + +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 = () => ( + <View> + <RkText rkType='header4'>REAL TIME VISITORS</RkText> + <VictoryChart + padding={{ + top: 20, left: 40, right: 5, bottom: 5, + }} + width={this.size - 60}> + <VictoryAxis + tickValues={[]} + style={{ + axis: { stroke: 'transparent' }, + }} + /> + <VictoryAxis + dependentAxis + tickValues={['50', '100', '150', '200']} + style={{ + axis: { stroke: 'transparent' }, + grid: { stroke: RkTheme.current.colors.disabled, strokeWidth: 0.5 }, + tickLabels: { + fontSize: 14, + stroke: RkTheme.current.colors.text.secondary, + fill: RkTheme.current.colors.text.secondary, + fontFamily: RkTheme.current.fonts.family.regular, + strokeWidth: 0.5, + }, + }} + /> + <VictoryGroup data={this.state.data}> + <VictoryArea + style={{ + data: { + fill: RkTheme.current.colors.charts.area.fill, + fillOpacity: 0.5, + stroke: RkTheme.current.colors.charts.area.stroke, + strokeOpacity: 0.8, + strokeWidth: 1.5, + }, + }} + /> + <VictoryScatter + style={{ + data: { + fill: 'white', + stroke: RkTheme.current.colors.charts.area.stroke, + strokeOpacity: 0.8, + strokeWidth: 1.5, + }, + }} + /> + </VictoryGroup> + </VictoryChart> + </View> + ); +} 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) => ( + <VictoryArea + key={`${area.length * index}`} + interpolation="natural" + style={{ + data: { + fill: this.colors[index], + stroke: this.colors[index], + }, + }} + data={area} + /> + )); + + render = () => ( + <View> + <RkText rkType='header4'>NEW FOLLOWERS</RkText> + <VictoryChart + padding={{ + top: 20, left: 40, right: 15, bottom: 40, + }} + width={this.size - 60}> + <VictoryAxis + tickValues={['Sun', 'Mon', 'Tue', ' Wed', 'Thu', 'Fri', 'Sat']} + style={{ + axis: { stroke: 'transparent' }, + tickLabels: { + fontSize: 14, + stroke: RkTheme.current.colors.text.secondary, + fill: RkTheme.current.colors.text.secondary, + fontFamily: RkTheme.current.fonts.family.regular, + strokeWidth: 0.5, + }, + }} + /> + <VictoryAxis + dependentAxis + tickValues={['10k', '20k', '30k', '40k']} + style={{ + axis: { stroke: 'transparent' }, + grid: { stroke: RkTheme.current.colors.disabled, strokeWidth: 0.5 }, + tickLabels: { + fontSize: 14, + stroke: RkTheme.current.colors.text.secondary, + fill: RkTheme.current.colors.text.secondary, + fontFamily: RkTheme.current.fonts.family.regular, + strokeWidth: 0.5, + }, + }} + /> + {this.renderChartAreas()} + </VictoryChart> + </View> + ) +} 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) => ( + <View key={item.name} style={styles.legendItem}> + <View style={[styles.itemBadge, { backgroundColor: item.color }]} /> + <RkText rkType="primary3">{item.name}</RkText> + </View> + ); + + render = () => ( + <View> + <RkText rkType='header4'>AUDIENCE OVERVIEW</RkText> + <View style={{ alignSelf: 'center' }}> + <Svg width={scale(this.size)} height={scale(this.size)}> + <VictoryPie + labels={[]} + width={scale(this.size)} + height={scale(this.size)} + colorScale={this.computeColors()} + data={this.state.data} + standalone={false} + padding={scale(25)} + innerRadius={scale(70)} + events={[{ + target: 'data', + eventHandlers: { + onPressIn: this.onPeopleChartPressed, + }, + }]} + /> + <SvgText + textAnchor="middle" + verticalAnchor="middle" + x={scale(this.size / 2)} + y={scale(this.size / 2)} + height={scale(this.fontSize)} + fontSize={scale(this.fontSize)} + fontFamily={RkTheme.current.fonts.family.regular} + stroke={RkTheme.current.colors.text.base} + fill={RkTheme.current.colors.text.base}> + {this.state.data[this.state.selected].title} + </SvgText> + </Svg> + </View> + <View style={styles.legendContainer}> + {this.renderMarkdown()} + </View> + </View> + ); +} + +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 = () => ( + <View> + <RkText rkType='header4'>FOLLOWERS</RkText> + <View style={styles.chartContainer}> + <Svg width={scale(this.size)} height={scale(this.size)}> + <VictoryPie + labels={[]} + padding={0} + standalone={false} + width={scale(this.size)} + height={scale(this.size)} + style={{ data: { fill: this.onChartFill } }} + data={this.getChartData()} + cornerRadius={scale(25)} + innerRadius={scale(40)} + /> + <SvgText + textAnchor="middle" + verticalAnchor="middle" + x={scale(this.size / 2)} + y={scale(this.size / 2)} + height={scale(this.fontSize)} + fontSize={scale(this.fontSize)} + fontFamily={RkTheme.current.fonts.family.regular} + stroke={RkTheme.current.colors.text.base} + fill={RkTheme.current.colors.text.base}> + {`${this.state.percents}%`} + </SvgText> + </Svg> + <View> + <RkText rkType='header4'>REACH</RkText> + <RkText rkType='header2'>1 500 356</RkText> + <RkText rkType='secondary2'>+6 per day in average</RkText> + </View> + </View> + </View> + ); +} + +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 ( + <View style={styles.container}> + <View style={styles.dot} /> + <View style={styles.dot} /> + <View style={styles.dot} /> + </View> + ); + } +} + +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 ( + <TouchableOpacity style={[styles.wrapper, this.props.style]} onPress={this.props.onPress}> + <View style={styles.container}> + <View style={styles.text}> + <RkText rkType='awesome' style={[styles.icon, { color }]}>{this.props.icon}</RkText> + <RkText rkType='header6' style={{ color }}>{`Find Friends With ${this.props.text}`}</RkText> + </View> + <RkText rkType='awesome small' style={{ color }}>{FontAwesome.chevronRight}</RkText> + </View> + </TouchableOpacity> + ); + }; +} + +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) => ( + <View style={styles.header}> + <RkButton rkType='clear contrast' onPress={options.closeImage}>Close</RkButton> + <RkText rkType='header4'>{`${options.pageNumber}/${options.totalPages}`}</RkText> + <RkButton rkType='clear'> + <Ellipsis /> + </RkButton> + </View> + ); + + renderFooter = () => ( + <SocialBar /> + ); + + renderItem = ({ index }) => ( + <RkModalImg + style={{ width: this.state.itemSize, height: this.state.itemSize }} + renderHeader={this.renderHeader} + renderFooter={this.renderFooter} + source={this.props.items} + index={index} + /> + ); + + render = () => ( + <View style={styles.images}> + <FlatList + data={this.state.data} + numColumns={3} + keyExtractor={this.extractItemKey} + renderItem={this.renderItem} + /> + </View> + ); +} + +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) => ( + <RkText style={textStyle}>{this.props.text}</RkText> + ); + + render() { + const { button, gradient, text: textStyle } = this.defineStyles(); + const { style, rkType, ...restProps } = this.props; + const colors = this.props.colors || this.extractNonStyleValue(gradient, 'colors'); + return ( + <RkButton + rkType='stretch' + style={[button, style]} + {...restProps}> + <LinearGradient + colors={colors} + start={{ x: 0.0, y: 0.5 }} + end={{ x: 1, y: 0.5 }} + style={[gradient]}> + {this.renderContent(textStyle)} + </LinearGradient> + </RkButton> + ); + } +} 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) => ( + <View style={styles.title}> + <RkText>{title}</RkText> + </View> + ); + + renderNavigationLeftBackItem = () => ( + <RkButton + rkType='clear' + style={styles.menu} + onPress={this.onNavigationLeftBackButtonPressed}> + <RkText rkType='awesome hero'>{FontAwesome.chevronLeft}</RkText> + </RkButton> + ); + + renderNavigationLeftMenuItem = () => ( + <RkButton + rkType='clear' + style={styles.menu} + onPress={this.onNavigationLeftMenuButtonPressed}> + <RkText rkType='awesome'>{FontAwesome.bars}</RkText> + </RkButton> + ); + + renderNavigationLeftItemContent = (sceneIndex) => { + const isFirstScene = sceneIndex === 0; + return isFirstScene ? this.renderNavigationLeftMenuItem() : this.renderNavigationLeftBackItem(); + }; + + renderNavigationLeftItem = () => { + const sceneIndex = _.findIndex(this.props.headerProps.scenes, { isActive: true }); + return ( + <View style={styles.left}> + {this.renderNavigationLeftItemContent(sceneIndex)} + </View> + ); + }; + + renderNavigationRightItem = () => undefined; + + renderCustomTitleItem = (options) => ( + <View + style={styles.title}> + {options} + </View> + ); + + renderCustomLeftItem = (options) => ( + <View style={styles.left}>{options}</View> + ); + + renderCustomRightItem = (options) => ( + <View style={styles.right}>{options}</View> + ); + + render() { + const { options } = this.props.headerProps.scene.descriptor; + return ( + <View style={styles.layout}> + <View style={styles.container}> + {this.renderTitleItem(options.title, options.headerTitle)} + {this.renderLeftItem(options.headerLeft)} + {this.renderRightItem(options.headerRight)} + </View> + </View> + ); + } +} + +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) => ( + <View style={selected ? [styles.base, styles.selected] : styles.base} key={index} /> + ); + + 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 = () => ( + <View style={styles.container}> + {this.renderIndicators()} + </View> + ); +} + +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 = () => ( + <RkButton + style={styles.button} + rkType='clear' + onPress={this.onInputLabelPressed}> + <RkText style={styles.icon} rkType='awesome secondaryColor'>{FontAwesome.slashEye}</RkText> + </RkButton> + ); + + render = () => ( + <RkTextInput + autoCapitalize='none' + rkType='bordered rounded iconRight' + autoCorrect={false} + label={this.renderInputLabel()} + secureTextEntry={this.state.hidden} + {...this.props} + /> + ); +} + +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 ( + <RkPicker + rkType='highlight' + title='Set Date' + data={data} + onConfirm={this.onDatePickerConfirm} + selectedOptions={selectedOptions} + optionRkType='subtitle small' + selectedOptionRkType='header4' + titleTextRkType='header4' + cancelTextRkType='light' + confirmTextRkType='' + {...props} + /> + ); + } +} 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 ( + <View style={[styles.container, this.props.style, { width: this.props.width }]}> + <Animated.View style={[styles.value, { width }, { backgroundColor: this.props.color }]} /> + </View> + ); + } +} + +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 ( + <View style={container}> + <View style={section}> + <RkButton rkType='clear' onPress={this.onCommentButtonPressed}> + <RkText rkType='awesome hintColor' style={icon}>{FontAwesome.comment}</RkText> + <RkText rkType='primary4 hintColor' style={label}>{comments}</RkText> + </RkButton> + </View> + <View style={section}> + <RkButton rkType='clear' > + <RkText rkType='awesome hintColor' style={icon}>{FontAwesome.slashEye}</RkText> + <RkText rkType='primary4 hintColor' style={label}>{is_solved}</RkText> + </RkButton> + </View> + </View> + ); + } +} 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 ( + <View style={styles.container}> + <View style={styles.left}> + <RkText rkType='awesome large' style={[styles.icon, { color }]}>{this.props.icon}</RkText> + <RkText rkType='small' style={{ color }}>{this.props.name}</RkText> + </View> + <RkSwitch value={this.state.selected} onValueChange={this.onSwitchValueChanged} /> + </View> + ); + } +} + +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 ( + <Animated.View + {...rest} + {...this.panResponder.panHandlers} + style={[style, container, { + backgroundColor: interpolatedBackgroundColor, + }]}> + <Animated.View style={[thumb, { + position: 'absolute', + left, + height: handlerAnimation, + transform: [{ translateX: switchAnimation }], + }]} + /> + </Animated.View> + ); + } +} 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 ( + <Switch + style={this.props.style} + value={this.props.value} + onValueChange={(value) => 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 }) => ( + <View style={[styles.item, { width: this.itemWidth }]}> + {item} + </View> + ); + + render = () => ( + <FlatList + style={styles.list} + data={this.props.children} + onMomentumScrollEnd={this.onScrollEnd} + keyExtractor={this.extractItemKey} + pagingEnabled + horizontal + renderSeparator={() => null} + showsHorizontalScrollIndicator={false} + showsVerticalScrollIndicator={false} + directionalLockEnabled + renderItem={this.renderItem} + /> + ); +} + +const styles = StyleSheet.create({ + list: { + flex: 1, + }, + item: { + flex: 1, + }, +}); diff --git a/frontend/app/config/analytics.js b/frontend/app/config/analytics.js new file mode 100644 index 0000000..6e48218 --- /dev/null +++ b/frontend/app/config/analytics.js @@ -0,0 +1,7 @@ +import { Analytics, PageHit } from 'expo-analytics'; + +const analytics = new Analytics('UA-112172761-2'); + +const track = screen => analytics.hit(new PageHit(screen)); + +export default track; diff --git a/frontend/app/config/appConstants.js b/frontend/app/config/appConstants.js new file mode 100644 index 0000000..a120915 --- /dev/null +++ b/frontend/app/config/appConstants.js @@ -0,0 +1,7 @@ +import { Platform } from 'react-native'; + +export class UIConstants { + static AppbarHeight = Platform.OS === 'ios' ? 44 : 56; + static StatusbarHeight = Platform.OS === 'ios' ? 20 : 0; + static HeaderHeight = UIConstants.AppbarHeight + UIConstants.StatusbarHeight; +} diff --git a/frontend/app/config/bootstrap.js b/frontend/app/config/bootstrap.js new file mode 100644 index 0000000..58fe84f --- /dev/null +++ b/frontend/app/config/bootstrap.js @@ -0,0 +1,492 @@ +import { + StatusBar, + StyleSheet, +} from 'react-native'; +import { RkTheme } from 'react-native-ui-kitten'; +import { KittenTheme } from './theme'; +import { AvatarTypes } from '../components/avatar/types'; +import { GradientButtonTypes } from '../components/gradientButton/types'; +import { SwitchTypes } from '../components/switch/types'; +import { SocialBarTypes } from '../components/socialBar/types'; +import { scale, scaleVertical } from '../utils/scale'; + +export const bootstrap = () => { + RkTheme.setTheme(KittenTheme, null); + + /* + RkText types + */ + + RkTheme.setType('RkText', 'basic', { + fontFamily: theme => theme.fonts.family.bold, + backgroundColor: 'transparent', + }); + + RkTheme.setType('RkText', 'regular', { + fontFamily: theme => theme.fonts.family.regular, + }); + + RkTheme.setType('RkText', 'light', { + fontFamily: theme => theme.fonts.family.light, + }); + + RkTheme.setType('RkText', 'logo', { + fontFamily: theme => theme.fonts.family.logo, + }); + + RkTheme.setType('RkText', 'moon', { + fontFamily: 'icomoon', + }); + + RkTheme.setType('RkText', 'awesome', { + fontFamily: 'fontawesome', + }); + + RkTheme.setType('RkText', 'hero', { + fontSize: scale(33), + }); + + RkTheme.setType('RkText', 'menuIcon', { + fontSize: 44, + }); + + // all font sizes + Object.keys(RkTheme.current.fonts.sizes).forEach(key => { + RkTheme.setType('RkText', key, { + fontSize: theme => theme.fonts.sizes[key], + }); + }); + + // all text colors + Object.keys(RkTheme.current.colors.text).forEach(key => { + RkTheme.setType('RkText', `${key}Color`, { + color: theme => theme.colors.text[key], + }); + }); + + // all text line heights + Object.keys(RkTheme.current.fonts.lineHeights).forEach(key => { + RkTheme.setType('RkText', `${key}Line`, { + text: { lineHeight: theme => theme.fonts.lineHeights[key] }, + }); + }); + + // theme text styles + RkTheme.setType('RkText', 'header1', { + fontSize: theme => theme.fonts.sizes.h1, + fontFamily: theme => theme.fonts.family.bold, + }); + RkTheme.setType('RkText', 'header2', { + fontSize: theme => theme.fonts.sizes.h2, + fontFamily: theme => theme.fonts.family.bold, + }); + RkTheme.setType('RkText', 'header3', { + fontSize: theme => theme.fonts.sizes.h3, + fontFamily: theme => theme.fonts.family.bold, + }); + RkTheme.setType('RkText', 'header4', { + fontSize: theme => theme.fonts.sizes.h4, + fontFamily: theme => theme.fonts.family.bold, + }); + RkTheme.setType('RkText', 'header5', { + fontSize: theme => theme.fonts.sizes.h5, + fontFamily: theme => theme.fonts.family.bold, + }); + RkTheme.setType('RkText', 'header6', { + fontSize: theme => theme.fonts.sizes.h6, + fontFamily: theme => theme.fonts.family.bold, + }); + RkTheme.setType('RkText', 'secondary1', { + fontSize: theme => theme.fonts.sizes.s1, + fontFamily: theme => theme.fonts.family.light, + }); + RkTheme.setType('RkText', 'secondary2', { + fontSize: theme => theme.fonts.sizes.s2, + fontFamily: theme => theme.fonts.family.light, + }); + RkTheme.setType('RkText', 'secondary3', { + fontSize: theme => theme.fonts.sizes.s3, + fontFamily: theme => theme.fonts.family.regular, + }); + RkTheme.setType('RkText', 'secondary4', { + fontSize: theme => theme.fonts.sizes.s4, + fontFamily: theme => theme.fonts.family.regular, + }); + RkTheme.setType('RkText', 'secondary5', { + fontSize: theme => theme.fonts.sizes.s5, + fontFamily: theme => theme.fonts.family.light, + }); + RkTheme.setType('RkText', 'secondary6', { + fontSize: theme => theme.fonts.sizes.s6, + fontFamily: theme => theme.fonts.family.light, + }); + RkTheme.setType('RkText', 'secondary7', { + fontSize: theme => theme.fonts.sizes.s7, + fontFamily: theme => theme.fonts.family.regular, + }); + RkTheme.setType('RkText', 'primary1', { + fontSize: theme => theme.fonts.sizes.p1, + fontFamily: theme => theme.fonts.family.light, + }); + RkTheme.setType('RkText', 'primary2', { + fontSize: theme => theme.fonts.sizes.p2, + fontFamily: theme => theme.fonts.family.regular, + }); + RkTheme.setType('RkText', 'primary3', { + fontSize: theme => theme.fonts.sizes.p3, + fontFamily: theme => theme.fonts.family.light, + }); + RkTheme.setType('RkText', 'primary4', { + fontSize: theme => theme.fonts.sizes.p4, + fontFamily: theme => theme.fonts.family.regular, + }); + + RkTheme.setType('RkText', 'center', { + text: { + textAlign: 'center', + }, + }); + + RkTheme.setType('RkText', 'chat', { + color: theme => theme.colors.chat.text, + }); + /* + RkButton types + */ + + RkTheme.setType('RkButton', 'basic', { + container: { + alignSelf: 'auto', + }, + }); + + RkTheme.setType('RkButton', 'square', { + borderRadius: 3, + backgroundColor: theme => theme.colors.button.back, + container: { + flexDirection: 'column', + margin: 8, + }, + }); + + RkTheme.setType('RkButton', 'tile', { + borderRadius: 0, + backgroundColor: 'transparent', + borderWidth: 0.5, + borderColor: theme => theme.colors.border.base, + container: { + flexDirection: 'column', + }, + }); + + RkTheme.setType('RkButton', 'link', { + color: theme => theme.colors.primary, + }); + + RkTheme.setType('RkButton', 'contrast', { + color: theme => theme.colors.text.base, + }); + + RkTheme.setType('RkButton', 'icon', { + height: scale(56), + width: scale(56), + borderColor: theme => theme.colors.border.base, + backgroundColor: theme => theme.colors.control.background, + borderWidth: 1, + }); + + RkTheme.setType('RkButton', 'highlight', { + backgroundColor: theme => theme.colors.button.highlight, + }); + + RkTheme.setType('RkButton', 'social', { + height: scale(62), + width: scale(62), + borderRadius: scale(31), + borderColor: theme => theme.colors.border.accent, + borderWidth: 1, + backgroundColor: theme => theme.colors.control.background, + }); + /* + RkModalImg types + */ + + RkTheme.setType('RkModalImg', 'basic', { + img: { + margin: 1.5, + }, + modal: { + backgroundColor: theme => theme.colors.screen.base, + }, + footer: { + backgroundColor: theme => theme.colors.screen.base, + height: 50, + }, + header: { + backgroundColor: theme => theme.colors.screen.base, + paddingBottom: 6, + }, + }); + + /* + RkTextInput + */ + + RkTheme.setType('RkTextInput', 'basic', { + input: { + fontFamily: theme => theme.fonts.family.bold, + }, + color: theme => theme.colors.text.base, + backgroundColor: theme => theme.colors.control.background, + labelColor: theme => theme.colors.input.label, + placeholderTextColor: theme => theme.colors.input.placeholder, + }); + + RkTheme.setType('RkTextInput', 'rounded', { + fontSize: theme => theme.fonts.sizes.h6, + borderWidth: 1, + underlineWidth: 1, + placeholderTextColor: theme => theme.colors.input.text, + input: { + marginVertical: { + ios: scaleVertical(15), + android: scaleVertical(4), + }, + }, + }); + + + RkTheme.setType('RkTextInput', 'right', { + input: { + textAlign: 'right', + marginTop: { + ios: scaleVertical(18), + android: scaleVertical(11), + }, + }, + label: { + fontFamily: theme => theme.fonts.family.light, + }, + container: { + marginVertical: 4, + }, + backgroundColor: 'transparent', + labelFontSize: theme => theme.fonts.sizes.small, + }); + + RkTheme.setType('RkTextInput', 'row', { + input: { + marginVertical: 0, + marginHorizontal: 0, + marginTop: 0, + paddingTop: { + ios: 2, + android: 0, + }, + paddingBottom: 0, + textAlignVertical: 'center', + includeFontPadding: false, + fontFamily: theme => theme.fonts.family.light, + fontSize: theme => theme.fonts.sizes.small, + }, + container: { + flex: 1, + backgroundColor: theme => theme.colors.input.background, + marginVertical: 0, + borderRadius: 20, + paddingHorizontal: 16, + }, + + }); + + RkTheme.setType('RkTextInput', 'iconRight', { + label: { + position: 'absolute', + right: 0, + }, + input: { + marginRight: scale(46), + }, + }); + + RkTheme.setType('RkTextInput', 'sticker', { + input: { + marginHorizontal: 14, + }, + container: { + justifyContent: 'center', + paddingHorizontal: 0, + }, + label: { + position: 'absolute', + right: 0, + }, + }); + + /* + RkCard types + */ + + RkTheme.setType('RkCard', 'basic', { + container: { + borderRadius: 3, + backgroundColor: theme => theme.colors.control.background, + }, + header: { + justifyContent: 'flex-start', + paddingVertical: 14, + }, + content: { + padding: 16, + }, + footer: { + paddingBottom: 20, + paddingTop: 7.5, + paddingHorizontal: 0, + }, + }); + + RkTheme.setType('RkCard', 'backImg', { + container: { + borderWidth: 0, + borderRadius: 0, + }, + img: { + height: 225, + }, + imgOverlay: { + height: 225, + backgroundColor: 'transparent', + }, + content: { + paddingHorizontal: 14, + }, + footer: { + paddingTop: 15, + paddingBottom: 0, + paddingVertical: 7.5, + paddingHorizontal: 0, + }, + }); + + + RkTheme.setType('RkCard', 'imgBlock', { + img: { + height: 235, + }, + header: { + padding: 0, + paddingVertical: 13, + paddingHorizontal: 16, + }, + imgOverlay: { + height: -1, + }, + footer: { + paddingTop: 18, + paddingBottom: 15, + paddingVertical: 0, + paddingHorizontal: 0, + }, + }); + + RkTheme.setType('RkCard', 'horizontal', { + container: { + flexDirection: 'row', + height: 110, + }, + content: { + flex: 1, + }, + img: { + height: null, + flex: -1, + width: 120, + }, + }); + + RkTheme.setType('RkCard', 'blog', { + header: { + paddingHorizontal: 16, + paddingVertical: 0, + paddingTop: 16, + }, + content: { + padding: 0, + paddingVertical: 0, + paddingTop: 12, + }, + footer: { + paddingHorizontal: 16, + paddingTop: 15, + paddingBottom: 16, + alignItems: 'center', + }, + }); + + RkTheme.setType('RkCard', 'article', { + container: { + borderWidth: 0, + backgroundColor: 'transparent', + }, + header: { + paddingVertical: 0, + paddingTop: 20, + paddingBottom: 16, + justifyContent: 'space-between', + borderBottomWidth: StyleSheet.hairlineWidth, + borderColor: theme => theme.colors.border.base, + }, + content: { + padding: 16, + borderBottomWidth: 1, + borderColor: theme => theme.colors.border.base, + }, + footer: { + paddingHorizontal: 14, + paddingTop: 15, + paddingBottom: 16, + alignItems: 'center', + }, + }); + + RkTheme.setType('RkCard', 'credit', { + container: { + borderWidth: 0, + borderRadius: 7, + }, + header: { + justifyContent: 'space-between', + paddingHorizontal: 14, + alignItems: 'center', + paddingBottom: scaleVertical(46), + }, + content: { + alignItems: 'center', + paddingVertical: 0, + }, + footer: { + paddingBottom: scaleVertical(14), + paddingTop: scaleVertical(16), + paddingHorizontal: 14, + alignItems: 'flex-end', + }, + }); + + RkTheme.setType('RkPicker', 'highlight', { + highlightBorderTopColor: theme => theme.colors.border.highlight, + highlightBorderBottomColor: theme => theme.colors.border.highlight, + windowBorderColor: theme => theme.colors.border.highlight, + }); + + /* + Register components + */ + + RkTheme.registerComponent('Avatar', AvatarTypes); + RkTheme.registerComponent('GradientButton', GradientButtonTypes); + RkTheme.registerComponent('RkSwitch', SwitchTypes); + RkTheme.registerComponent('SocialBar', SocialBarTypes); + + StatusBar.setBarStyle('dark-content', true); +}; diff --git a/frontend/app/config/darkTheme.js b/frontend/app/config/darkTheme.js new file mode 100644 index 0000000..4461dcb --- /dev/null +++ b/frontend/app/config/darkTheme.js @@ -0,0 +1,209 @@ +const Colors = { + accent: '#ffffff', + primary: '#ffffff', + success: '#3bd555', + disabled: '#686894', + danger: '#f53d56', + + foreground: '#ffffff', + alterForeground: '#acacd2', + inverseForeground: '#ffffff', + secondaryForeground: '#bcbcbc', + hintForeground: '#969696', + fadedForeground: '#ffffffdd', + + boldBackground: '#090f3f', + background: '#0a1142', + alterBackground: '#12194d', + overlayBackground: '#00000057', + neutralBackground: '#2f396b', + fadedBackground: '#28305a', + brandBackground: '#6b35e4', + + border: '#080e36', + + twitter: '#ffffff', + google: '#ffffff', + facebook: '#ffffff', + + gradientBaseBegin: '#6b35e4', + gradientBaseEnd: '#6b35e4', + gradientVisaBegin: '#63e2ff', + gradientVisaEnd: '#712ec3', + gradientMasterBegin: '#febb5b', + gradientMasterEnd: '#f24645', + gradientAxpBegin: '#42e695', + gradientAxpEnd: '#3bb2bb', + highlight: '#acacd2', + + faded: '#e5e5e5', + icon: '#c2c2c2', + neutral: '#f2f2f2', + + info: '#2942ff', + warning: '#feb401', + + doughnutFirst: '#d500f9', + doughnutSecond: '#7c4dff', + doughnutThird: '#40c4ff', + doughnutFourth: '#2962ff', + + followersProgress: '#d500f9', + + followersFirst: '#2942ff', + followersSecond: '#1b2ba6', + followersThird: '#081c6e', + followersFourth: '#09103f', + + chartsAreaStroke: '#2942ff', + chartsAreaFill: '#0d1238', +}; + +const Fonts = { + light: 'Roboto-Light', + regular: 'Roboto-Regular', + bold: 'Roboto-Medium', + logo: 'Righteous-Regular', +}; + +const FontBaseValue = 18; + +export const DarkKittenTheme = { + name: 'dark', + colors: { + accent: Colors.accent, + primary: Colors.primary, + disabled: Colors.disabled, + twitter: Colors.twitter, + google: Colors.google, + facebook: Colors.facebook, + brand: Colors.brandBackground, + info: Colors.info, + infoActive: Colors.info, + text: { + base: Colors.foreground, + secondary: Colors.foreground, + accent: Colors.accent, + inverse: Colors.inverseForeground, + hint: Colors.alterForeground, + }, + screen: { + base: Colors.background, + alter: Colors.alterBackground, + scroll: Colors.background, + bold: Colors.boldBackground, + overlay: Colors.overlayBackground, + }, + button: { + back: Colors.alterBackground, + underlay: Colors.neutralBackground, + highlight: Colors.brandBackground, + }, + input: { + text: Colors.alterForeground, + background: Colors.alterBackground, + label: Colors.alterForeground, + placeholder: Colors.alterForeground, + }, + border: { + base: Colors.border, + accent: Colors.alterBackground, + secondary: Colors.secondaryForeground, + highlight: Colors.highlight, + }, + control: { + background: Colors.alterBackground, + }, + badge: { + likeBackground: Colors.foreground, + likeForeground: Colors.danger, + plusBackground: Colors.foreground, + plusForeground: Colors.success, + }, + chat: { + messageInBackground: Colors.fadedBackground, + messageOutBackground: Colors.neutralBackground, + text: Colors.fadedForeground, + }, + gradients: { + base: [ + Colors.gradientBaseBegin, + Colors.gradientBaseEnd, + ], + visa: [ + Colors.gradientVisaBegin, + Colors.gradientVisaEnd, + ], + mastercard: [ + Colors.gradientMasterBegin, + Colors.gradientMasterEnd, + ], + axp: [ + Colors.gradientAxpBegin, + Colors.gradientAxpEnd, + ], + }, + dashboard: { + stars: Colors.alterBackground, + tweets: Colors.alterBackground, + likes: Colors.alterBackground, + }, + charts: { + followersProgress: Colors.followersProgress, + doughnut: [ + Colors.doughnutFirst, + Colors.doughnutSecond, + Colors.doughnutThird, + Colors.doughnutFourth, + ], + followersArea: [ + Colors.followersFirst, + Colors.followersSecond, + Colors.followersThird, + Colors.followersFourth, + ], + area: { + stroke: Colors.chartsAreaStroke, + fill: Colors.chartsAreaFill, + }, + }, + }, + fonts: { + sizes: { + h0: 32, + h1: 26, + h2: 24, + h3: 20, + h4: 18, + h5: 16, + h6: 15, + p1: 16, + p2: 15, + p3: 15, + p4: 13, + s1: 15, + s2: 14, + s3: 14, + s4: 12, + s5: 12, + s6: 13, + s7: 10, + base: FontBaseValue, + small: FontBaseValue * 0.8, + medium: FontBaseValue, + large: FontBaseValue * 1.2, + xlarge: FontBaseValue / 0.75, + xxlarge: FontBaseValue * 1.6, + }, + lineHeights: { + medium: 18, + big: 24, + }, + family: { + regular: Fonts.regular, + light: Fonts.light, + bold: Fonts.bold, + logo: Fonts.logo, + }, + }, +}; diff --git a/frontend/app/config/navigation/propTypes.js b/frontend/app/config/navigation/propTypes.js new file mode 100644 index 0000000..a7c6ffc --- /dev/null +++ b/frontend/app/config/navigation/propTypes.js @@ -0,0 +1,15 @@ +import PropTypes from 'prop-types'; + +const shape = (propShape) => PropTypes.shape(propShape); + +const functionTypes = { + goBack: PropTypes.func, + navigate: PropTypes.func, +}; + +const NavigationType = shape({ + goBack: functionTypes.goBack.isRequired, + navigate: functionTypes.navigate.isRequired, +}); + +export default NavigationType; diff --git a/frontend/app/config/navigation/routes.js b/frontend/app/config/navigation/routes.js new file mode 100644 index 0000000..6a75312 --- /dev/null +++ b/frontend/app/config/navigation/routes.js @@ -0,0 +1,269 @@ +import _ from 'lodash'; +import {FontIcons} from '../../assets/icons'; +import * as Screens from '../../screens/index'; + +export const MainRoutes = [ + { + id: 'LoginMenu', + title: 'Auth', + icon: FontIcons.login, + screen: Screens.LoginMenu, + children: [ + { + id: 'Login1', + title: 'Login V1', + screen: Screens.LoginV1, + children: [], + }, + { + id: 'Login2', + title: 'Login V2', + screen: Screens.LoginV2, + children: [], + }, + { + id: 'SignUp', + title: 'Sign Up', + screen: Screens.SignUp, + children: [], + }, + { + id: 'password', + title: 'Password Recovery', + screen: Screens.PasswordRecovery, + children: [], + }, + ], + }, + { + id: 'SocialMenu', + title: 'Social', + icon: FontIcons.profile, + screen: Screens.SocialMenu, + children: [ + { + id: 'ProfileV1', + title: 'User Profile V1', + screen: Screens.ProfileV1, + children: [], + }, + { + id: 'ProfileV2', + title: 'User Profile V2', + screen: Screens.ProfileV2, + children: [], + }, + { + id: 'ProfileV3', + title: 'User Profile V3', + screen: Screens.ProfileV3, + children: [], + }, + { + id: 'ProfileSettings', + title: 'Profile Settings', + screen: Screens.ProfileSettings, + children: [], + }, + { + id: 'Notifications', + title: 'Notifications', + screen: Screens.Notifications, + children: [], + }, + { + id: 'Contacts', + title: 'Contacts', + screen: Screens.Contacts, + children: [], + }, + { + id: 'Feed', + title: 'Feed', + screen: Screens.Feed, + children: [], + }, + ], + }, + { + id: 'ArticlesMenu', + title: 'Articles', + icon: FontIcons.article, + screen: Screens.ArticleMenu, + children: [ + { + id: 'Articles1', + title: 'Article List V1', + screen: Screens.Articles1, + children: [], + }, + { + id: 'Articles2', + title: 'Article List V2', + screen: Screens.Articles2, + children: [], + }, + { + id: 'Articles3', + title: 'Article List V3', + screen: Screens.Articles3, + children: [], + }, + { + id: 'Articles4', + title: 'Article List V4', + screen: Screens.Articles4, + children: [], + }, + { + id: 'Blogposts', + title: 'Blogposts', + screen: Screens.Blogposts, + children: [], + }, + { + id: 'Article', + title: 'Article View', + screen: Screens.Article, + children: [], + }, + ], + }, + { + id: 'MessagingMenu', + title: 'Messaging', + icon: FontIcons.mail, + screen: Screens.MessagingMenu, + children: [ + { + id: 'Chat', + title: 'Chat', + screen: Screens.Chat, + children: [], + }, + { + id: 'ChatList', + title: 'Chat List', + screen: Screens.ChatList, + children: [], + }, + { + id: 'Comments', + title: 'Comments', + screen: Screens.Comments, + children: [], + }, + ], + }, + { + id: 'DashboardsMenu', + title: 'Dashboards', + icon: FontIcons.dashboard, + screen: Screens.DashboardMenu, + children: [{ + id: 'Dashboard', + title: 'Dashboard', + screen: Screens.Dashboard, + children: [], + }], + }, + { + id: 'WalkthroughMenu', + title: 'Walkthroughs', + icon: FontIcons.mobile, + screen: Screens.WalkthroughMenu, + children: [{ + id: 'Walkthrough', + title: 'Walkthrough', + screen: Screens.WalkthroughScreen, + children: [], + }], + }, + { + id: 'EcommerceMenu', + title: 'Ecommerce', + icon: FontIcons.card, + screen: Screens.EcommerceMenu, + children: [ + { + id: 'Cards', + title: 'Cards', + icon: FontIcons.card, + screen: Screens.Cards, + children: [], + }, + { + id: 'AddProblem', + title: 'Add Card Form', + icon: FontIcons.addToCardForm, + screen: Screens.AddToCardForm, + children: [], + }, + + ], + }, + { + id: 'NavigationMenu', + icon: FontIcons.navigation, + title: 'Navigation', + screen: Screens.NavigationMenu, + children: [ + { + id: 'GridV1', + title: 'Grid Menu V1', + screen: Screens.GridV1, + children: [], + }, + { + id: 'GridV2', + title: 'Grid Menu V2', + screen: Screens.GridV2, + children: [], + }, + { + id: 'List', + title: 'List Menu', + screen: Screens.ListMenu, + children: [], + }, + { + id: 'Side', + title: 'Side Menu', + action: 'DrawerOpen', + screen: Screens.SideMenu, + children: [], + }, + ], + }, + { + id: 'OtherMenu', + title: 'Other', + icon: FontIcons.other, + screen: Screens.OtherMenu, + children: [ + { + id: 'Settings', + title: 'Settings', + screen: Screens.Settings, + children: [], + }, + ], + }, + { + id: 'Themes', + title: 'Themes', + icon: FontIcons.theme, + screen: Screens.Themes, + children: [], + }, +]; + +const menuRoutes = _.cloneDeep(MainRoutes); +menuRoutes.unshift({ + id: 'Walkthrough', + title: 'Walkthrough', + screen: Screens.WalkthroughScreen, + children: [], +}); + +export const MenuRoutes = menuRoutes; diff --git a/frontend/app/config/navigation/routesBuilder.js b/frontend/app/config/navigation/routesBuilder.js new file mode 100644 index 0000000..157a958 --- /dev/null +++ b/frontend/app/config/navigation/routesBuilder.js @@ -0,0 +1,62 @@ +import React from 'react'; +import _ from 'lodash'; +import { createStackNavigator } from 'react-navigation'; +import { withRkTheme } from 'react-native-ui-kitten'; +import { NavBar } from '../../components/index'; +import transition from './transitions'; +import { + MainRoutes, + MenuRoutes, +} from './routes'; + +const main = {}; +const flatRoutes = {}; + +const routeMapping = (route) => ({ + screen: withRkTheme(route.screen), + title: route.title, +}); + +(MenuRoutes).forEach(route => { + flatRoutes[route.id] = routeMapping(route); + main[route.id] = routeMapping(route); + route.children.forEach(nestedRoute => { + flatRoutes[nestedRoute.id] = routeMapping(nestedRoute); + }); +}); + +const renderHeader = (navigation, props) => { + const ThemedNavigationBar = withRkTheme(NavBar); + return ( + <ThemedNavigationBar navigation={navigation} headerProps={props} /> + ); +}; + +const DrawerRoutes = Object.keys(main).reduce((routes, name) => { + const rawRoutes = routes; + rawRoutes[name] = { + name, + screen: createStackNavigator(flatRoutes, { + initialRouteName: name, + headerMode: 'screen', + cardStyle: { backgroundColor: 'transparent' }, + transitionConfig: transition, + navigationOptions: ({ navigation }) => ({ + gesturesEnabled: false, + header: (props) => renderHeader(navigation, props), + }), + }), + }; + return rawRoutes; +}, {}); + +export const AppRoutes = DrawerRoutes; +export const LoginRoutes = _.find(MainRoutes, { id: 'LoginMenu' }).children; +export const NavigationRoutes = _.find(MainRoutes, { id: 'NavigationMenu' }).children; +export const SocialRoutes = _.find(MainRoutes, { id: 'SocialMenu' }).children; +export const ArticleRoutes = _.find(MainRoutes, { id: 'ArticlesMenu' }).children; +export const MessagingRoutes = _.find(MainRoutes, { id: 'MessagingMenu' }).children; +export const DashboardRoutes = _.find(MainRoutes, { id: 'DashboardsMenu' }).children; +export const WalkthroughRoutes = _.find(MainRoutes, { id: 'WalkthroughMenu' }).children; +export const EcommerceRoutes = _.find(MainRoutes, { id: 'EcommerceMenu' }).children; +export const OtherRoutes = _.find(MainRoutes, { id: 'OtherMenu' }).children; diff --git a/frontend/app/config/navigation/transitions.js b/frontend/app/config/navigation/transitions.js new file mode 100644 index 0000000..ff3d2fa --- /dev/null +++ b/frontend/app/config/navigation/transitions.js @@ -0,0 +1,59 @@ +import { + Dimensions, + Platform, +} from 'react-native'; + +const { width } = Dimensions.get('window'); + +const IosTransition = (index, position) => { + const inputRange = [index - 1, index, index + 0.99, index + 1]; + const outputRange = [width, 0, -10, -10]; + + const translateY = 0; + const translateX = position.interpolate({ + inputRange, + outputRange, + }); + + const opacity = position.interpolate({ + inputRange, + outputRange: [0, 1, 1, 0], + }); + return { + opacity, + transform: [{ translateX }, { translateY }], + }; +}; + +const DroidTransition = (index, position) => { + const inputRange = [index - 1, index, index + 0.99, index + 1]; + + const opacity = position.interpolate({ + inputRange, + outputRange: [0, 1, 1, 0], + }); + + const translateX = 0; + const translateY = position.interpolate({ + inputRange, + outputRange: [50, 0, 0, 0], + }); + + return { + opacity, + transform: [{ translateX }, { translateY }], + }; +}; + +function transition() { + return { + screenInterpolator: (sceneProps) => { + const { position, scene } = sceneProps; + const { index } = scene; + if (Platform.OS === 'ios') { return IosTransition(index, position); } + return DroidTransition(index, position); + }, + }; +} + +export default transition; diff --git a/frontend/app/config/theme.js b/frontend/app/config/theme.js new file mode 100644 index 0000000..b3f4225 --- /dev/null +++ b/frontend/app/config/theme.js @@ -0,0 +1,210 @@ +import { scale } from '../utils/scale'; + +const Colors = { + accent: '#5c9b3a', + primary: '#f64e59', + success: '#3bd555', + disabled: '#cacaca', + + foreground: '#212121', + alterForeground: '#707070', + inverseForeground: '#ffffff', + secondaryForeground: '#bcbcbc', + hintForeground: '#969696', + highlight: '#bcbcbc', + + background: '#ffffff', + alterBackground: '#f2f2f2', + overlayBackground: '#00000057', + neutralBackground: '#f2f2f2', + fadedBackground: '#e5e5e5', + + border: '#f2f2f2', + + twitter: '#41abe1', + google: '#e94335', + facebook: '#3b5998', + + gradientBaseBegin: '#69b338', + gradientBaseEnd: '#0d712a', + gradientVisaBegin: '#63e2ff', + gradientVisaEnd: '#712ec3', + gradientMasterBegin: '#febb5b', + gradientMasterEnd: '#f24645', + gradientAxpBegin: '#42e695', + gradientAxpEnd: '#3bb2bb', + + faded: '#e5e5e5', + icon: '#c2c2c2', + neutral: '#f2f2f2', + + info: '#19bfe5', + warning: '#feb401', + danger: '#ed1c4d', + + starsStat: '#2ab5fa', + tweetsStat: '#ffc61c', + likesStat: '#5468ff', + + doughnutFirst: '#8a98ff', + doughnutSecond: '#ffd146', + doughnutThird: '#c2d521', + doughnutFourth: '#ff6b5c', + + followersProgress: '#c2d521', + + followersFirst: '#b3e5fc', + followersSecond: '#81d4fa', + followersThird: '#4fc3f7', + followersFourth: '#42a5f5', + + chartsAreaStroke: '#097fe5', + chartsAreaFill: '#d6ecff', +}; + +const Fonts = { + light: 'Roboto-Light', + regular: 'Roboto-Regular', + bold: 'Roboto-Medium', + logo: 'Righteous-Regular', +}; + +const FontBaseValue = scale(18); + +export const KittenTheme = { + name: 'light', + colors: { + accent: Colors.accent, + primary: Colors.primary, + disabled: Colors.disabled, + twitter: Colors.twitter, + google: Colors.google, + facebook: Colors.facebook, + brand: Colors.accent, + text: { + base: Colors.foreground, + secondary: Colors.secondaryForeground, + accent: Colors.accent, + inverse: Colors.inverseForeground, + hint: Colors.alterForeground, + }, + input: { + text: Colors.alterForeground, + background: Colors.background, + label: Colors.secondaryForeground, + placeholder: Colors.secondaryForeground, + }, + screen: { + base: Colors.background, + alter: Colors.alterBackground, + scroll: Colors.alterBackground, + bold: Colors.alterBackground, + overlay: Colors.overlayBackground, + }, + button: { + back: Colors.background, + underlay: Colors.neutralBackground, + highlight: Colors.primary, + }, + border: { + base: Colors.border, + accent: Colors.accent, + secondary: Colors.secondaryForeground, + highlight: Colors.highlight, + }, + control: { + background: Colors.background, + }, + badge: { + likeBackground: Colors.primary, + likeForeground: Colors.inverseForeground, + plusBackground: Colors.success, + plusForeground: Colors.inverseForeground, + }, + chat: { + messageInBackground: Colors.neutralBackground, + messageOutBackground: Colors.fadedBackground, + text: Colors.foreground, + }, + gradients: { + base: [ + Colors.gradientBaseBegin, + Colors.gradientBaseEnd, + ], + visa: [ + Colors.gradientVisaBegin, + Colors.gradientVisaEnd, + ], + mastercard: [ + Colors.gradientMasterBegin, + Colors.gradientMasterEnd, + ], + axp: [ + Colors.gradientAxpBegin, + Colors.gradientAxpEnd, + ], + }, + dashboard: { + stars: Colors.starsStat, + tweets: Colors.tweetsStat, + likes: Colors.likesStat, + }, + charts: { + followersProgress: Colors.followersProgress, + doughnut: [ + Colors.doughnutFirst, + Colors.doughnutSecond, + Colors.doughnutThird, + Colors.doughnutFourth, + ], + followersArea: [ + Colors.followersFirst, + Colors.followersSecond, + Colors.followersThird, + Colors.followersFourth, + ], + area: { + stroke: Colors.chartsAreaStroke, + fill: Colors.chartsAreaFill, + }, + }, + }, + fonts: { + sizes: { + h0: scale(32), + h1: scale(26), + h2: scale(24), + h3: scale(20), + h4: scale(18), + h5: scale(16), + h6: scale(15), + p1: scale(16), + p2: scale(15), + p3: scale(15), + p4: scale(13), + s1: scale(15), + s2: scale(13), + s3: scale(13), + s4: scale(12), + s5: scale(12), + s6: scale(13), + s7: scale(10), + base: FontBaseValue, + small: FontBaseValue * 0.8, + medium: FontBaseValue, + large: FontBaseValue * 1.2, + xlarge: FontBaseValue / 0.75, + xxlarge: FontBaseValue * 1.6, + }, + lineHeights: { + medium: 18, + big: 24, + }, + family: { + regular: Fonts.regular, + light: Fonts.light, + bold: Fonts.bold, + logo: Fonts.logo, + }, + }, +}; diff --git a/frontend/app/data/dataGenerator.js b/frontend/app/data/dataGenerator.js new file mode 100644 index 0000000..1a171cf --- /dev/null +++ b/frontend/app/data/dataGenerator.js @@ -0,0 +1,45 @@ +import _ from 'lodash'; +import users from './raw/users'; +import articles from './raw/articles'; +import notifications from './raw/notifications'; +import conversations from './raw/conversations'; + +const populateArticles = () => { + articles.forEach(article => { + const userArticle = article; + const userId = articles.indexOf(article) % users.length; + userArticle.user = _.find(users, x => x.id === userId) || users[0]; + userArticle.comments.map(comment => { + const userComment = comment; + const commentUserId = article.comments.indexOf(comment) % users.length; + userComment.user = _.find(users, x => x.id === commentUserId) || users[0]; + return userComment; + }); + return userArticle; + }); +}; + +const populateNotifications = () => { + notifications.map(notification => { + const userNotification = notification; + const userId = notifications.indexOf(notification) % users.length; + userNotification.user = _.find(users, x => x.id === userId) || users[0]; + return userNotification; + }); +}; + +const populateConversations = () => { + conversations.map(conversation => { + const userConversation = conversation; + userConversation.withUser = _.find(users, x => x.id === conversation.withUserId) || users[0]; + return userConversation; + }); +}; + +const populate = () => { + populateArticles(); + populateNotifications(); + populateConversations(); +}; + +export default populate; diff --git a/frontend/app/data/img/Image1.png b/frontend/app/data/img/Image1.png Binary files differnew file mode 100755 index 0000000..51a7b11 --- /dev/null +++ b/frontend/app/data/img/Image1.png diff --git a/frontend/app/data/img/Image10.png b/frontend/app/data/img/Image10.png Binary files differnew file mode 100755 index 0000000..1d5a19b --- /dev/null +++ b/frontend/app/data/img/Image10.png diff --git a/frontend/app/data/img/Image11.png b/frontend/app/data/img/Image11.png Binary files differnew file mode 100755 index 0000000..a604b67 --- /dev/null +++ b/frontend/app/data/img/Image11.png diff --git a/frontend/app/data/img/Image12.png b/frontend/app/data/img/Image12.png Binary files differnew file mode 100755 index 0000000..8883ccf --- /dev/null +++ b/frontend/app/data/img/Image12.png diff --git a/frontend/app/data/img/Image2.png b/frontend/app/data/img/Image2.png Binary files differnew file mode 100755 index 0000000..0774f0a --- /dev/null +++ b/frontend/app/data/img/Image2.png diff --git a/frontend/app/data/img/Image3.png b/frontend/app/data/img/Image3.png Binary files differnew file mode 100755 index 0000000..3451fe3 --- /dev/null +++ b/frontend/app/data/img/Image3.png diff --git a/frontend/app/data/img/Image4.png b/frontend/app/data/img/Image4.png Binary files differnew file mode 100755 index 0000000..2cc648c --- /dev/null +++ b/frontend/app/data/img/Image4.png diff --git a/frontend/app/data/img/Image5.png b/frontend/app/data/img/Image5.png Binary files differnew file mode 100755 index 0000000..b407820 --- /dev/null +++ b/frontend/app/data/img/Image5.png diff --git a/frontend/app/data/img/Image6.png b/frontend/app/data/img/Image6.png Binary files differnew file mode 100755 index 0000000..886ad46 --- /dev/null +++ b/frontend/app/data/img/Image6.png diff --git a/frontend/app/data/img/Image7.png b/frontend/app/data/img/Image7.png Binary files differnew file mode 100755 index 0000000..c4ed0be --- /dev/null +++ b/frontend/app/data/img/Image7.png diff --git a/frontend/app/data/img/Image8.png b/frontend/app/data/img/Image8.png Binary files differnew file mode 100755 index 0000000..e9e67b1 --- /dev/null +++ b/frontend/app/data/img/Image8.png diff --git a/frontend/app/data/img/Image9.png b/frontend/app/data/img/Image9.png Binary files differnew file mode 100755 index 0000000..fd5112a --- /dev/null +++ b/frontend/app/data/img/Image9.png diff --git a/frontend/app/data/img/avatars/Image1.png b/frontend/app/data/img/avatars/Image1.png Binary files differnew file mode 100755 index 0000000..7e3754a --- /dev/null +++ b/frontend/app/data/img/avatars/Image1.png diff --git a/frontend/app/data/img/avatars/Image10.png b/frontend/app/data/img/avatars/Image10.png Binary files differnew file mode 100755 index 0000000..2b5c16d --- /dev/null +++ b/frontend/app/data/img/avatars/Image10.png diff --git a/frontend/app/data/img/avatars/Image11.png b/frontend/app/data/img/avatars/Image11.png Binary files differnew file mode 100755 index 0000000..3907308 --- /dev/null +++ b/frontend/app/data/img/avatars/Image11.png diff --git a/frontend/app/data/img/avatars/Image12.png b/frontend/app/data/img/avatars/Image12.png Binary files differnew file mode 100755 index 0000000..5429eeb --- /dev/null +++ b/frontend/app/data/img/avatars/Image12.png diff --git a/frontend/app/data/img/avatars/Image2.png b/frontend/app/data/img/avatars/Image2.png Binary files differnew file mode 100755 index 0000000..fb2123b --- /dev/null +++ b/frontend/app/data/img/avatars/Image2.png diff --git a/frontend/app/data/img/avatars/Image3.png b/frontend/app/data/img/avatars/Image3.png Binary files differnew file mode 100755 index 0000000..823e5e1 --- /dev/null +++ b/frontend/app/data/img/avatars/Image3.png diff --git a/frontend/app/data/img/avatars/Image4.png b/frontend/app/data/img/avatars/Image4.png Binary files differnew file mode 100755 index 0000000..86b24ba --- /dev/null +++ b/frontend/app/data/img/avatars/Image4.png diff --git a/frontend/app/data/img/avatars/Image5.png b/frontend/app/data/img/avatars/Image5.png Binary files differnew file mode 100755 index 0000000..508f7ab --- /dev/null +++ b/frontend/app/data/img/avatars/Image5.png diff --git a/frontend/app/data/img/avatars/Image6.png b/frontend/app/data/img/avatars/Image6.png Binary files differnew file mode 100755 index 0000000..d08e3d0 --- /dev/null +++ b/frontend/app/data/img/avatars/Image6.png diff --git a/frontend/app/data/img/avatars/Image7.png b/frontend/app/data/img/avatars/Image7.png Binary files differnew file mode 100755 index 0000000..30330de --- /dev/null +++ b/frontend/app/data/img/avatars/Image7.png diff --git a/frontend/app/data/img/avatars/Image8.png b/frontend/app/data/img/avatars/Image8.png Binary files differnew file mode 100755 index 0000000..840aab5 --- /dev/null +++ b/frontend/app/data/img/avatars/Image8.png diff --git a/frontend/app/data/img/avatars/Image9.png b/frontend/app/data/img/avatars/Image9.png Binary files differnew file mode 100755 index 0000000..d111f6d --- /dev/null +++ b/frontend/app/data/img/avatars/Image9.png diff --git a/frontend/app/data/img/photo1.png b/frontend/app/data/img/photo1.png Binary files differnew file mode 100644 index 0000000..a49e452 --- /dev/null +++ b/frontend/app/data/img/photo1.png diff --git a/frontend/app/data/img/photo17.png b/frontend/app/data/img/photo17.png Binary files differnew file mode 100644 index 0000000..2575aef --- /dev/null +++ b/frontend/app/data/img/photo17.png diff --git a/frontend/app/data/img/photo18.png b/frontend/app/data/img/photo18.png Binary files differnew file mode 100644 index 0000000..2f4d60f --- /dev/null +++ b/frontend/app/data/img/photo18.png diff --git a/frontend/app/data/img/photo19.png b/frontend/app/data/img/photo19.png Binary files differnew file mode 100644 index 0000000..a893ed0 --- /dev/null +++ b/frontend/app/data/img/photo19.png diff --git a/frontend/app/data/img/photo2.png b/frontend/app/data/img/photo2.png Binary files differnew file mode 100644 index 0000000..96cbcee --- /dev/null +++ b/frontend/app/data/img/photo2.png diff --git a/frontend/app/data/img/photo20.png b/frontend/app/data/img/photo20.png Binary files differnew file mode 100644 index 0000000..8c10cf2 --- /dev/null +++ b/frontend/app/data/img/photo20.png diff --git a/frontend/app/data/img/photo3.png b/frontend/app/data/img/photo3.png Binary files differnew file mode 100644 index 0000000..7197a4a --- /dev/null +++ b/frontend/app/data/img/photo3.png diff --git a/frontend/app/data/img/photo32.jpg b/frontend/app/data/img/photo32.jpg Binary files differnew file mode 100644 index 0000000..6237110 --- /dev/null +++ b/frontend/app/data/img/photo32.jpg diff --git a/frontend/app/data/img/photo4.png b/frontend/app/data/img/photo4.png Binary files differnew file mode 100644 index 0000000..59d901b --- /dev/null +++ b/frontend/app/data/img/photo4.png diff --git a/frontend/app/data/img/photo45.png b/frontend/app/data/img/photo45.png Binary files differnew file mode 100644 index 0000000..f403555 --- /dev/null +++ b/frontend/app/data/img/photo45.png diff --git a/frontend/app/data/img/photo46.png b/frontend/app/data/img/photo46.png Binary files differnew file mode 100644 index 0000000..2c9ae21 --- /dev/null +++ b/frontend/app/data/img/photo46.png diff --git a/frontend/app/data/img/photo47.png b/frontend/app/data/img/photo47.png Binary files differnew file mode 100644 index 0000000..4fc232b --- /dev/null +++ b/frontend/app/data/img/photo47.png diff --git a/frontend/app/data/img/photo48.png b/frontend/app/data/img/photo48.png Binary files differnew file mode 100644 index 0000000..881c554 --- /dev/null +++ b/frontend/app/data/img/photo48.png diff --git a/frontend/app/data/img/photo49.png b/frontend/app/data/img/photo49.png Binary files differnew file mode 100644 index 0000000..db0f107 --- /dev/null +++ b/frontend/app/data/img/photo49.png diff --git a/frontend/app/data/img/photo5.png b/frontend/app/data/img/photo5.png Binary files differnew file mode 100644 index 0000000..ba61c08 --- /dev/null +++ b/frontend/app/data/img/photo5.png diff --git a/frontend/app/data/img/photo6.png b/frontend/app/data/img/photo6.png Binary files differnew file mode 100644 index 0000000..8c93751 --- /dev/null +++ b/frontend/app/data/img/photo6.png diff --git a/frontend/app/data/index.js b/frontend/app/data/index.js new file mode 100644 index 0000000..2c5abb0 --- /dev/null +++ b/frontend/app/data/index.js @@ -0,0 +1,52 @@ +import _ from 'lodash'; +import populate from './dataGenerator'; +import users from './raw/users'; +import articles from './raw/articles'; +import notifications from './raw/notifications'; +import conversations from './raw/conversations'; +import cards from './raw/cards'; + +class DataProvider { + getUser(id = 1) { + return _.find(users, x => x.id === id); + } + + getUsers() { + return users; + } + + getNotifications() { + return notifications; + } + + getArticles(type = 'article') { + return _.filter(articles, x => x.type === type); + } + + getArticle(id) { + return _.find(articles, x => x.id === id); + } + + + getConversation(userId = 1) { + return _.find(conversations, x => x.withUser.id === userId); + } + + getChatList() { + return conversations; + } + + getComments(postId = 1) { + return this.getArticle(postId).comments; + } + + getCards() { + return cards; + } + + populateData() { + populate(); + } +} + +export const data = new DataProvider(); diff --git a/frontend/app/data/raw/articles.js b/frontend/app/data/raw/articles.js new file mode 100644 index 0000000..38bff3b --- /dev/null +++ b/frontend/app/data/raw/articles.js @@ -0,0 +1,173 @@ +const articles = [{ + id: 1, + photo: require('../img/photo1.png'), + type: 'article', + time: -300, + header: 'Plants Of Our Nature', + text: 'Ferns are a very old group of plants. They first appeared on Earth in the middle Devonian Era about 360 million years ago, just before the Carboniferous Era. Most of the modern fern families we see today first appeared in the Late Cretaceous about 45 or 50 million years ago – during the age of the dinosaurs!', + comments: [{ + id: 1, + text: 'Aliquam sit amet diam in magna bibendum imperdiet. Nullam orci pede, venenatis non, sodales sed, tincidunt eu, felis. Fusce posuere felis sed lacus. Morbi sem mauris, laoreet ut, rhoncus aliquet, pulvinar sed, nisl. Nunc rhoncus dui vel sem.', + time: 0, + }, { + id: 2, + text: 'Quisque ut erat. Curabitur gravida nisi at nibh.', + time: -311, + }, { + id: 3, + text: 'Etiam pretium iaculis justo. In hac habitasse platea dictumst. Etiam faucibus cursus urna. Ut tellus. Nulla ut erat id mauris vulputate elementum.', + time: -622, + }, { + id: 4, + text: 'In est risus, auctor sed, tristique in, tempus sit amet, sem.', + time: -933, + }, { + id: 5, + text: 'In hac habitasse platea dictumst.', + time: -1244, + }, { + id: 6, + text: 'In hac habitasse platea dictumst. Morbi vestibulum, velit id pretium iaculis, diam erat fermentum justo, nec condimentum neque sapien placerat ante. Nulla justo.', + time: -1555, + }, { + id: 7, + text: 'Vivamus vel nulla eget eros elementum pellentesque. Quisque porta volutpat erat.', + time: -1866, + }, { + id: 8, + text: 'Duis mattis egestas metus. Aenean fermentum. Donec ut mauris eget massa tempor convallis. Nulla neque libero, convallis eget, eleifend luctus, ultricies eu, nibh.', + time: -2177, + }], +}, { + id: 2, + photo: require('../img/photo2.png'), + type: 'article', + time: -1373, + header: 'Balloon Trip', + text: 'Mostly it’s about hot air - for without that balloons are just big empty bags with baskets on the bottom. ' + + 'The Montgolfier brothers had great hopes when they made the first manned flight. ' + + 'They thought balloons would take off as an available means of commercial flight. ' + + 'Instead, they have remained the province of sport, adventure and enjoyment. ' + + 'Modern balloons are a lot more sophisticated than their ancestors, ' + + 'but they still retain the essential characteristics which makes them so attractive. ' + + 'A plane is claustrophobic and very noisy. Balloons are so gentle and majestic and silent when the burner’s not working.', + comments: [], +}, { + id: 3, + photo: require('../img/photo3.png'), + type: 'article', + time: -2446, + header: 'Sea World', + text: 'The worlds oceans cover two thirds of our planet. As we take a dive from the rocks or paddle out from the beach, we are entering a place which is teeming with marine life. ' + + 'From fish to crabs to octopuses or even sea creatures that have not yet been discovered, the oceans and its coastlines are an amazing and interesting foray of water wildlife.', + comments: [], +}, { + id: 4, + photo: require('../img/photo4.png'), + type: 'article', + time: -3519, + header: 'Flowers', + text: 'Flowers did not always exist; they first appeared 140 million years ago. Before that, ferns and cone bearing trees dominated the earth. ' + + 'Several centuries ago in Holland, tulips were more valuable than gold. ' + + 'Broccoli is actually a flower. Some plants such as orchids do not need soil to grow-they get all of their nutrients from the air.', + comments: [], +}, { + id: 5, + photo: require('../img/photo5.png'), + type: 'article', + time: -4592, + header: 'Birds Of Our Planet', + text: 'Birds have feathers, wings, lay eggs and are warm blooded. There are around 10000 different species of birds worldwide. ' + + 'The Ostrich is the largest bird in the world. It also lays the largest eggs and has the fastest maximum running speed (97 kph). ' + + 'Scientists believe that birds evolved from theropod dinosaurs. Birds have hollow bones which help them fly. ' + + 'Some bird species are intelligent enough to create and use tools.', + comments: [], +}, { + id: 6, + photo: require('../img/photo6.png'), + type: 'article', + time: -5665, + header: 'Mountains', + text: 'Mountains make up about one-fifth of the world\'s landscape, and provide homes to at least one-tenth of the world\'s people. ' + + 'The tallest known mountain in the solar system is Olympus Mons, located on Mars. ' + + 'There are mountains under the surface of the sea! ' + + 'Mountains occur more often in oceans than on land; some islands are the peaks of mountains coming out of the water.', + comments: [], +}, { + id: 7, + photo: require('../img/photo45.png'), + type: 'fact', + time: -5665, + header: 'Smile and Frown', + text: 'It takes 17 muscles to smile and 43 to frown.', + comments: [], +}, { + id: 8, + photo: require('../img/photo46.png'), + type: 'fact', + time: -8373, + header: 'Interesting Fact', + text: 'Dolphins sleep with one eye open.', + comments: [], +}, { + id: 9, + photo: require('../img/photo47.png'), + type: 'fact', + time: -565, + header: 'Elephant', + text: 'Elephant is one of the few mammals that can\'t jump.', + comments: [], +}, { + id: 10, + photo: require('../img/photo48.png'), + type: 'fact', + time: -52365, + header: 'Cold Water', + text: 'Cold water weighs less than hot water.', + comments: [], +}, { + id: 11, + photo: require('../img/photo49.png'), + type: 'fact', + time: -1295, + header: 'Our Eyes', + text: 'You blink over 10,000,000 times a year.', + comments: [], +}, { + id: 12, + photo: require('../img/photo17.png'), + type: 'post', + time: -300, + title: 'My Little Kitten', + text: 'I have got a cat. Her name is Matilda. She is quite old for a cat. She is eleven years old. Matilda is very' + + ' fluffy. Her back is black and her belly and chest are white. She also has a black muzzle with long white whiskers. ' + + 'Her legs and paws are white. Matilda has big eyes. Her eyes are light green, but they become yellow in bright sunlight. I love my cat.', + comments: [], +}, { + id: 13, + photo: require('../img/photo18.png'), + type: 'post', + time: -1373, + header: 'Interesting Fact', + text: 'One chef prepared a delicious cake with apples and named it in honor of his beloved Charlotte.', + comments: [], +}, { + id: 14, + photo: require('../img/photo19.png'), + type: 'post', + time: -2446, + header: 'Music In Our Life', + text: 'The scientists say that they can define your character if they know what music you like.', + comments: [], +}, { + id: 15, + photo: require('../img/photo20.png'), + type: 'post', + time: -3519, + header: 'Exciting Adventure', + text: 'My trip to Spain last summer. I think that it was the most interesting trip in my life.', + comments: [], +}, +]; + +export default articles; diff --git a/frontend/app/data/raw/cards.js b/frontend/app/data/raw/cards.js new file mode 100644 index 0000000..7533e89 --- /dev/null +++ b/frontend/app/data/raw/cards.js @@ -0,0 +1,40 @@ +const cards = + [{ + id: 1, + name: 'Cindee Seton', + bank: 'CitiBank', + amount: 440, + date: '07/19', + cardNo: '3538********8699', + type: 'visa', + currency: 'usd', + }, { + id: 2, + name: 'Cindee Seton', + bank: 'CitiBank', + amount: 1740, + date: '07/19', + cardNo: '5602********1161', + type: 'mastercard', + currency: 'eur', + }, { + id: 3, + name: 'Cindee Seton', + bank: 'CitiBank', + amount: 2120, + date: '09/24', + cardNo: '3379********5427', + type: 'axp', + currency: 'usd', + }, { + id: 4, + name: 'Cindee Seton', + bank: 'CitiBank', + amount: 3150, + date: '02/21', + cardNo: '4911********8396', + type: 'visa', + currency: 'eur', + }]; + +export default cards; diff --git a/frontend/app/data/raw/conversations.js b/frontend/app/data/raw/conversations.js new file mode 100644 index 0000000..2e778e7 --- /dev/null +++ b/frontend/app/data/raw/conversations.js @@ -0,0 +1,85 @@ +export const Conversations = [ + { + withUserId: 1, + messages: [ + { + id: 0, + type: 'out', + time: -300, + text: 'Hey, how’ve you been?', + }, + { + id: 1, + time: -240, + type: 'in', + text: 'Yeah, not bad, actually I finally got a call back from that job that I interviewed for, and guess what? I got it!', + }, + { + id: 2, + time: -230, + type: 'out', + text: 'Awesome! Yeah, well done, that’s really great to hear. Do you start right away?', + }, + { + id: 3, + time: -100, + type: 'out', + text: 'Well, uhm yes and no, I go in for training tomorrow, but I don’t really start until next week. ' + + 'Do you have some time this weekend, maybe we could get together?', + }, + { + id: 4, + time: -45, + type: 'in', + text: 'I’ve got a lot planned this weekend, just running around, doing loads of stuff, but Friday’s pretty open.', + }, + { + id: 5, + time: -5, + type: 'out', + text: 'That works pretty well for me!', + }], + }, + { + withUserId: 5, + messages: [ + { + id: 0, + type: 'out', + time: -300, + text: 'I have no idea what to buy for Mary for her birthday.', + }, + { + id: 1, + time: -240, + type: 'in', + text: 'Me, neither! Would you like to go in and buy her a gift together?', + }, + { + id: 2, + time: -100, + type: 'out', + text: 'If I remember right, she likes music, skiing, and reading', + }, + { + id: 3, + time: -45, + type: 'out', + text: 'You know, maybe we could get her some concert tickets. Who would know her favorite groups?', + }, + { + id: 4, + time: -25, + type: 'in', + text: 'Her roommate, Malia, might know what her favorite groups are.', + }, + { + id: 5, + time: -5, + type: 'out', + text: 'Cool! Let\'s give Malia a call and ask her for her help right now', + }], + }, +]; + +export default Conversations; diff --git a/frontend/app/data/raw/notifications.js b/frontend/app/data/raw/notifications.js new file mode 100644 index 0000000..967bec0 --- /dev/null +++ b/frontend/app/data/raw/notifications.js @@ -0,0 +1,59 @@ +const notifications = [ + { + id: 1, + type: 'follow', + description: 'liked profile page for Dribbble App Design Concept', + time: -1, + }, { + id: 2, + type: 'like', + description: 'liked a photo on your album', + time: -271, + attach: require('../img/Image8.png'), + }, { + id: 3, + type: 'like', + description: 'liked a photo on your album', + time: -541, + attach: require('../img/Image8.png'), + }, { + id: 4, + type: 'like', + description: 'liked profile page for Dribbble App Design Concept', + time: -811, + }, { + id: 5, + type: 'like', + description: 'liked profile page for Dribbble App Design Concept', + time: -1081, + }, { + id: 6, + type: 'follow', + description: 'followed you UI/UX Designer and Interaction Designer', + time: -1351, + }, { + id: 7, + type: 'follow', + description: 'liked a photo on your album', + time: -1621, + attach: require('../img/Image8.png'), + }, { + id: 8, + type: 'follow', + description: 'followed you iOS Developer and Graphic Designer', + time: -1891, + }, { + id: 9, + type: 'follow', + description: 'liked profile page for Dribbble App Design Concept', + time: -2161, + }, { + id: 10, + type: 'like', + description: 'liked a photo on your album', + time: -2431, + attach: require('../img/Image8.png'), + }, +]; + +export default notifications; diff --git a/frontend/app/data/raw/users.js b/frontend/app/data/raw/users.js new file mode 100644 index 0000000..d7e651a --- /dev/null +++ b/frontend/app/data/raw/users.js @@ -0,0 +1,194 @@ +const images = [ + require('../img/Image10.png'), + require('../img/Image11.png'), + require('../img/Image2.png'), + require('../img/Image3.png'), + require('../img/Image4.png'), + require('../img/Image1.png'), + require('../img/Image12.png'), + require('../img/Image8.png'), + require('../img/Image6.png'), + require('../img/Image9.png'), + require('../img/Image5.png'), + require('../img/Image7.png'), +]; + +const users = [{ + id: 1, + firstName: 'Helen', + lastName: 'Gilbert', + phone: '+1 415 670 90 34', + country: 'Belarus', + email: 'h.gilbert@akveo.com', + password: '123456', + newPassword: '12345678', + confirmPassword: '12345678', + photo: require('../img/avatars/Image9.png'), + postCount: 86, + followersCount: 22102, + followingCount: 536, + images, + +}, +{ + id: 2, + firstName: 'Emilie', + lastName: 'McDiarmid', + email: 'emcdiarmid1@yale.edu', + country: 'China', + password: 'YyKgJ8A3b4b', + newPassword: 'DpCRPYW7Fgy', + confirmPassword: 'DpCRPYW7Fgy', + postCount: 95, + phone: '86-(261)670-4133', + followingCount: 975, + followersCount: 1703, + images, + photo: require('../img/avatars/Image1.png'), +}, +{ + id: 3, + firstName: 'Sandra', + lastName: 'Paver', + email: 'spaver2@ox.ac.uk', + country: 'Greece', + password: '0BCeHRlt84Zo', + newPassword: '61BaifSE20w', + confirmPassword: '61BaifSE20w', + postCount: 60, + phone: '30-(524)246-5851', + followingCount: 736, + followersCount: 1534, + images, + photo: require('../img/avatars/Image3.png'), +}, +{ + id: 4, + firstName: 'Nancy', + lastName: 'O\'Crevan', + email: 'nocrevan3@zimbio.com', + country: 'China', + password: 'W0NxvHo2C', + newPassword: 'vj4ueTKK', + confirmPassword: 'vj4ueTKK', + postCount: 78, + phone: '86-(499)721-5796', + followingCount: 86, + followersCount: 3303, + images, + photo: require('../img/avatars/Image4.png'), +}, +{ + id: 5, + firstName: 'Clayton', + lastName: 'O\'Mullaney', + email: 'cmullaney4@tripadvisor.com', + country: 'Philippines', + password: 'ZlzECwoN', + newPassword: 'N9l5KLpBW', + confirmPassword: 'N9l5KLpBW', + postCount: 37, + phone: '63-(210)188-9126', + followingCount: 745, + followersCount: 2703, + images, + photo: require('../img/avatars/Image5.png'), +}, +{ + id: 6, + firstName: 'Carlee', + lastName: 'Aubry', + email: 'caubry5@nytimes.com', + country: 'China', + password: 'jUIz9PNbU', + newPassword: 'nJRP3MdIh4U', + confirmPassword: 'nJRP3MdIh4U', + postCount: 89, + phone: '86-(939)186-9659', + followingCount: 444, + followersCount: 8432, + images, + photo: require('../img/avatars/Image6.png'), +}, +{ + id: 7, + firstName: 'Patrick', + lastName: 'Holden', + email: 'p.holden6@woothemes.com', + country: 'Indonesia', + password: 'inOEsoAlnh', + newPassword: '60z2bgL', + confirmPassword: '60z2bgL', + postCount: 48, + phone: '62-(373)613-7229', + followingCount: 731, + followersCount: 18230, + images, + photo: require('../img/avatars/Image7.png'), +}, +{ + id: 8, + firstName: 'Edward', + lastName: 'Storton', + email: 'estorton7@google.ca', + country: 'Nigeria', + password: 'e1H56GRP', + newPassword: 't2a1FbI8oCo', + confirmPassword: 't2a1FbI8oCo', + postCount: 100, + phone: '234-(135)610-8989', + followingCount: 667, + followersCount: 4234, + images, + photo: require('../img/avatars/Image8.png'), +}, +{ + id: 9, + firstName: 'Carole', + lastName: 'Blundon', + email: 'cblundon8@google.pl', + country: 'United States', + password: 't9xI6skPz', + newPassword: 'y84Jquaxg8', + confirmPassword: 'y84Jquaxg8', + postCount: 74, + phone: '1-(913)904-8423', + followingCount: 750, + followersCount: 1032, + images, + photo: require('../img/avatars/Image10.png'), +}, +{ + id: 10, + firstName: 'Bryce', + lastName: 'Curle', + email: 'bcurled@paginegialle.it', + country: 'Indonesia', + password: 'ACCsjlPq', + newPassword: 'm05jBM1S88', + confirmPassword: 'm05jBM1S88', + postCount: 54, + phone: '62-(688)911-5487', + followingCount: 343, + followersCount: 3721, + images, + photo: require('../img/avatars/Image11.png'), +}, +{ + id: 11, + firstName: 'Babara', + lastName: 'Greasty', + email: 'bgreastya@netlog.com', + country: 'Russia', + password: '0SuOdS8XQK', + newPassword: 'f49mZd49eGHm', + confirmPassword: 'f49mZd49eGHm', + postCount: 58, + phone: '7-(121)282-0448', + followingCount: 165, + followersCount: 5433, + images, + photo: require('../img/avatars/Image12.png'), +}]; + +export default users; diff --git a/frontend/app/screens/articles/article.js b/frontend/app/screens/articles/article.js new file mode 100644 index 0000000..059ab44 --- /dev/null +++ b/frontend/app/screens/articles/article.js @@ -0,0 +1,94 @@ +import React from 'react'; +import axios from 'axios'; + + +import { + ScrollView, + Image, + View, + TouchableOpacity, +} from 'react-native'; +import { + RkCard, + RkText, + RkStyleSheet, +} from 'react-native-ui-kitten'; +import { + Avatar, + SocialBar, +} from '../../components'; +import NavigationType from '../../config/navigation/propTypes'; + + +export class Article extends React.Component { + state = { + article: {}, + mounted: false, + }; + + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Current problem'.toUpperCase(), + }; + + + componentWillMount() { + const articleId = this.props.navigation.getParam('id', 1); + console.log(articleId); + + axios.get(`http://192.168.1.43:8000/api/articles/${articleId}`) + .then(res => { + this.setState({ + article: res.data, + mounted: true, + }); + console.log(this.state.article); + }); + } + + render() { + if (this.state.mounted) { + return ( + <ScrollView style={styles.root}> + <RkCard rkType='article'> + <Image + rkCardImg + source={{ + uri: `${this.state.article.image.toString() + .replace('http://127.0.0.1:8000/', '')}`, + }} + /> + <View rkCardHeader> + <View> + <RkText style={styles.title} rkType='header4'>{this.state.article.title}</RkText> + </View> + + + </View> + <View rkCardContent> + <View> + <RkText rkType='primary3 bigLine'>{this.state.article.text}</RkText> + </View> + </View> + <View rkCardFooter> + <SocialBar comments={this.state.article.n_comments} is_solved={this.state.article.is_solved ? 'Solved' : "Doesn't solved"}/> + </View> + </RkCard> + </ScrollView> + + ); + } + return null; + } +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.base, + }, + title: { + marginBottom: 5, + }, +})); diff --git a/frontend/app/screens/articles/articles1.js b/frontend/app/screens/articles/articles1.js new file mode 100644 index 0000000..47049b4 --- /dev/null +++ b/frontend/app/screens/articles/articles1.js @@ -0,0 +1,74 @@ +import React from 'react'; +import { + FlatList, + Image, + View, + TouchableOpacity, +} from 'react-native'; +import { + RkText, + RkCard, RkStyleSheet, +} from 'react-native-ui-kitten'; +import { SocialBar } from '../../components'; +import { data } from '../../data'; +import NavigationType from '../../config/navigation/propTypes'; + +const moment = require('moment'); + +export class Articles1 extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Article List'.toUpperCase(), + }; + + state = { + data: data.getArticles(), + }; + + extractItemKey = (item) => `${item.id}`; + + onItemPressed = ({ item }) => { + this.props.navigation.navigate('Article', { id: item.id }); + }; + + renderItem = ({ item }) => ( + <TouchableOpacity + delayPressIn={70} + activeOpacity={0.8} + onPress={() => this.onItemPressed(item)}> + <RkCard rkType='backImg'> + <Image rkCardImg source={item.photo} /> + <View rkCardImgOverlay rkCardContent style={styles.overlay}> + <RkText rkType='header2 inverseColor'>{item.header}</RkText> + <RkText rkType='secondary2 inverseColor'>{moment().add(item.time, 'seconds').fromNow()}</RkText> + <View rkCardFooter style={styles.footer}> + <SocialBar rkType='leftAligned' /> + </View > + </View> + </RkCard> + </TouchableOpacity> + ); + + render = () => ( + <FlatList + data={this.state.data} + renderItem={this.renderItem} + keyExtractor={this.extractItemKey} + style={styles.root} + /> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.base, + }, + overlay: { + justifyContent: 'flex-end', + }, + footer: { + width: 240, + }, +})); diff --git a/frontend/app/screens/articles/articles2.js b/frontend/app/screens/articles/articles2.js new file mode 100644 index 0000000..0d48812 --- /dev/null +++ b/frontend/app/screens/articles/articles2.js @@ -0,0 +1,84 @@ +import React from 'react'; +import { + FlatList, + Image, + View, + TouchableOpacity, +} from 'react-native'; +import { + RkText, + RkCard, RkStyleSheet, +} from 'react-native-ui-kitten'; +import axios from 'axios'; +import { SocialBar } from '../../components'; +import NavigationType from '../../config/navigation/propTypes'; + + +export class Articles2 extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Problems List'.toUpperCase(), + }; + + state = { + articles: [], + }; + + componentDidMount() { + axios.get('http://192.168.1.43:8000/api/articles') + .then(res => { + this.setState({ + articles: res.data, + }); + + }); + } + + extractItemKey = (item) => `${item.id}`; + + onItemPressed = (item) => { + this.props.navigation.navigate('Article', { id: item.id }); + }; + + renderItem = ({ item }) => ( + <TouchableOpacity + delayPressIn={70} + activeOpacity={0.8} + onPress={() => this.onItemPressed(item)}> + <RkCard rkType='imgBlock' style={styles.card}> + <Image rkCardImg source={{ uri: `${item.image.toString().replace('http://127.0.0.1:8000/', '')}` }} /> + <View rkCardImgOverlay rkCardContent style={styles.overlay}> + <RkText rkType='header4 inverseColor'>{item.title}</RkText> + </View> + <View rkCardFooter> + <SocialBar rkType='space' showLabel likes={item.rating} comments={item.n_comments} is_solved={item.is_solved ? 'Solved' : "Doesn't solved"} /> + </View> + </RkCard> + </TouchableOpacity> + ); + + render = () => ( + <FlatList + data={this.state.articles} + renderItem={this.renderItem} + keyExtractor={this.extractItemKey} + style={styles.container} + /> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + container: { + backgroundColor: theme.colors.screen.scroll, + paddingVertical: 8, + paddingHorizontal: 14, + }, + card: { + marginVertical: 8, + }, + time: { + marginTop: 5, + }, +})); diff --git a/frontend/app/screens/articles/articles3.js b/frontend/app/screens/articles/articles3.js new file mode 100644 index 0000000..e5eda4e --- /dev/null +++ b/frontend/app/screens/articles/articles3.js @@ -0,0 +1,81 @@ +import React from 'react'; +import { + FlatList, + Image, + View, + TouchableOpacity, +} from 'react-native'; +import { + RkText, + RkCard, RkStyleSheet, +} from 'react-native-ui-kitten'; +import { SocialBar } from '../../components'; +import { data } from '../../data'; +import NavigationType from '../../config/navigation/propTypes'; + +const moment = require('moment'); + +export class Articles3 extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Article List'.toUpperCase(), + }; + + state = { + data: data.getArticles(), + }; + + extractItemKey = (item) => `${item.id}`; + + onItemPressed = ({ item }) => { + this.props.navigation.navigate('Article', { id: item.id }); + }; + + renderItem = ({ item }) => ( + <TouchableOpacity + delayPressIn={70} + activeOpacity={0.8} + onPress={() => this.onItemPressed(item)}> + <RkCard style={styles.card}> + <View rkCardHeader> + <View> + <RkText rkType='header4'>{item.header}</RkText> + <RkText rkType='secondary2 hintColor'>{moment().add(item.time, 'seconds').fromNow()}</RkText> + </View> + </View> + <Image rkCardImg source={item.photo} /> + <View style={styles.footer} rkCardFooter> + <SocialBar /> + </View > + </RkCard> + </TouchableOpacity> + ); + + render = () => ( + <FlatList + data={this.state.data} + renderItem={this.renderItem} + keyExtractor={this.extractItemKey} + style={styles.container} + /> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + container: { + backgroundColor: theme.colors.screen.scroll, + paddingHorizontal: 14, + paddingVertical: 8, + }, + card: { + marginVertical: 8, + }, + footer: { + paddingTop: 16, + }, + time: { + marginTop: 5, + }, +})); diff --git a/frontend/app/screens/articles/articles4.js b/frontend/app/screens/articles/articles4.js new file mode 100644 index 0000000..1f08ba1 --- /dev/null +++ b/frontend/app/screens/articles/articles4.js @@ -0,0 +1,78 @@ +import React from 'react'; +import { + FlatList, + Image, + View, + TouchableOpacity, +} from 'react-native'; +import { + RkText, + RkCard, + RkStyleSheet, +} from 'react-native-ui-kitten'; +import { SocialBar } from '../../components'; +import { data } from '../../data'; +import NavigationType from '../../config/navigation/propTypes'; + + +export class Articles4 extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Article List'.toUpperCase(), + }; + + state = { + data: data.getArticles(), + }; + + extractItemKey = (item) => `${item.id}`; + + renderItem = ({ item }) => ( + <TouchableOpacity + delayPressIn={70} + activeOpacity={0.8} + onPress={() => this.props.navigation.navigate('Article', { id: item.id })}> + <RkCard rkType='horizontal' style={styles.card}> + <Image rkCardImg source={item.photo} /> + <View rkCardContent> + <RkText numberOfLines={1} rkType='header6'>{item.header}</RkText> + <RkText rkType='secondary6 hintColor'> + {`${item.user.firstName} ${item.user.lastName}`} + </RkText> + <RkText style={styles.post} numberOfLines={2} rkType='secondary1'>{item.text}</RkText> + </View> + <View rkCardFooter> + <SocialBar rkType='space' showLabel /> + </View > + </RkCard> + </TouchableOpacity> + ); + + render = () => ( + <View> + <FlatList + data={this.state.data} + renderItem={this.renderItem} + keyExtractor={this.extractItemKey} + style={styles.container} + /> + </View> + ); +} + + +const styles = RkStyleSheet.create(theme => ({ + container: { + backgroundColor: theme.colors.screen.scroll, + paddingVertical: 8, + paddingHorizontal: 14, + }, + card: { + marginVertical: 8, + }, + post: { + marginTop: 13, + }, +})); diff --git a/frontend/app/screens/articles/blogposts.js b/frontend/app/screens/articles/blogposts.js new file mode 100644 index 0000000..3ce4565 --- /dev/null +++ b/frontend/app/screens/articles/blogposts.js @@ -0,0 +1,88 @@ +import React from 'react'; +import { + FlatList, + View, + Image, + TouchableOpacity, +} from 'react-native'; +import { + RkCard, RkStyleSheet, + RkText, +} from 'react-native-ui-kitten'; +import { Avatar } from '../../components'; +import { data } from '../../data'; +import NavigationType from '../../config/navigation/propTypes'; + +const moment = require('moment'); + +export class Blogposts extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Blogposts'.toUpperCase(), + }; + + state = { + data: data.getArticles('post'), + }; + + extractItemKey = (item) => `${item.id}`; + + onItemPressed = (item) => { + this.props.navigation.navigate('Article', { id: item.id }); + }; + + renderItem = ({ item }) => ( + <TouchableOpacity + delayPressIn={70} + activeOpacity={0.8} + onPress={() => this.onItemPressed(item)}> + <RkCard rkType='blog' style={styles.card}> + <Image rkCardImg source={item.photo} /> + <View rkCardHeader style={styles.content}> + <RkText style={styles.section} rkType='header4'>{item.title}</RkText> + </View> + <View rkCardContent> + <View> + <RkText rkType='primary3 mediumLine' numberOfLines={2}>{item.text}</RkText> + </View> + </View> + <View rkCardFooter> + <View style={styles.userInfo}> + <Avatar style={styles.avatar} rkType='circle small' img={item.user.photo} /> + <RkText rkType='header6'>{`${item.user.firstName} ${item.user.lastName}`}</RkText> + </View> + <RkText rkType='secondary2 hintColor'>{moment().add(item.time, 'seconds').fromNow()}</RkText> + </View> + </RkCard> + </TouchableOpacity> + ); + + render = () => ( + <FlatList + data={this.state.data} + renderItem={this.renderItem} + keyExtractor={this.extractItemKey} + style={styles.container} + /> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + container: { + backgroundColor: theme.colors.screen.scroll, + paddingVertical: 8, + paddingHorizontal: 14, + }, + card: { + marginVertical: 8, + }, + userInfo: { + flexDirection: 'row', + alignItems: 'center', + }, + avatar: { + marginRight: 17, + }, +})); diff --git a/frontend/app/screens/articles/index.js b/frontend/app/screens/articles/index.js new file mode 100644 index 0000000..115d2ac --- /dev/null +++ b/frontend/app/screens/articles/index.js @@ -0,0 +1,6 @@ +export * from './articles1'; +export * from './articles2'; +export * from './articles3'; +export * from './articles4'; +export * from './blogposts'; +export * from './article'; diff --git a/frontend/app/screens/dashboard/dashboard.js b/frontend/app/screens/dashboard/dashboard.js new file mode 100644 index 0000000..06c8d6a --- /dev/null +++ b/frontend/app/screens/dashboard/dashboard.js @@ -0,0 +1,117 @@ +import React from 'react'; +import { + View, + ScrollView, +} from 'react-native'; +import { + RkText, + RkStyleSheet, + RkTheme, +} from 'react-native-ui-kitten'; +import { FontAwesome } from '../../assets/icons'; +import { + ProgressChart, + DoughnutChart, + AreaChart, + AreaSmoothedChart, +} from '../../components/'; + +export class Dashboard extends React.Component { + static navigationOptions = { + title: 'Dashboard'.toUpperCase(), + }; + + state = { + data: { + statItems: [ + { + name: 'Stars', + value: '4,512', + icon: 'github', + background: RkTheme.current.colors.dashboard.stars, + }, + { + name: 'Tweets', + value: '2,256', + icon: 'twitter', + background: RkTheme.current.colors.dashboard.tweets, + }, + { + name: 'Likes', + value: '1,124', + icon: 'facebook', + background: RkTheme.current.colors.dashboard.likes, + }, + ], + }, + }; + + renderStatItem = (item) => ( + <View style={[styles.statItemContainer, { backgroundColor: item.background }]} key={item.name}> + <View> + <RkText rkType='header6' style={styles.statItemValue}>{item.value}</RkText> + <RkText rkType='secondary7' style={styles.statItemName}>{item.name}</RkText> + </View> + <RkText rkType='awesome hero' style={styles.statItemIcon}>{FontAwesome[item.icon]}</RkText> + </View> + ); + + render = () => { + const chartBackgroundStyle = { backgroundColor: RkTheme.current.colors.control.background }; + return ( + <ScrollView style={styles.screen}> + <View style={styles.statItems}> + {this.state.data.statItems.map(this.renderStatItem)} + </View> + <View style={[styles.chartBlock, chartBackgroundStyle]}> + <DoughnutChart /> + </View> + <View style={[styles.chartBlock, chartBackgroundStyle]}> + <AreaChart /> + </View> + <View style={[styles.chartBlock, chartBackgroundStyle]}> + <ProgressChart /> + </View> + <View style={[styles.chartBlock, chartBackgroundStyle]}> + <AreaSmoothedChart /> + </View> + </ScrollView> + ); + }; +} + +const styles = RkStyleSheet.create(theme => ({ + screen: { + backgroundColor: theme.colors.screen.scroll, + paddingHorizontal: 15, + }, + statItems: { + flexDirection: 'row', + justifyContent: 'space-between', + marginVertical: 15, + }, + statItemContainer: { + flexDirection: 'row', + justifyContent: 'space-between', + borderRadius: 3, + paddingHorizontal: 10, + paddingVertical: 10, + }, + statItemIcon: { + alignSelf: 'center', + marginLeft: 10, + color: 'white', + }, + statItemValue: { + color: 'white', + }, + statItemName: { + color: 'white', + }, + chartBlock: { + padding: 15, + marginBottom: 15, + justifyContent: 'center', + }, +})); + diff --git a/frontend/app/screens/dashboard/index.js b/frontend/app/screens/dashboard/index.js new file mode 100644 index 0000000..b58b6c9 --- /dev/null +++ b/frontend/app/screens/dashboard/index.js @@ -0,0 +1 @@ +export * from './dashboard'; diff --git a/frontend/app/screens/eCommerce/addToCardForm.js b/frontend/app/screens/eCommerce/addToCardForm.js new file mode 100644 index 0000000..5d41885 --- /dev/null +++ b/frontend/app/screens/eCommerce/addToCardForm.js @@ -0,0 +1,138 @@ +import React from 'react'; +import axios from 'axios'; +import { + View, + Keyboard, +} from 'react-native'; +import { + RkText, + RkTextInput, + RkStyleSheet, + RkAvoidKeyboard, +} from 'react-native-ui-kitten'; +import { GradientButton } from '../../components/'; +import { PasswordTextInput } from '../../components/passwordTextInput'; + +import { scale } from '../../utils/scale'; +import NavigationType from '../../config/navigation/propTypes'; + +export class AddToCardForm extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Add Card'.toUpperCase(), + }; + + state = { + name: '', + email: '', + problem: '', + + }; + + + onAddButtonPressed = () => { + axios.post('http://192.168.1.43:8000/api/articles/', { + title: this.state.problem.split(' ').slice(0, 5), + text: this.state.problem, + + }) + this.props.navigation.goBack(); + }; + + render = () => ( + <RkAvoidKeyboard + style={styles.screen} + onStartShouldSetResponder={() => true} + onResponderRelease={() => Keyboard.dismiss()}> + <View style={[styles.formContent]}> + <View> + + + <View style={[styles.content]}> + <View style={[styles.textRow]}> + <RkText rkType='subtitle'>Your name</RkText> + </View> + <RkTextInput + rkType='rounded' + onChangeText={(name) => this.setState({ name })} + value={this.state.name} + /> + </View> + + <View style={[styles.content]}> + <View style={[styles.textRow]}> + <RkText rkType='subtitle'>Your e-mail</RkText> + </View> + <RkTextInput + rkType='rounded' + onChangeText={(email) => this.setState({ email })} + value={this.state.email} + /> + </View> + + <View style={[styles.content]}> + <View style={[styles.textRow]}> + <RkText rkType='subtitle'>Describe eco-problem</RkText> + </View> + <RkTextInput + rkType='rounded' + onChangeText={(problem) => this.setState({ problem })} + value={this.state.problem} + /> + </View> + + + </View> + <View> + <GradientButton + rkType='large' + text='ADD TO CARD' + onPress={this.onAddButtonPressed} + /> + </View> + </View> + </RkAvoidKeyboard> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + screen: { + padding: 15, + flex: 1, + backgroundColor: theme.colors.screen.base, + }, + content: { + marginTop: 10, + }, + formContent: { + justifyContent: 'space-between', + flexDirection: 'column', + flex: 1, + }, + textRow: { + marginLeft: 20, + }, + expireDateBlock: { + justifyContent: 'space-between', + flexDirection: 'row', + }, + expireDateInput: { + flex: 0.48, + marginVertical: 10, + }, + expireDateInnerInput: { + textAlign: 'center', + }, + expireDateDelimiter: { + flex: 0.04, + }, + balloon: { + maxWidth: scale(250), + padding: 15, + borderRadius: 100, + borderWidth: 0.5, + borderColor: theme.colors.border.solid, + }, +})); diff --git a/frontend/app/screens/eCommerce/cards.js b/frontend/app/screens/eCommerce/cards.js new file mode 100644 index 0000000..c22c30d --- /dev/null +++ b/frontend/app/screens/eCommerce/cards.js @@ -0,0 +1,242 @@ +import React from 'react'; +import { + FlatList, + View, + Image, + TouchableOpacity, + Modal, +} from 'react-native'; +import { + RkText, + RkCard, + RkButton, + RkStyleSheet, + RkTheme, +} from 'react-native-ui-kitten'; +import { LinearGradient } from 'expo'; +import { data } from '../../data'; +import { PasswordTextInput } from '../../components/passwordTextInput'; +import { UIConstants } from '../../config/appConstants'; +import { scaleVertical } from '../../utils/scale'; + +export class Cards extends React.Component { + static navigationOptions = { + title: 'Cards'.toUpperCase(), + }; + + state = { + data: data.getCards(), + modalVisible: false, + }; + + getCardStyle = (type) => { + switch (type) { + case 'visa': + return { + gradient: RkTheme.current.colors.gradients.visa, + icon: require('../../assets/icons/visaIcon.png'), + }; + case 'mastercard': + return { + gradient: RkTheme.current.colors.gradients.mastercard, + icon: require('../../assets/icons/masterCardIcon.png'), + }; + case 'axp': + return { + gradient: RkTheme.current.colors.gradients.axp, + icon: require('../../assets/icons/americanExpressIcon.png'), + }; + default: return {}; + } + }; + + formatCurrency = (amount, currency) => { + switch (currency) { + case 'usd': + return `$${amount}`; + case 'eur': + return `€${amount}`; + default: return ''; + } + }; + + prepareCardNo = (cardNo) => { + const re = /\*+/; + const parts = cardNo.split(re); + return { + firstPart: parts[0], + lastPart: parts[1], + }; + }; + + renderFooter = () => ( + <View style={styles.footer}> + <RkButton style={styles.button} rkType='circle highlight'> + <Image source={require('../../assets/icons/iconPlus.png')} /> + </RkButton> + </View> + ); + + setModalVisible = (visible) => { + this.setState({ modalVisible: visible }); + }; + + onItemPressed = () => { + this.setModalVisible(true); + }; + + extractItemKey = (item) => `${item.id}`; + + renderItem = ({ item }) => { + const { gradient, icon } = this.getCardStyle(item.type); + const { firstPart, lastPart } = this.prepareCardNo(item.cardNo); + + return ( + <RkCard rkType='credit' style={styles.card}> + <TouchableOpacity + delayPressIn={70} + activeOpacity={0.8} + onPress={this.onItemPressed}> + <LinearGradient + colors={gradient} + start={{ x: 0.0, y: 0.5 }} + end={{ x: 1, y: 0.5 }} + style={styles.background}> + <View rkCardHeader> + <RkText rkType='header4 inverseColor'>{item.bank}</RkText> + <Image source={icon} /> + </View> + <View rkCardContent> + <View style={styles.cardNoContainer}> + <RkText style={styles.cardNo} rkType='header2 inverseColor'>{firstPart}</RkText> + <RkText style={[styles.cardNo, styles.cardPlaceholder]} rkType='header2 inverseColor'>* * * *</RkText> + <RkText style={[styles.cardNo, styles.cardPlaceholder]} rkType='header2 inverseColor'>* * * *</RkText> + <RkText style={styles.cardNo} rkType='header2 inverseColor'>{lastPart}</RkText> + </View> + <RkText style={styles.date} rkType='header6 inverseColor'>{item.date}</RkText> + </View> + <View rkCardFooter> + <View> + <RkText rkType='header4 inverseColor'>{item.currency.toUpperCase()}</RkText> + <RkText rkType='header6 inverseColor'>{item.name.toUpperCase()}</RkText> + </View> + <RkText + rkType='header2 inverseColor'>{this.formatCurrency(item.amount, item.currency)} + </RkText> + </View> + </LinearGradient> + </TouchableOpacity> + </RkCard> + ); + }; + + render = () => ( + <View style={styles.root}> + <FlatList + style={styles.list} + showsVerticalScrollIndicator={false} + ListFooterComponent={this.renderFooter} + keyExtractor={this.extractItemKey} + data={this.state.data} + renderItem={this.renderItem} + /> + <Modal + animationType="fade" + transparent + onRequestClose={() => this.setModalVisible(false)} + visible={this.state.modalVisible}> + <View style={styles.popupOverlay}> + <View style={styles.popup}> + <View style={styles.popupContent}> + <RkText style={styles.popupHeader} rkType='header4'>Enter security code</RkText> + <PasswordTextInput /> + </View> + <View style={styles.popupButtons}> + <RkButton + onPress={() => this.setModalVisible(false)} + style={styles.popupButton} + rkType='clear'> + <RkText rkType='light'>CANCEL</RkText> + </RkButton> + <View style={styles.separator} /> + <RkButton + onPress={() => this.setModalVisible(false)} + style={styles.popupButton} + rkType='clear'> + <RkText>OK</RkText> + </RkButton> + </View> + </View> + </View> + </Modal> + </View> + ) +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.base, + }, + list: { + marginHorizontal: 16, + }, + card: { + marginVertical: 8, + }, + background: { + borderRadius: 7, + }, + cardNoContainer: { + flexDirection: 'row', + }, + cardNo: { + marginHorizontal: 8, + }, + cardPlaceholder: { + paddingTop: 4, + }, + date: { + marginTop: scaleVertical(20), + }, + footer: { + marginTop: 8, + marginBottom: scaleVertical(16), + alignItems: 'center', + }, + button: { + height: 56, + width: 56, + }, + popup: { + backgroundColor: theme.colors.screen.base, + marginTop: scaleVertical(70), + marginHorizontal: 37, + borderRadius: 7, + }, + popupOverlay: { + backgroundColor: theme.colors.screen.overlay, + flex: 1, + marginTop: UIConstants.HeaderHeight, + }, + popupContent: { + alignItems: 'center', + margin: 16, + }, + popupHeader: { + marginBottom: scaleVertical(45), + }, + popupButtons: { + marginTop: 15, + flexDirection: 'row', + borderTopWidth: 1, + borderColor: theme.colors.border.base, + }, + popupButton: { + flex: 1, + marginVertical: 16, + }, + separator: { + backgroundColor: theme.colors.border.base, + width: 1, + }, +})); diff --git a/frontend/app/screens/eCommerce/index.js b/frontend/app/screens/eCommerce/index.js new file mode 100644 index 0000000..7658d81 --- /dev/null +++ b/frontend/app/screens/eCommerce/index.js @@ -0,0 +1,2 @@ +export * from './cards'; +export * from './addToCardForm'; diff --git a/frontend/app/screens/index.js b/frontend/app/screens/index.js new file mode 100644 index 0000000..b7ce1cb --- /dev/null +++ b/frontend/app/screens/index.js @@ -0,0 +1,11 @@ +export * from './navigation'; +export * from './menu'; +export * from './other'; +export * from './dashboard'; +export * from './social'; +export * from './articles'; +export * from './messaging'; +export * from './login'; +export * from './walkthroughs'; +export * from './eCommerce'; +export * from './theme'; diff --git a/frontend/app/screens/login/index.js b/frontend/app/screens/login/index.js new file mode 100644 index 0000000..b0b1cd1 --- /dev/null +++ b/frontend/app/screens/login/index.js @@ -0,0 +1,4 @@ +export * from './login1'; +export * from './login2'; +export * from './signUp'; +export * from './passwordRecovery'; diff --git a/frontend/app/screens/login/login1.js b/frontend/app/screens/login/login1.js new file mode 100644 index 0000000..73e97d5 --- /dev/null +++ b/frontend/app/screens/login/login1.js @@ -0,0 +1,129 @@ +import React from 'react'; +import { + View, + Image, + Dimensions, + Keyboard, +} from 'react-native'; +import { + RkButton, + RkText, + RkTextInput, + RkAvoidKeyboard, + RkStyleSheet, + RkTheme, +} from 'react-native-ui-kitten'; +import { FontAwesome } from '../../assets/icons'; +import { GradientButton } from '../../components/gradientButton'; +import { scaleModerate, scaleVertical } from '../../utils/scale'; +import NavigationType from '../../config/navigation/propTypes'; + +export class LoginV1 extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + header: null, + }; + + getThemeImageSource = (theme) => ( + theme.name === 'light' ? + require('../../assets/images/backgroundLoginV1.png') : require('../../assets/images/backgroundLoginV1DarkTheme.png') + ); + + renderImage = () => { + const screenSize = Dimensions.get('window'); + const imageSize = { + width: screenSize.width, + height: screenSize.height - scaleModerate(375, 1), + }; + return ( + <Image + style={[styles.image, imageSize]} + source={this.getThemeImageSource(RkTheme.current)} + /> + ); + }; + + onLoginButtonPressed = () => { + this.props.navigation.goBack(); + }; + + onSignUpButtonPressed = () => { + this.props.navigation.navigate('SignUp'); + }; + + render = () => ( + <RkAvoidKeyboard + onStartShouldSetResponder={() => true} + onResponderRelease={() => Keyboard.dismiss()} + style={styles.screen}> + {this.renderImage()} + <View style={styles.container}> + <View style={styles.buttons}> + <RkButton style={styles.button} rkType='social'> + <RkText rkType='awesome hero accentColor'>{FontAwesome.twitter}</RkText> + </RkButton> + <RkButton style={styles.button} rkType='social'> + <RkText rkType='awesome hero accentColor'>{FontAwesome.google}</RkText> + </RkButton> + <RkButton style={styles.button} rkType='social'> + <RkText rkType='awesome hero accentColor'>{FontAwesome.facebook}</RkText> + </RkButton> + </View> + <RkTextInput rkType='rounded' placeholder='Username' /> + <RkTextInput rkType='rounded' placeholder='Password' secureTextEntry /> + <GradientButton + style={styles.save} + rkType='large' + onPress={this.onLoginButtonPressed} + text='LOGIN' + /> + <View style={styles.footer}> + <View style={styles.textRow}> + <RkText rkType='primary3'>Don’t have an account?</RkText> + <RkButton rkType='clear'> + <RkText rkType='header6' onPress={this.onSignUpButtonPressed}>Sign up now</RkText> + </RkButton> + </View> + </View> + </View> + </RkAvoidKeyboard> + ) +} + +const styles = RkStyleSheet.create(theme => ({ + screen: { + flex: 1, + alignItems: 'center', + backgroundColor: theme.colors.screen.base, + }, + image: { + resizeMode: 'cover', + marginBottom: scaleVertical(10), + }, + container: { + paddingHorizontal: 17, + paddingBottom: scaleVertical(22), + alignItems: 'center', + flex: -1, + }, + footer: { + justifyContent: 'flex-end', + flex: 1, + }, + buttons: { + flexDirection: 'row', + marginBottom: scaleVertical(24), + }, + button: { + marginHorizontal: 14, + }, + save: { + marginVertical: 9, + }, + textRow: { + justifyContent: 'center', + flexDirection: 'row', + }, +})); diff --git a/frontend/app/screens/login/login2.js b/frontend/app/screens/login/login2.js new file mode 100644 index 0000000..c211190 --- /dev/null +++ b/frontend/app/screens/login/login2.js @@ -0,0 +1,127 @@ +import React from 'react'; +import { + View, + Image, + Keyboard, +} from 'react-native'; +import { + RkButton, + RkText, + RkTextInput, + RkAvoidKeyboard, + RkTheme, + RkStyleSheet, +} from 'react-native-ui-kitten'; +import { FontAwesome } from '../../assets/icons'; +import { GradientButton } from '../../components/gradientButton'; +import { scaleVertical } from '../../utils/scale'; +import NavigationType from '../../config/navigation/propTypes'; + +export class LoginV2 extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + header: null, + }; + + onLoginButtonPressed = () => { + this.props.navigation.goBack(); + }; + + onSignUpButtonPressed = () => { + this.props.navigation.navigate('SignUp'); + }; + + getThemeImageSource = (theme) => ( + theme.name === 'light' ? + require('../../assets/images/logo.png') : require('../../assets/images/logoDark.png') + ); + + renderImage = () => ( + <Image style={styles.image} source={this.getThemeImageSource(RkTheme.current)} /> + ); + + render = () => ( + <RkAvoidKeyboard + style={styles.screen} + onStartShouldSetResponder={() => true} + onResponderRelease={() => Keyboard.dismiss()}> + <View style={styles.header}> + {this.renderImage()} + <RkText rkType='light h1'>React Native</RkText> + <RkText rkType='logo h0'>UI Kitten</RkText> + </View> + <View style={styles.content}> + <View> + <RkTextInput rkType='rounded' placeholder='Username' /> + <RkTextInput rkType='rounded' placeholder='Password' secureTextEntry /> + <GradientButton + style={styles.save} + rkType='large' + text='LOGIN' + onPress={this.onLoginButtonPressed} + /> + </View> + <View style={styles.buttons}> + <RkButton style={styles.button} rkType='social'> + <RkText rkType='awesome hero'>{FontAwesome.twitter}</RkText> + </RkButton> + <RkButton style={styles.button} rkType='social'> + <RkText rkType='awesome hero'>{FontAwesome.google}</RkText> + </RkButton> + <RkButton style={styles.button} rkType='social'> + <RkText rkType='awesome hero'>{FontAwesome.facebook}</RkText> + </RkButton> + </View> + <View style={styles.footer}> + <View style={styles.textRow}> + <RkText rkType='primary3'>Don’t have an account?</RkText> + <RkButton rkType='clear' onPress={this.onSignUpButtonPressed}> + <RkText rkType='header6'>Sign up now</RkText> + </RkButton> + </View> + </View> + </View> + </RkAvoidKeyboard> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + screen: { + padding: scaleVertical(16), + flex: 1, + justifyContent: 'space-between', + backgroundColor: theme.colors.screen.base, + }, + image: { + height: scaleVertical(77), + resizeMode: 'contain', + }, + header: { + paddingBottom: scaleVertical(10), + alignItems: 'center', + justifyContent: 'center', + flex: 1, + }, + content: { + justifyContent: 'space-between', + }, + save: { + marginVertical: 20, + }, + buttons: { + flexDirection: 'row', + marginBottom: scaleVertical(24), + marginHorizontal: 24, + justifyContent: 'space-around', + }, + textRow: { + flexDirection: 'row', + justifyContent: 'center', + }, + button: { + borderColor: theme.colors.border.solid, + }, + footer: {}, +})); diff --git a/frontend/app/screens/login/passwordRecovery.js b/frontend/app/screens/login/passwordRecovery.js new file mode 100644 index 0000000..b383e4c --- /dev/null +++ b/frontend/app/screens/login/passwordRecovery.js @@ -0,0 +1,83 @@ +import React from 'react'; +import { + View, + Image, + Keyboard, +} from 'react-native'; +import { + RkStyleSheet, + RkText, + RkTextInput, + RkTheme, +} from 'react-native-ui-kitten'; +import { GradientButton } from '../../components/'; +import { scaleVertical } from '../../utils/scale'; +import NavigationType from '../../config/navigation/propTypes'; + +export class PasswordRecovery extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + header: null, + }; + + onSendButtonPressed = () => { + this.props.navigation.goBack(); + }; + + getThemeImageSource = (theme) => ( + theme.name === 'light' ? + require('../../assets/images/logo.png') : require('../../assets/images/logoDark.png') + ); + + renderImage = () => ( + <Image style={styles.image} source={this.getThemeImageSource(RkTheme.current)} /> + ); + + render = () => ( + <View + behavior='position' + style={styles.screen} + onStartShouldSetResponder={() => true} + onResponderRelease={() => Keyboard.dismiss()}> + <View style={styles.header}> + {this.renderImage()} + <RkText rkType='h1'>Password Recovery</RkText> + </View> + <View style={styles.content}> + <RkTextInput rkType='rounded' placeholder='Email' /> + <RkText rkType='secondary5 secondaryColor center'> + Enter your email below to receive your password reset instructions + </RkText> + </View> + <GradientButton + style={styles.save} + rkType='large' + text='SEND' + onPress={this.onSendButtonPressed} + /> + </View> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + screen: { + flex: 1, + paddingHorizontal: 16, + paddingVertical: scaleVertical(24), + justifyContent: 'space-between', + backgroundColor: theme.colors.screen.base, + }, + header: { + alignItems: 'center', + }, + image: { + marginVertical: scaleVertical(27), + height: scaleVertical(77), + resizeMode: 'contain', + }, + content: { + alignItems: 'center', + }, +})); diff --git a/frontend/app/screens/login/signUp.js b/frontend/app/screens/login/signUp.js new file mode 100644 index 0000000..5c7965e --- /dev/null +++ b/frontend/app/screens/login/signUp.js @@ -0,0 +1,110 @@ +import React from 'react'; +import { + View, + Image, + Keyboard, +} from 'react-native'; +import { + RkButton, + RkText, + RkTextInput, + RkStyleSheet, + RkTheme, + RkAvoidKeyboard, +} from 'react-native-ui-kitten'; +import { GradientButton } from '../../components/'; +import { scaleVertical } from '../../utils/scale'; +import NavigationType from '../../config/navigation/propTypes'; + +export class SignUp extends React.Component { + static navigationOptions = { + header: null, + }; + static propTypes = { + navigation: NavigationType.isRequired, + }; + + getThemeImageSource = (theme) => ( + theme.name === 'light' ? + require('../../assets/images/logo.png') : require('../../assets/images/logoDark.png') + ); + + renderImage = () => ( + <Image style={styles.image} source={this.getThemeImageSource(RkTheme.current)} /> + ); + + onSignUpButtonPressed = () => { + this.props.navigation.goBack(); + }; + + onSignInButtonPressed = () => { + this.props.navigation.navigate('Login1'); + }; + + render = () => ( + <RkAvoidKeyboard + style={styles.screen} + onStartShouldSetResponder={() => true} + onResponderRelease={() => Keyboard.dismiss()}> + <View style={{ alignItems: 'center' }}> + {this.renderImage()} + <RkText rkType='h1'>Registration</RkText> + </View> + <View style={styles.content}> + <View> + <RkTextInput rkType='rounded' placeholder='Name' /> + <RkTextInput rkType='rounded' placeholder='Email' /> + <RkTextInput rkType='rounded' placeholder='Password' secureTextEntry /> + <RkTextInput rkType='rounded' placeholder='Confirm Password' secureTextEntry /> + <GradientButton + style={styles.save} + rkType='large' + text='SIGN UP' + onPress={this.onSignUpButtonPressed} + /> + </View> + <View style={styles.footer}> + <View style={styles.textRow}> + <RkText rkType='primary3'>Already have an account?</RkText> + <RkButton rkType='clear' onPress={this.onSignInButtonPressed}> + <RkText rkType='header6'>Sign in now</RkText> + </RkButton> + </View> + </View> + </View> + </RkAvoidKeyboard> + ) +} + +const styles = RkStyleSheet.create(theme => ({ + screen: { + padding: 16, + flex: 1, + justifyContent: 'space-around', + backgroundColor: theme.colors.screen.base, + }, + image: { + marginBottom: 10, + height: scaleVertical(77), + resizeMode: 'contain', + }, + content: { + justifyContent: 'space-between', + }, + save: { + marginVertical: 20, + }, + buttons: { + flexDirection: 'row', + marginBottom: 24, + marginHorizontal: 24, + justifyContent: 'space-around', + }, + footer: { + justifyContent: 'flex-end', + }, + textRow: { + flexDirection: 'row', + justifyContent: 'center', + }, +})); diff --git a/frontend/app/screens/menu/categoryMenu.js b/frontend/app/screens/menu/categoryMenu.js new file mode 100644 index 0000000..6ed6400 --- /dev/null +++ b/frontend/app/screens/menu/categoryMenu.js @@ -0,0 +1,77 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { + TouchableHighlight, + View, + FlatList, + StyleSheet, +} from 'react-native'; +import { + RkStyleSheet, + RkTheme, + RkText, +} from 'react-native-ui-kitten'; +import NavigationType from '../../config/navigation/propTypes'; + +export class CategoryMenu extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + items: PropTypes.arrayOf(PropTypes.shape({ + title: PropTypes.string.isRequired, + })).isRequired, + }; + + onItemPressed = (item) => { + const url = item.action || item.id; + this.props.navigation.navigate(url); + }; + + extractItemKey = (item) => item.id; + + renderItem = ({ item }) => ( + <TouchableHighlight + style={styles.item} + underlayColor={RkTheme.current.colors.button.underlay} + activeOpacity={1} + onPress={() => this.onItemPressed(item)}> + <View> + <RkText>{item.title}</RkText> + </View> + </TouchableHighlight> + ); + + renderPlaceholder = () => ( + <View style={styles.emptyContainer}> + <RkText rkType='light subtitle'>Coming Soon...</RkText> + </View> + ); + + renderList = () => ( + <FlatList + style={styles.list} + data={this.props.items} + keyExtractor={this.extractItemKey} + renderItem={this.renderItem} + /> + ); + + render = () => (this.props.items.length === 0 ? this.renderPlaceholder() : this.renderList()); +} + +const styles = RkStyleSheet.create(theme => ({ + item: { + paddingVertical: 32.5, + paddingHorizontal: 16.5, + borderBottomWidth: StyleSheet.hairlineWidth, + borderColor: theme.colors.border.base, + }, + list: { + backgroundColor: theme.colors.screen.base, + }, + emptyContainer: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + backgroundColor: theme.colors.screen.base, + }, +})); diff --git a/frontend/app/screens/menu/index.js b/frontend/app/screens/menu/index.js new file mode 100644 index 0000000..4bea133 --- /dev/null +++ b/frontend/app/screens/menu/index.js @@ -0,0 +1,2 @@ +export * from './categoryMenu'; +export * from './menus'; diff --git a/frontend/app/screens/menu/menus.js b/frontend/app/screens/menu/menus.js new file mode 100644 index 0000000..8cd8ff2 --- /dev/null +++ b/frontend/app/screens/menu/menus.js @@ -0,0 +1,114 @@ +/* eslint-disable react/no-multi-comp */ +import React from 'react'; + +import { CategoryMenu } from './categoryMenu'; +import * as Routes from '../../config/navigation/routesBuilder'; +import NavigationType from '../../config/navigation/propTypes'; + +export class LoginMenu extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Login'.toUpperCase(), + }; + render = () => ( + <CategoryMenu navigation={this.props.navigation} items={Routes.LoginRoutes} /> + ); +} + +export class NavigationMenu extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Navigation'.toUpperCase(), + }; + render = () => ( + <CategoryMenu navigation={this.props.navigation} items={Routes.NavigationRoutes} /> + ); +} + +export class SocialMenu extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Social'.toUpperCase(), + }; + render = () => ( + <CategoryMenu navigation={this.props.navigation} items={Routes.SocialRoutes} /> + ); +} + +export class ArticleMenu extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Articles'.toUpperCase(), + }; + render = () => ( + <CategoryMenu navigation={this.props.navigation} items={Routes.ArticleRoutes} /> + ); +} + +export class MessagingMenu extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Messaging'.toUpperCase(), + }; + render = () => ( + <CategoryMenu navigation={this.props.navigation} items={Routes.MessagingRoutes} /> + ); +} + +export class DashboardMenu extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Dashboards'.toUpperCase(), + }; + render = () => ( + <CategoryMenu navigation={this.props.navigation} items={Routes.DashboardRoutes} /> + ); +} + +export class WalkthroughMenu extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Walkthrough'.toUpperCase(), + }; + render = () => ( + <CategoryMenu navigation={this.props.navigation} items={Routes.WalkthroughRoutes} /> + ); +} + +export class EcommerceMenu extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Ecommerce'.toUpperCase(), + }; + render = () => ( + <CategoryMenu navigation={this.props.navigation} items={Routes.EcommerceRoutes} /> + ); +} + +export class OtherMenu extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Other'.toUpperCase(), + }; + render = () => ( + <CategoryMenu navigation={this.props.navigation} items={Routes.OtherRoutes} /> + ); +} diff --git a/frontend/app/screens/messaging/chat.js b/frontend/app/screens/messaging/chat.js new file mode 100644 index 0000000..c6b0b5e --- /dev/null +++ b/frontend/app/screens/messaging/chat.js @@ -0,0 +1,214 @@ +import React from 'react'; +import { + FlatList, + View, + Platform, + Image, + TouchableOpacity, + Keyboard, + InteractionManager, +} from 'react-native'; +import { + RkButton, + RkText, + RkTextInput, + RkAvoidKeyboard, + RkStyleSheet, + RkTheme, +} from 'react-native-ui-kitten'; +import _ from 'lodash'; +import { FontAwesome } from '../../assets/icons'; +import { data } from '../../data'; +import { Avatar } from '../../components/avatar'; +import { scale } from '../../utils/scale'; +import NavigationType from '../../config/navigation/propTypes'; + +const moment = require('moment'); + +export class Chat extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = ({ navigation }) => { + const userId = navigation.state.params ? navigation.state.params.userId : undefined; + const user = data.getUser(userId); + return ({ + headerTitle: Chat.renderNavigationTitle(navigation, user), + headerRight: Chat.renderNavigationAvatar(navigation, user), + }); + }; + + constructor(props) { + super(props); + const userId = this.props.navigation.getParam('userId', undefined); + this.state = { + data: data.getConversation(userId), + }; + } + + componentDidMount() { + InteractionManager.runAfterInteractions(() => { + this.listRef.scrollToEnd(); + }); + } + + setListRef = (ref) => { + this.listRef = ref; + }; + + extractItemKey = (item) => `${item.id}`; + + scrollToEnd = () => { + if (Platform.OS === 'ios') { + this.listRef.scrollToEnd(); + } else { + _.delay(this.listRef.scrollToEnd, 100); + } + }; + + onInputChanged = (text) => { + this.setState({ message: text }); + }; + + onSendButtonPressed = () => { + if (!this.state.message) { + return; + } + this.state.data.messages.push({ + id: this.state.data.messages.length, time: 0, type: 'out', text: this.state.message, + }); + this.setState({ message: '' }); + this.scrollToEnd(true); + }; + + static onNavigationTitlePressed = (navigation, user) => { + navigation.navigate('ProfileV1', { id: user.id }); + }; + + static onNavigationAvatarPressed = (navigation, user) => { + navigation.navigate('ProfileV1', { id: user.id }); + }; + + static renderNavigationTitle = (navigation, user) => ( + <TouchableOpacity onPress={() => Chat.onNavigationTitlePressed(navigation, user)}> + <View style={styles.header}> + <RkText rkType='header5'>{`${user.firstName} ${user.lastName}`}</RkText> + <RkText rkType='secondary3 secondaryColor'>Online</RkText> + </View> + </TouchableOpacity> + ); + + static renderNavigationAvatar = (navigation, user) => ( + <TouchableOpacity onPress={() => Chat.onNavigationAvatarPressed(navigation, user)}> + <Avatar style={styles.avatar} rkType='small' img={user.photo} /> + </TouchableOpacity> + ); + + renderDate = (date) => ( + <RkText style={styles.time} rkType='secondary7 hintColor'> + {moment().add(date, 'seconds').format('LT')} + </RkText> + ); + + renderItem = ({ item }) => { + const isIncoming = item.type === 'in'; + const backgroundColor = isIncoming + ? RkTheme.current.colors.chat.messageInBackground + : RkTheme.current.colors.chat.messageOutBackground; + const itemStyle = isIncoming ? styles.itemIn : styles.itemOut; + + return ( + <View style={[styles.item, itemStyle]}> + {!isIncoming && this.renderDate(item.time)} + <View style={[styles.balloon, { backgroundColor }]}> + <RkText rkType='primary2 mediumLine chat' style={{ paddingTop: 5 }}>{item.text}</RkText> + </View> + {isIncoming && this.renderDate(item.time)} + </View> + ); + }; + + render = () => ( + <RkAvoidKeyboard + style={styles.container} + onResponderRelease={Keyboard.dismiss}> + <FlatList + ref={this.setListRef} + extraData={this.state} + style={styles.list} + data={this.state.data.messages} + keyExtractor={this.extractItemKey} + renderItem={this.renderItem} + /> + <View style={styles.footer}> + <RkButton style={styles.plus} rkType='clear'> + <RkText rkType='awesome secondaryColor'>{FontAwesome.plus}</RkText> + </RkButton> + <RkTextInput + onFocus={this.scrollToEnd} + onBlur={this.scrollToEnd} + onChangeText={this.onInputChanged} + value={this.state.message} + rkType='row sticker' + placeholder="Add a comment..." + /> + <RkButton onPress={this.onSendButtonPressed} style={styles.send} rkType='circle highlight'> + <Image source={require('../../assets/icons/sendIcon.png')} /> + </RkButton> + </View> + </RkAvoidKeyboard> + + ) +} + +const styles = RkStyleSheet.create(theme => ({ + header: { + alignItems: 'center', + }, + avatar: { + marginRight: 16, + }, + container: { + flex: 1, + backgroundColor: theme.colors.screen.base, + }, + list: { + paddingHorizontal: 17, + }, + footer: { + flexDirection: 'row', + minHeight: 60, + padding: 10, + backgroundColor: theme.colors.screen.alter, + }, + item: { + marginVertical: 14, + flex: 1, + flexDirection: 'row', + }, + itemIn: {}, + itemOut: { + alignSelf: 'flex-end', + }, + balloon: { + maxWidth: scale(250), + paddingHorizontal: 15, + paddingTop: 10, + paddingBottom: 15, + borderRadius: 20, + }, + time: { + alignSelf: 'flex-end', + margin: 15, + }, + plus: { + paddingVertical: 10, + paddingHorizontal: 10, + marginRight: 7, + }, + send: { + width: 40, + height: 40, + marginLeft: 10, + }, +})); diff --git a/frontend/app/screens/messaging/chatList.js b/frontend/app/screens/messaging/chatList.js new file mode 100644 index 0000000..36cde5c --- /dev/null +++ b/frontend/app/screens/messaging/chatList.js @@ -0,0 +1,147 @@ +import React from 'react'; +import { + FlatList, + View, + StyleSheet, + TouchableOpacity, +} from 'react-native'; +import _ from 'lodash'; +import { + RkStyleSheet, + RkText, + RkTextInput, +} from 'react-native-ui-kitten'; +import { Avatar } from '../../components'; +import { FontAwesome } from '../../assets/icons'; +import { data } from '../../data'; +import NavigationType from '../../config/navigation/propTypes'; + +const moment = require('moment'); + +export class ChatList extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Chats List'.toUpperCase(), + }; + + state = { + data: { + original: data.getChatList(), + filtered: data.getChatList(), + }, + }; + + extractItemKey = (item) => `${item.withUser.id}`; + + onInputChanged = (event) => { + const pattern = new RegExp(event.nativeEvent.text, 'i'); + const chats = _.filter(this.state.data.original, chat => { + const filterResult = { + firstName: chat.withUser.firstName.search(pattern), + lastName: chat.withUser.lastName.search(pattern), + }; + return filterResult.firstName !== -1 || filterResult.lastName !== -1 ? chat : undefined; + }); + this.setState({ + data: { + original: this.state.data.original, + filtered: chats, + }, + }); + }; + + onItemPressed = (item) => { + const navigationParams = { userId: item.withUser.id }; + this.props.navigation.navigate('Chat', navigationParams); + }; + + renderSeparator = () => ( + <View style={styles.separator} /> + ); + + renderInputLabel = () => ( + <RkText rkType='awesome'>{FontAwesome.search}</RkText> + ); + + renderHeader = () => ( + <View style={styles.searchContainer}> + <RkTextInput + autoCapitalize='none' + autoCorrect={false} + onChange={this.onInputChanged} + label={this.renderInputLabel()} + rkType='row' + placeholder='Search' + /> + </View> + ); + + renderItem = ({ item }) => { + const last = item.messages[item.messages.length - 1]; + return ( + <TouchableOpacity onPress={() => this.onItemPressed(item)}> + <View style={styles.container}> + <Avatar rkType='circle' style={styles.avatar} img={item.withUser.photo} /> + <View style={styles.content}> + <View style={styles.contentHeader}> + <RkText rkType='header5'>{`${item.withUser.firstName} ${item.withUser.lastName}`}</RkText> + <RkText rkType='secondary4 hintColor'> + {moment().add(last.time, 'seconds').format('LT')} + </RkText> + </View> + <RkText numberOfLines={2} rkType='primary3 mediumLine' style={{ paddingTop: 5 }}> + {last.text} + </RkText> + </View> + </View> + </TouchableOpacity> + ); + }; + + render = () => ( + <FlatList + style={styles.root} + data={this.state.data.filtered} + extraData={this.state} + ListHeaderComponent={this.renderHeader} + ItemSeparatorComponent={this.renderSeparator} + keyExtractor={this.extractItemKey} + renderItem={this.renderItem} + /> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.base, + }, + searchContainer: { + backgroundColor: theme.colors.screen.bold, + paddingHorizontal: 16, + paddingVertical: 10, + height: 60, + alignItems: 'center', + }, + container: { + paddingLeft: 19, + paddingRight: 16, + paddingBottom: 12, + paddingTop: 7, + flexDirection: 'row', + }, + content: { + marginLeft: 16, + flex: 1, + }, + contentHeader: { + flexDirection: 'row', + justifyContent: 'space-between', + marginBottom: 6, + }, + separator: { + height: StyleSheet.hairlineWidth, + backgroundColor: theme.colors.border.base, + }, +})); diff --git a/frontend/app/screens/messaging/comments.js b/frontend/app/screens/messaging/comments.js new file mode 100644 index 0000000..3b772c5 --- /dev/null +++ b/frontend/app/screens/messaging/comments.js @@ -0,0 +1,98 @@ +import React from 'react'; +import { + FlatList, + View, + StyleSheet, + TouchableOpacity, +} from 'react-native'; +import { + RkStyleSheet, + RkText, +} from 'react-native-ui-kitten'; +import { Avatar } from '../../components'; +import { data } from '../../data'; +import NavigationType from '../../config/navigation/propTypes'; + +const moment = require('moment'); + +export class Comments extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Comments'.toUpperCase(), + }; + + constructor(props) { + super(props); + const postId = this.props.navigation.getParam('postId', undefined); + this.state = { + data: data.getComments(postId), + }; + } + + extractItemKey = (item) => `${item.id}`; + + onItemPressed = (item) => { + const navigationParams = { id: item.user.id }; + this.props.navigation.navigate('ProfileV1', navigationParams); + }; + + renderSeparator = () => ( + <View style={styles.separator} /> + ); + + renderItem = ({ item }) => ( + <View style={styles.container}> + <TouchableOpacity onPress={() => this.onItemPressed(item)}> + <Avatar rkType='circle' style={styles.avatar} img={item.user.photo} /> + </TouchableOpacity> + <View style={styles.content}> + <View style={styles.contentHeader}> + <RkText rkType='header5'>{`${item.user.firstName} ${item.user.lastName}`}</RkText> + <RkText rkType='secondary4 hintColor'> + {moment().add(item.time, 'seconds').format('LT')} + </RkText> + </View> + <RkText rkType='primary3 mediumLine'>{item.text}</RkText> + </View> + </View> + ); + + render = () => ( + <FlatList + style={styles.root} + data={this.state.data} + extraData={this.state} + ItemSeparatorComponent={this.renderSeparator} + keyExtractor={this.extractItemKey} + renderItem={this.renderItem} + /> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.base, + }, + container: { + paddingLeft: 19, + paddingRight: 16, + paddingVertical: 12, + flexDirection: 'row', + alignItems: 'flex-start', + }, + content: { + marginLeft: 16, + flex: 1, + }, + contentHeader: { + flexDirection: 'row', + justifyContent: 'space-between', + marginBottom: 6, + }, + separator: { + height: StyleSheet.hairlineWidth, + backgroundColor: theme.colors.border.base, + }, +})); diff --git a/frontend/app/screens/messaging/index.js b/frontend/app/screens/messaging/index.js new file mode 100644 index 0000000..0d39244 --- /dev/null +++ b/frontend/app/screens/messaging/index.js @@ -0,0 +1,3 @@ +export * from './chat'; +export * from './chatList'; +export * from './comments'; diff --git a/frontend/app/screens/navigation/grid.js b/frontend/app/screens/navigation/grid.js new file mode 100644 index 0000000..7fbe404 --- /dev/null +++ b/frontend/app/screens/navigation/grid.js @@ -0,0 +1,70 @@ +import React from 'react'; +import { + ScrollView, + Dimensions, +} from 'react-native'; +import { + RkButton, RkStyleSheet, + RkText, +} from 'react-native-ui-kitten'; +import { MainRoutes } from '../../config/navigation/routes'; +import NavigationType from '../../config/navigation/propTypes'; + +const paddingValue = 8; + +export class GridV1 extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Grid Menu'.toUpperCase(), + }; + + constructor(props) { + super(props); + const screenWidth = Dimensions.get('window').width; + this.itemSize = { + width: (screenWidth - (paddingValue * 6)) / 2, + height: (screenWidth - (paddingValue * 6)) / 2, + }; + } + + onItemPressed = (item) => { + this.props.navigation.navigate(item.id); + }; + + renderItems = () => MainRoutes.map(route => ( + <RkButton + rkType='square shadow' + style={{ ...this.itemSize }} + key={route.id} + onPress={() => this.onItemPressed(route)}> + <RkText style={styles.icon} rkType='primary moon menuIcon'> + {route.icon} + </RkText> + <RkText>{route.title}</RkText> + </RkButton> + )); + + render = () => ( + <ScrollView + style={styles.root} + contentContainerStyle={styles.rootContainer}> + {this.renderItems()} + </ScrollView> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.scroll, + padding: paddingValue, + }, + rootContainer: { + flexDirection: 'row', + flexWrap: 'wrap', + }, + icon: { + marginBottom: 16, + }, +})); diff --git a/frontend/app/screens/navigation/grid2.js b/frontend/app/screens/navigation/grid2.js new file mode 100644 index 0000000..8773e02 --- /dev/null +++ b/frontend/app/screens/navigation/grid2.js @@ -0,0 +1,82 @@ +import React from 'react'; +import { + ScrollView, + View, + StyleSheet, +} from 'react-native'; +import { + RkText, + RkButton, + RkStyleSheet, +} from 'react-native-ui-kitten'; +import { MainRoutes } from '../../config/navigation/routes'; +import NavigationType from '../../config/navigation/propTypes'; + +export class GridV2 extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Grid Menu'.toUpperCase(), + }; + + state = { + dimensions: undefined, + }; + + onContainerLayout = (event) => { + if (this.state.height) { + return; + } + const dimensions = event.nativeEvent.layout; + this.setState({ dimensions }); + }; + + renderItems = () => MainRoutes.map(this.renderItem); + + renderItem = (item) => ( + <RkButton + rkType='tile' + style={{ height: this.state.dimensions.width / 3, width: this.state.dimensions.width / 3 }} + key={item.id} + onPress={() => this.onItemPressed(item)}> + <RkText style={styles.icon} rkType='primary moon xxlarge'> + {item.icon} + </RkText> + <RkText rkType='small'>{item.title}</RkText> + </RkButton> + ); + + onItemPressed = (item) => { + this.props.navigation.navigate(item.id); + }; + + render() { + const items = this.state.dimensions === undefined ? <View /> : this.renderItems(); + return ( + <ScrollView + style={styles.root} + onLayout={this.onContainerLayout} + contentContainerStyle={styles.rootContainer}> + {items} + </ScrollView> + ); + } +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.base, + }, + rootContainer: { + flexDirection: 'row', + flexWrap: 'wrap', + }, + empty: { + borderWidth: StyleSheet.hairlineWidth, + borderColor: theme.colors.border.base, + }, + icon: { + marginBottom: 16, + }, +})); diff --git a/frontend/app/screens/navigation/index.js b/frontend/app/screens/navigation/index.js new file mode 100644 index 0000000..3f5229f --- /dev/null +++ b/frontend/app/screens/navigation/index.js @@ -0,0 +1,4 @@ +export * from './grid2'; +export * from './grid'; +export * from './sideMenu'; +export * from './list'; diff --git a/frontend/app/screens/navigation/list.js b/frontend/app/screens/navigation/list.js new file mode 100644 index 0000000..33c52ba --- /dev/null +++ b/frontend/app/screens/navigation/list.js @@ -0,0 +1,73 @@ +import React from 'react'; +import { + FlatList, + TouchableOpacity, + View, + StyleSheet, +} from 'react-native'; +import { + RkText, + RkStyleSheet, +} from 'react-native-ui-kitten'; +import { MainRoutes } from '../../config/navigation/routes'; +import NavigationType from '../../config/navigation/propTypes'; + +export class ListMenu extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'List Menu'.toUpperCase(), + }; + + onItemPressed = (item) => { + this.props.navigation.navigate(item.id); + }; + + extractItemKey = (item) => item.id; + + renderItem = ({ item }) => ( + <TouchableOpacity + style={styles.item} + onPress={() => this.onItemPressed(item)}> + <View style={styles.container}> + <RkText + style={styles.icon} + rkType='primary moon xxlarge'>{item.icon} + </RkText> + <RkText>{item.title}</RkText> + </View> + </TouchableOpacity> + ); + + render = () => ( + <FlatList + style={styles.list} + data={MainRoutes} + keyExtractor={this.extractItemKey} + renderItem={this.renderItem} + /> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + item: { + height: 80, + justifyContent: 'center', + borderBottomWidth: StyleSheet.hairlineWidth, + borderColor: theme.colors.border.base, + paddingHorizontal: 16, + }, + list: { + backgroundColor: theme.colors.screen.base, + }, + container: { + flexDirection: 'row', + alignItems: 'center', + }, + icon: { + width: 34, + textAlign: 'center', + marginRight: 16, + }, +})); diff --git a/frontend/app/screens/navigation/sideMenu.js b/frontend/app/screens/navigation/sideMenu.js new file mode 100644 index 0000000..7f5c5a1 --- /dev/null +++ b/frontend/app/screens/navigation/sideMenu.js @@ -0,0 +1,113 @@ +import React from 'react'; +import { + TouchableHighlight, + View, + ScrollView, + Image, + Platform, + StyleSheet, +} from 'react-native'; +import { + RkStyleSheet, + RkText, + RkTheme, +} from 'react-native-ui-kitten'; +import { MainRoutes } from '../../config/navigation/routes'; +import { FontAwesome, FontIcons } from '../../assets/icons'; +import NavigationType from '../../config/navigation/propTypes'; +import * as Screens from '../index'; + +export const NavbarRoutes = [ + { + id: 'Articles2', + title: 'Problems', + icon: FontIcons.article, + screen: Screens.Articles2, + + }, { + id: 'Login1', + title: 'Login', + icon: FontIcons.login, + screen: Screens.LoginV1, + + }, { + id: 'AddProblem', + title: 'Add Problem', + icon: FontIcons.article, + screen: Screens.AddToCardForm, + + }]; + +export class SideMenu extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + + onMenuItemPressed = (item) => { + this.props.navigation.navigate(item.id); + }; + + getThemeImageSource = (theme) => ( + theme.name === 'light' ? + require('../../assets/images/smallLogo.png') : require('../../assets/images/smallLogoDark.png') + ); + + renderIcon = () => ( + <Image style={styles.icon} source={this.getThemeImageSource(RkTheme.current)}/> + ); + + renderMenu = () => NavbarRoutes.map(this.renderMenuItem); + + renderMenuItem = (item) => ( + <TouchableHighlight + style={styles.container} + key={item.id} + underlayColor={RkTheme.current.colors.button.underlay} + activeOpacity={1} + onPress={() => this.onMenuItemPressed(item)}> + <View style={styles.content}> + <View style={styles.content}> + <RkText + style={styles.icon} + rkType='moon primary xlarge'>{item.icon} + </RkText> + <RkText>{item.title}</RkText> + </View> + <RkText rkType='awesome secondaryColor small'>{FontAwesome.chevronRight}</RkText> + </View> + </TouchableHighlight> + ); + + render = () => ( + <View style={styles.root}> + <ScrollView + showsVerticalScrollIndicator={false}> + <View style={[styles.container, styles.content]}> + <RkText rkType='logo'>EcoAlerts</RkText> + </View> + {this.renderMenu()} + </ScrollView> + </View> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + container: { + height: 80, + paddingHorizontal: 16, + borderTopWidth: StyleSheet.hairlineWidth, + borderColor: theme.colors.border.base, + }, + root: { + paddingTop: Platform.OS === 'ios' ? 20 : 0, + backgroundColor: theme.colors.screen.base, + }, + content: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + }, + icon: { + marginRight: 13, + }, +})); diff --git a/frontend/app/screens/other/index.js b/frontend/app/screens/other/index.js new file mode 100644 index 0000000..9784f88 --- /dev/null +++ b/frontend/app/screens/other/index.js @@ -0,0 +1,2 @@ +export * from './splash'; +export * from './settings'; diff --git a/frontend/app/screens/other/settings.js b/frontend/app/screens/other/settings.js new file mode 100644 index 0000000..910deb0 --- /dev/null +++ b/frontend/app/screens/other/settings.js @@ -0,0 +1,176 @@ +import React from 'react'; +import { + ScrollView, + View, + TouchableOpacity, + StyleSheet, +} from 'react-native'; +import { + RkText, + RkStyleSheet, + RkTheme, +} from 'react-native-ui-kitten'; +import { + RkSwitch, + FindFriends, +} from '../../components'; +import { FontAwesome } from '../../assets/icons'; + +export class Settings extends React.Component { + static navigationOptions = { + title: 'Settings'.toUpperCase(), + }; + + state = { + sendPush: true, + shouldRefresh: false, + twitterEnabled: true, + googleEnabled: false, + facebookEnabled: true, + }; + + onPushNotificationsSettingChanged = (value) => { + this.setState({ sendPush: value }); + }; + + onRefreshAutomaticallySettingChanged = (value) => { + this.setState({ shouldRefresh: value }); + }; + + onFindFriendsTwitterButtonPressed = () => { + this.setState({ twitterEnabled: !this.state.twitterEnabled }); + }; + + onFindFriendsGoogleButtonPressed = () => { + this.setState({ googleEnabled: !this.state.googleEnabled }); + }; + + onFindFriendsFacebookButtonPressed = () => { + this.setState({ facebookEnabled: !this.state.facebookEnabled }); + }; + + render = () => ( + <ScrollView style={styles.container}> + <View style={styles.section}> + <View style={[styles.row, styles.heading]}> + <RkText rkType='primary header6'>PROFILE SETTINGS</RkText> + </View> + <View style={styles.row}> + <TouchableOpacity style={styles.rowButton}> + <RkText rkType='header6'>Edit Profile</RkText> + </TouchableOpacity> + </View> + <View style={styles.row}> + <TouchableOpacity style={styles.rowButton}> + <RkText rkType='header6'>Change Password</RkText> + </TouchableOpacity> + </View> + <View style={styles.row}> + <RkText rkType='header6'>Send Push Notifications</RkText> + <RkSwitch + style={styles.switch} + value={this.state.sendPush} + name="Push" + onValueChange={this.onPushNotificationsSettingChanged} + /> + </View> + <View style={styles.row}> + <RkText rkType='header6'>Refresh Automatically</RkText> + <RkSwitch + style={styles.switch} + value={this.state.shouldRefresh} + name="Refresh" + onValueChange={this.onRefreshAutomaticallySettingChanged} + /> + </View> + </View> + <View style={styles.section}> + <View style={[styles.row, styles.heading]}> + <RkText rkType='primary header6'>FIND FRIENDS</RkText> + </View> + <View style={styles.row}> + <FindFriends + color={RkTheme.current.colors.twitter} + text='Twitter' + icon={FontAwesome.twitter} + selected={this.state.twitterEnabled} + onPress={this.onFindFriendsTwitterButtonPressed} + /> + </View> + <View style={styles.row}> + <FindFriends + color={RkTheme.current.colors.google} + text='Google' + icon={FontAwesome.google} + selected={this.state.googleEnabled} + onPress={this.onFindFriendsGoogleButtonPressed} + /> + </View> + <View style={styles.row}> + <FindFriends + color={RkTheme.current.colors.facebook} + text='Facebook' + icon={FontAwesome.facebook} + selected={this.state.facebookEnabled} + onPress={this.onFindFriendsFacebookButtonPressed} + /> + </View> + </View> + <View style={styles.section}> + <View style={[styles.row, styles.heading]}> + <RkText rkType='primary header6'>SUPPORT</RkText> + </View> + <View style={styles.row}> + <TouchableOpacity style={styles.rowButton}> + <RkText rkType='header6'>Help</RkText> + </TouchableOpacity> + </View> + <View style={styles.row}> + <TouchableOpacity style={styles.rowButton}> + <RkText rkType='header6'>Privacy Policy</RkText> + </TouchableOpacity> + </View> + <View style={styles.row}> + <TouchableOpacity style={styles.rowButton}> + <RkText rkType='header6'>Terms & Conditions</RkText> + </TouchableOpacity> + </View> + <View style={styles.row}> + <TouchableOpacity style={styles.rowButton}> + <RkText rkType='header6'>Logout</RkText> + </TouchableOpacity> + </View> + </View> + </ScrollView> + ) +} + +const styles = RkStyleSheet.create(theme => ({ + container: { + backgroundColor: theme.colors.screen.base, + }, + header: { + paddingVertical: 25, + }, + section: { + marginVertical: 25, + }, + heading: { + paddingBottom: 12.5, + }, + row: { + flexDirection: 'row', + justifyContent: 'space-between', + paddingHorizontal: 17.5, + borderBottomWidth: StyleSheet.hairlineWidth, + borderColor: theme.colors.border.base, + alignItems: 'center', + }, + rowButton: { + flex: 1, + paddingVertical: 24, + }, + switch: { + marginVertical: 14, + }, +})); diff --git a/frontend/app/screens/other/splash.js b/frontend/app/screens/other/splash.js new file mode 100644 index 0000000..33e7abe --- /dev/null +++ b/frontend/app/screens/other/splash.js @@ -0,0 +1,108 @@ +import React from 'react'; +import { + StyleSheet, + Image, + View, + Dimensions, + StatusBar, +} from 'react-native'; +import { + RkText, + RkTheme, +} from 'react-native-ui-kitten'; +import { + StackActions, + NavigationActions, +} from 'react-navigation'; +import { ProgressBar } from '../../components'; +import { KittenTheme } from '../../config/theme'; +import { scale, scaleVertical } from '../../utils/scale'; +import NavigationType from '../../config/navigation/propTypes'; + +const delay = 800; + +export class SplashScreen extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + state = { + progress: 0, + }; + + componentDidMount() { + StatusBar.setHidden(true, 'none'); + RkTheme.setTheme(KittenTheme); + this.timer = setInterval(this.updateProgress, delay); + } + + componentWillUnmount() { + clearInterval(this.timer); + } + + updateProgress = () => { + if (this.state.progress === 1) { + clearInterval(this.timer); + setTimeout(this.onLoaded, delay); + } else { + const randProgress = this.state.progress + (Math.random() * 0.5); + this.setState({ progress: randProgress > 1 ? 1 : randProgress }); + } + }; + + onLoaded = () => { + StatusBar.setHidden(false, 'slide'); + const toHome = StackActions.reset({ + index: 0, + actions: [NavigationActions.navigate({ routeName: 'Home' })], + }); + this.props.navigation.dispatch(toHome); + }; + + render = () => ( + <View style={styles.container}> + <View> + <Image + style={[styles.image, { width: Dimensions.get('window').width }]} + source={require('../../assets/images/splashBack.png')} + /> + <View style={styles.text}> + <RkText rkType='light' style={styles.hero}>EcoAlerts</RkText> + <RkText rkType='logo' style={styles.appName}>The best ecological problems coordinator</RkText> + </View> + </View> + <ProgressBar + color={RkTheme.current.colors.accent} + style={styles.progress} + progress={this.state.progress} + width={scale(320)} + /> + </View> + ); +} + +const styles = StyleSheet.create({ + container: { + backgroundColor: KittenTheme.colors.screen.base, + justifyContent: 'space-between', + flex: 1, + }, + image: { + resizeMode: 'cover', + height: scaleVertical(430), + }, + text: { + alignItems: 'center', + }, + hero: { + fontSize: 37, + }, + appName: { + textAlign: 'center', + fontSize: 33, + }, + progress: { + alignSelf: 'center', + marginBottom: 35, + backgroundColor: '#e5e5e5', + }, +}); diff --git a/frontend/app/screens/social/contacts.js b/frontend/app/screens/social/contacts.js new file mode 100644 index 0000000..8eaacae --- /dev/null +++ b/frontend/app/screens/social/contacts.js @@ -0,0 +1,124 @@ +import React from 'react'; +import { + FlatList, + View, + StyleSheet, + TouchableOpacity, +} from 'react-native'; +import _ from 'lodash'; +import { + RkStyleSheet, + RkText, + RkTextInput, +} from 'react-native-ui-kitten'; +import { data } from '../../data'; +import { Avatar } from '../../components/avatar'; +import { FontAwesome } from '../../assets/icons'; +import NavigationType from '../../config/navigation/propTypes'; + +export class Contacts extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'Contacts'.toUpperCase(), + }; + + state = { + data: { + original: data.getUsers(), + filtered: data.getUsers(), + }, + }; + + extractItemKey = (item) => `${item.id}`; + + onSearchInputChanged = (event) => { + const pattern = new RegExp(event.nativeEvent.text, 'i'); + const contacts = _.filter(this.state.data.original, contact => { + const filterResult = { + firstName: contact.firstName.search(pattern), + lastName: contact.lastName.search(pattern), + }; + return filterResult.firstName !== -1 || filterResult.lastName !== -1 ? contact : undefined; + }); + this.setState({ + data: { + original: this.state.data.original, + filtered: contacts, + }, + }); + }; + + onItemPressed = (item) => { + this.props.navigation.navigate('ProfileV1', { id: item.id }); + }; + + renderItem = ({ item }) => ( + <TouchableOpacity onPress={() => this.onItemPressed(item)}> + <View style={styles.container}> + <Avatar rkType='circle' style={styles.avatar} img={item.photo} /> + <RkText>{`${item.firstName} ${item.lastName}`}</RkText> + </View> + </TouchableOpacity> + ); + + renderSeparator = () => ( + <View style={styles.separator} /> + ); + + renderHeaderLabel = () => ( + <RkText rkType='awesome'>{FontAwesome.search}</RkText> + ); + + renderHeader = () => ( + <View style={styles.searchContainer}> + <RkTextInput + autoCapitalize='none' + autoCorrect={false} + onChange={this.onSearchInputChanged} + label={this.renderHeaderLabel()} + rkType='row' + placeholder='Search' + /> + </View> + ); + + render = () => ( + <FlatList + style={styles.root} + data={this.state.data.filtered} + renderItem={this.renderItem} + ListHeaderComponent={this.renderHeader} + ItemSeparatorComponent={this.renderSeparator} + keyExtractor={this.extractItemKey} + enableEmptySections + /> + ) +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.base, + }, + searchContainer: { + backgroundColor: theme.colors.screen.bold, + paddingHorizontal: 16, + paddingVertical: 10, + height: 60, + alignItems: 'center', + }, + container: { + flexDirection: 'row', + padding: 16, + alignItems: 'center', + }, + avatar: { + marginRight: 16, + }, + separator: { + flex: 1, + height: StyleSheet.hairlineWidth, + backgroundColor: theme.colors.border.base, + }, +})); diff --git a/frontend/app/screens/social/feed.js b/frontend/app/screens/social/feed.js new file mode 100644 index 0000000..3de46f2 --- /dev/null +++ b/frontend/app/screens/social/feed.js @@ -0,0 +1,73 @@ +import React from 'react'; +import { + FlatList, + View, + Image, +} from 'react-native'; +import { + RkCard, + RkText, RkStyleSheet, +} from 'react-native-ui-kitten'; +import { Avatar } from '../../components/avatar'; +import { SocialBar } from '../../components/socialBar'; +import { data } from '../../data'; + +const moment = require('moment'); + +export class Feed extends React.Component { + static navigationOptions = { + title: 'Feed'.toUpperCase(), + }; + + state = { + data: data.getArticles('post'), + }; + + extractItemKey = (item) => `${item.id}`; + + renderItem = ({ item }) => ( + <RkCard style={styles.card}> + <View rkCardHeader> + <Avatar + rkType='small' + style={styles.avatar} + img={item.user.photo} + /> + <View> + <RkText rkType='header4'>{`${item.user.firstName} ${item.user.lastName}`}</RkText> + <RkText rkType='secondary2 hintColor'>{moment().add(item.time, 'seconds').fromNow()}</RkText> + </View> + </View> + <Image rkCardImg source={item.photo} /> + <View rkCardContent> + <RkText rkType='primary3'>{item.text}</RkText> + </View> + <View rkCardFooter> + <SocialBar /> + </View > + </RkCard> + ); + + render = () => ( + <FlatList + data={this.state.data} + renderItem={this.renderItem} + keyExtractor={this.extractItemKey} + style={styles.container} + /> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + container: { + backgroundColor: theme.colors.screen.scroll, + paddingVertical: 8, + paddingHorizontal: 10, + }, + card: { + marginVertical: 8, + }, + avatar: { + marginRight: 16, + }, +})); diff --git a/frontend/app/screens/social/index.js b/frontend/app/screens/social/index.js new file mode 100644 index 0000000..bd66176 --- /dev/null +++ b/frontend/app/screens/social/index.js @@ -0,0 +1,7 @@ +export * from './profile1'; +export * from './profile2'; +export * from './profile3'; +export * from './profileSettings'; +export * from './notifications'; +export * from './contacts'; +export * from './feed'; diff --git a/frontend/app/screens/social/notifications.js b/frontend/app/screens/social/notifications.js new file mode 100644 index 0000000..c397d17 --- /dev/null +++ b/frontend/app/screens/social/notifications.js @@ -0,0 +1,98 @@ +import React from 'react'; +import { + FlatList, + View, + Image, +} from 'react-native'; +import { RkStyleSheet, RkText } from 'react-native-ui-kitten'; +import { Avatar } from '../../components'; +import { data } from '../../data'; + +const moment = require('moment'); + +export class Notifications extends React.Component { + static navigationOptions = { + title: 'Notifications', + }; + + state = { + data: data.getNotifications(), + }; + + extractItemKey = (item) => `${item.id}`; + + renderAttachment = (item) => { + const hasAttachment = item.attach !== undefined; + return hasAttachment ? <View /> : <Image style={styles.attachment} source={item.attach} />; + }; + + renderItem = ({ item }) => ( + <View style={styles.container}> + <Avatar + img={item.user.photo} + rkType='circle' + style={styles.avatar} + badge={item.type} + /> + <View style={styles.content}> + <View style={styles.mainContent}> + <View style={styles.text}> + <RkText> + <RkText rkType='header6'>{`${item.user.firstName} ${item.user.lastName}`}</RkText> + <RkText rkType='primary2'> {item.description}</RkText> + </RkText> + </View> + <RkText + rkType='secondary5 hintColor'>{moment().add(item.time, 'seconds').fromNow()} + </RkText> + </View> + {this.renderAttachment(item)} + </View> + </View> + ); + + render = () => ( + <FlatList + style={styles.root} + data={this.state.data} + renderItem={this.renderItem} + keyExtractor={this.extractItemKey} + /> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.base, + }, + container: { + padding: 16, + flexDirection: 'row', + borderBottomWidth: 1, + borderColor: theme.colors.border.base, + alignItems: 'flex-start', + }, + avatar: {}, + text: { + marginBottom: 5, + }, + content: { + flex: 1, + marginLeft: 16, + marginRight: 0, + }, + mainContent: { + marginRight: 60, + }, + img: { + height: 50, + width: 50, + margin: 0, + }, + attachment: { + position: 'absolute', + right: 0, + height: 50, + width: 50, + }, +})); diff --git a/frontend/app/screens/social/profile1.js b/frontend/app/screens/social/profile1.js new file mode 100644 index 0000000..1f3ec09 --- /dev/null +++ b/frontend/app/screens/social/profile1.js @@ -0,0 +1,104 @@ +import React from 'react'; +import { + View, + ScrollView, +} from 'react-native'; +import { + RkText, + RkButton, RkStyleSheet, +} from 'react-native-ui-kitten'; +import { Avatar } from '../../components/avatar'; +import { Gallery } from '../../components/gallery'; +import { data } from '../../data/'; +import formatNumber from '../../utils/textUtils'; +import NavigationType from '../../config/navigation/propTypes'; + +export class ProfileV1 extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + title: 'User Profile'.toUpperCase(), + }; + + state = { + data: undefined, + }; + + constructor(props) { + super(props); + const id = this.props.navigation.getParam('id', 1); + this.state.data = data.getUser(id); + } + + render = () => ( + <ScrollView style={styles.root}> + <View style={[styles.header, styles.bordered]}> + <Avatar img={this.state.data.photo} rkType='big' /> + <RkText rkType='header2'>{`${this.state.data.firstName} ${this.state.data.lastName}`}</RkText> + </View> + <View style={[styles.userInfo, styles.bordered]}> + <View style={styles.section}> + <RkText rkType='header3' style={styles.space}>{this.state.data.postCount}</RkText> + <RkText rkType='secondary1 hintColor'>Posts</RkText> + </View> + <View style={styles.section}> + <RkText rkType='header3' style={styles.space}>{formatNumber(this.state.data.followersCount)}</RkText> + <RkText rkType='secondary1 hintColor'>Followers</RkText> + </View> + <View style={styles.section}> + <RkText rkType='header3' style={styles.space}>{this.state.data.followingCount}</RkText> + <RkText rkType='secondary1 hintColor'>Following</RkText> + </View> + </View> + <View style={styles.buttons}> + <RkButton style={styles.button} rkType='clear link'>FOLLOW</RkButton> + <View style={styles.separator} /> + <RkButton style={styles.button} rkType='clear link'>MESSAGE</RkButton> + </View> + <Gallery items={this.state.data.images} /> + </ScrollView> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.base, + }, + header: { + alignItems: 'center', + paddingTop: 25, + paddingBottom: 17, + }, + userInfo: { + flexDirection: 'row', + paddingVertical: 18, + }, + bordered: { + borderBottomWidth: 1, + borderColor: theme.colors.border.base, + }, + section: { + flex: 1, + alignItems: 'center', + }, + space: { + marginBottom: 3, + }, + separator: { + backgroundColor: theme.colors.border.base, + alignSelf: 'center', + flexDirection: 'row', + flex: 0, + width: 1, + height: 42, + }, + buttons: { + flexDirection: 'row', + paddingVertical: 8, + }, + button: { + flex: 1, + alignSelf: 'center', + }, +})); diff --git a/frontend/app/screens/social/profile2.js b/frontend/app/screens/social/profile2.js new file mode 100644 index 0000000..1636910 --- /dev/null +++ b/frontend/app/screens/social/profile2.js @@ -0,0 +1,108 @@ +import React from 'react'; +import { + View, + ScrollView, +} from 'react-native'; +import { + RkText, + RkButton, RkStyleSheet, +} from 'react-native-ui-kitten'; +import { + Avatar, + Gallery, +} from '../../components'; +import { data } from '../../data'; +import { FontIcons } from '../../assets/icons'; +import formatNumber from '../../utils/textUtils'; + +export class ProfileV2 extends React.Component { + static navigationOptions = { + title: 'User Profile'.toUpperCase(), + }; + + state = { + data: data.getUser(), + }; + + render = () => ( + <ScrollView style={styles.root}> + <View style={[styles.header, styles.bordered]}> + <View style={styles.row}> + <View style={styles.buttons}> + <RkButton style={styles.button} rkType='icon circle'> + <RkText rkType='moon large primary'>{FontIcons.profile}</RkText> + </RkButton> + </View> + <Avatar img={this.state.data.photo} rkType='big' /> + <View style={styles.buttons}> + <RkButton style={styles.button} rkType='icon circle'> + <RkText rkType='moon large primary'>{FontIcons.mail}</RkText> + </RkButton> + </View> + </View> + <View style={styles.section}> + <RkText rkType='header2'>{`${this.state.data.firstName} ${this.state.data.lastName}`}</RkText> + </View> + </View> + <View style={styles.userInfo}> + <View style={styles.section}> + <RkText rkType='header3' style={styles.space}>{this.state.data.postCount}</RkText> + <RkText rkType='secondary1 hintColor'>Posts</RkText> + </View> + <View style={styles.section}> + <RkText rkType='header3' style={styles.space}>{formatNumber(this.state.data.followersCount)}</RkText> + <RkText rkType='secondary1 hintColor'>Followers</RkText> + </View> + <View style={styles.section}> + <RkText rkType='header3' style={styles.space}>{this.state.data.followingCount}</RkText> + <RkText rkType='secondary1 hintColor'>Following</RkText> + </View> + </View> + <Gallery items={this.state.data.images} /> + </ScrollView> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.base, + }, + header: { + paddingTop: 25, + paddingBottom: 17, + }, + row: { + flexDirection: 'row', + + }, + userInfo: { + flexDirection: 'row', + paddingVertical: 18, + }, + bordered: { + borderBottomWidth: 1, + borderColor: theme.colors.border.base, + }, + section: { + flex: 1, + alignItems: 'center', + }, + space: { + marginBottom: 3, + }, + separator: { + backgroundColor: theme.colors.border.base, + alignSelf: 'center', + flexDirection: 'row', + flex: 0, + width: 1, + height: 42, + }, + buttons: { + flex: 1, + }, + button: { + marginTop: 27.5, + alignSelf: 'center', + }, +})); diff --git a/frontend/app/screens/social/profile3.js b/frontend/app/screens/social/profile3.js new file mode 100644 index 0000000..985dc1e --- /dev/null +++ b/frontend/app/screens/social/profile3.js @@ -0,0 +1,96 @@ +import React from 'react'; +import { + View, + ScrollView, +} from 'react-native'; +import { + RkStyleSheet, + RkText, +} from 'react-native-ui-kitten'; +import { + Avatar, + Gallery, + GradientButton, +} from '../../components'; +import { data } from '../../data'; +import formatNumber from '../../utils/textUtils'; + +export class ProfileV3 extends React.Component { + static navigationOptions = { + title: 'User Profile'.toUpperCase(), + }; + + state = { + data: data.getUser(), + }; + + render = () => ( + <ScrollView style={styles.root}> + <View style={[styles.header, styles.bordered]}> + <Avatar img={this.state.data.photo} rkType='big' /> + <RkText rkType='header2'>{`${this.state.data.firstName} ${this.state.data.lastName}`}</RkText> + <GradientButton style={styles.button} text='FOLLOW' /> + </View> + + <View style={styles.userInfo}> + <View style={styles.section}> + <RkText rkType='header3' style={styles.space}>{this.state.data.postCount}</RkText> + <RkText rkType='secondary1 hintColor'>Posts</RkText> + </View> + <View style={styles.section}> + <RkText rkType='header3' style={styles.space}>{formatNumber(this.state.data.followersCount)}</RkText> + <RkText rkType='secondary1 hintColor'>Followers</RkText> + </View> + <View style={styles.section}> + <RkText rkType='header3' style={styles.space}>{this.state.data.followingCount}</RkText> + <RkText rkType='secondary1 hintColor'>Following</RkText> + </View> + </View> + <Gallery items={this.state.data.images} /> + </ScrollView> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.base, + }, + header: { + alignItems: 'center', + paddingTop: 25, + paddingBottom: 17, + }, + userInfo: { + flexDirection: 'row', + paddingVertical: 18, + }, + bordered: { + borderBottomWidth: 1, + borderColor: theme.colors.border.base, + }, + section: { + flex: 1, + alignItems: 'center', + }, + space: { + marginBottom: 3, + }, + separator: { + backgroundColor: theme.colors.border.base, + alignSelf: 'center', + flexDirection: 'row', + flex: 0, + width: 1, + height: 42, + }, + buttons: { + flexDirection: 'row', + paddingVertical: 8, + }, + button: { + marginTop: 18, + alignSelf: 'center', + width: 140, + }, + +})); diff --git a/frontend/app/screens/social/profileSettings.js b/frontend/app/screens/social/profileSettings.js new file mode 100644 index 0000000..0be1b94 --- /dev/null +++ b/frontend/app/screens/social/profileSettings.js @@ -0,0 +1,200 @@ +import React from 'react'; +import { + ScrollView, + View, + StyleSheet, +} from 'react-native'; +import { + RkText, + RkTextInput, + RkAvoidKeyboard, + RkTheme, + RkStyleSheet, +} from 'react-native-ui-kitten'; +import { data } from '../../data'; +import { + Avatar, + SocialSetting, + GradientButton, +} from '../../components'; +import { FontAwesome } from '../../assets/icons'; + +export class ProfileSettings extends React.Component { + static navigationOptions = { + title: 'Profile Settings'.toUpperCase(), + }; + + user = data.getUser(); + + state = { + firstName: this.user.firstName, + lastName: this.user.lastName, + email: this.user.email, + country: this.user.country, + phone: this.user.phone, + password: this.user.password, + newPassword: this.user.newPassword, + confirmPassword: this.user.confirmPassword, + }; + + onFirstNameInputChanged = (text) => { + this.setState({ firstName: text }); + }; + + onLastNameInputChanged = (text) => { + this.setState({ lastName: text }); + }; + + onEmailInputChanged = (text) => { + this.setState({ email: text }); + }; + + onCountryInputChanged = (text) => { + this.setState({ country: text }); + }; + + onPhoneInputChanged = (text) => { + this.setState({ phone: text }); + }; + + onPasswordInputChanged = (text) => { + this.setState({ password: text }); + }; + + onNewPasswordInputChanged = (text) => { + this.setState({ newPassword: text }); + }; + + onConfirmPasswordInputChanged = (text) => { + this.setState({ confirmPassword: text }); + }; + + render = () => ( + <ScrollView style={styles.root}> + <RkAvoidKeyboard> + <View style={styles.header}> + <Avatar img={this.user.photo} rkType='big' /> + </View> + <View style={styles.section}> + <View style={[styles.row, styles.heading]}> + <RkText rkType='header6 primary'>INFO</RkText> + </View> + <View style={styles.row}> + <RkTextInput + label='First Name' + value={this.state.firstName} + rkType='right clear' + onChangeText={this.onFirstNameInputChanged} + /> + </View> + <View style={styles.row}> + <RkTextInput + label='Last Name' + value={this.state.lastName} + onChangeText={this.onLastNameInputChanged} + rkType='right clear' + /> + </View> + <View style={styles.row}> + <RkTextInput + label='Email' + value={this.state.email} + onChangeText={this.onEmailInputChanged} + rkType='right clear' + /> + </View> + <View style={styles.row}> + <RkTextInput + label='Country' + value={this.state.country} + onChangeText={this.onCountryInputChanged} + rkType='right clear' + /> + </View> + <View style={styles.row}> + <RkTextInput + label='Phone' + value={this.state.phone} + onChangeText={this.onPhoneInputChanged} + rkType='right clear' + /> + </View> + </View> + <View style={styles.section}> + <View style={[styles.row, styles.heading]}> + <RkText rkType='primary header6'>CHANGE PASSWORD</RkText> + </View> + <View style={styles.row}> + <RkTextInput + label='Old Password' + value={this.state.password} + rkType='right clear' + secureTextEntry + onChangeText={this.onPasswordInputChanged} + /> + </View> + <View style={styles.row}> + <RkTextInput + label='New Password' + value={this.state.newPassword} + rkType='right clear' + secureTextEntry + onChangeText={this.onNewPasswordInputChanged} + /> + </View> + <View style={styles.row}> + <RkTextInput + label='Confirm Password' + value={this.state.confirmPassword} + rkType='right clear' + secureTextEntry + onChangeText={this.onConfirmPasswordInputChanged} + /> + </View> + </View> + <View style={styles.section}> + <View style={[styles.row, styles.heading]}> + <RkText rkType='primary header6'>CONNECT YOUR ACCOUNT</RkText> + </View> + <View style={styles.row}> + <SocialSetting name='Twitter' icon={FontAwesome.twitter} tintColor={RkTheme.current.colors.twitter} /> + </View> + <View style={styles.row}> + <SocialSetting name='Google' icon={FontAwesome.google} tintColor={RkTheme.current.colors.google} /> + </View> + <View style={styles.row}> + <SocialSetting name='Facebook' icon={FontAwesome.facebook} tintColor={RkTheme.current.colors.facebook} /> + </View> + </View> + <GradientButton rkType='large' style={styles.button} text='SAVE' /> + </RkAvoidKeyboard> + </ScrollView> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.base, + }, + header: { + backgroundColor: theme.colors.screen.neutral, + paddingVertical: 25, + }, + section: { + marginVertical: 25, + }, + heading: { + paddingBottom: 12.5, + }, + row: { + flexDirection: 'row', + paddingHorizontal: 17.5, + borderBottomWidth: StyleSheet.hairlineWidth, + borderColor: theme.colors.border.base, + alignItems: 'center', + }, + button: { + marginHorizontal: 16, + marginBottom: 32, + }, +})); diff --git a/frontend/app/screens/theme/index.js b/frontend/app/screens/theme/index.js new file mode 100644 index 0000000..22cacb2 --- /dev/null +++ b/frontend/app/screens/theme/index.js @@ -0,0 +1 @@ +export * from './themes'; diff --git a/frontend/app/screens/theme/themes.js b/frontend/app/screens/theme/themes.js new file mode 100644 index 0000000..82f4165 --- /dev/null +++ b/frontend/app/screens/theme/themes.js @@ -0,0 +1,77 @@ +import React from 'react'; +import { + View, + Image, + StatusBar, + Platform, +} from 'react-native'; +import { + RkText, + RkTheme, + RkStyleSheet, +} from 'react-native-ui-kitten'; +import { DarkKittenTheme } from '../../config/darkTheme'; +import { KittenTheme } from '../../config/theme'; +import { GradientButton } from '../../components/gradientButton'; +import { scale, scaleVertical } from '../../utils/scale'; + +export class Themes extends React.Component { + static navigationOptions = { + title: 'Theme'.toUpperCase(), + }; + + onLightThemeApplyButtonPressed = () => { + StatusBar.setBarStyle('dark-content', true); + if (Platform.OS === 'android') { + StatusBar.setBackgroundColor(KittenTheme.colors.screen.base); + } + RkTheme.setTheme(KittenTheme); + }; + + onDarkThemeApplyButtonPressed = () => { + StatusBar.setBarStyle('light-content', true); + if (Platform.OS === 'android') { + StatusBar.setBackgroundColor(DarkKittenTheme.colors.screen.base); + } + RkTheme.setTheme(DarkKittenTheme); + }; + + render = () => ( + <View style={styles.root}> + <View style={styles.container}> + <RkText>Light Theme</RkText> + <Image style={styles.image} source={require('../../assets/images/lightThemeImage.png')} /> + <GradientButton + text='APPLY' + onPress={this.onLightThemeApplyButtonPressed} + /> + </View> + <View style={styles.container}> + <RkText>Dark Theme</RkText> + <Image style={styles.image} source={require('../../assets/images/darkThemeImage.png')} /> + <GradientButton + text='APPLY' + onPress={this.onDarkThemeApplyButtonPressed} + /> + </View> + </View> + ); +} + +const styles = RkStyleSheet.create(theme => ({ + root: { + backgroundColor: theme.colors.screen.base, + flex: 1, + paddingHorizontal: scale(72), + + }, + image: { + height: scaleVertical(160), + }, + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'space-between', + paddingVertical: scaleVertical(20), + }, +})); diff --git a/frontend/app/screens/walkthroughs/index.js b/frontend/app/screens/walkthroughs/index.js new file mode 100644 index 0000000..e7f1eb0 --- /dev/null +++ b/frontend/app/screens/walkthroughs/index.js @@ -0,0 +1 @@ +export * from './walkthroughScreen'; diff --git a/frontend/app/screens/walkthroughs/walkthrough1.js b/frontend/app/screens/walkthroughs/walkthrough1.js new file mode 100644 index 0000000..2f08f97 --- /dev/null +++ b/frontend/app/screens/walkthroughs/walkthrough1.js @@ -0,0 +1,40 @@ +import React from 'react'; +import { + View, + Image, +} from 'react-native'; +import { + RkText, + RkStyleSheet, + RkTheme, +} from 'react-native-ui-kitten'; + +export class Walkthrough1 extends React.Component { + getThemeImageSource = (theme) => ( + theme.name === 'light' ? + require('../../assets/images/kittenImage.png') : require('../../assets/images/kittenImageDark.png') + ); + + renderImage = () => ( + <Image source={this.getThemeImageSource(RkTheme.current)} /> + ); + + render = () => ( + <View style={styles.screen}> + {this.renderImage()} + <RkText rkType='header2' style={styles.text}>Welcome to EcoAlerts</RkText> + </View> + ) +} + +const styles = RkStyleSheet.create(theme => ({ + screen: { + backgroundColor: theme.colors.screen.base, + alignItems: 'center', + justifyContent: 'center', + flex: 1, + }, + text: { + marginTop: 20, + }, +})); diff --git a/frontend/app/screens/walkthroughs/walkthrough2.js b/frontend/app/screens/walkthroughs/walkthrough2.js new file mode 100644 index 0000000..6e387b8 --- /dev/null +++ b/frontend/app/screens/walkthroughs/walkthrough2.js @@ -0,0 +1,46 @@ +import React from 'react'; +import { + View, + Image, + Dimensions, +} from 'react-native'; +import { + RkText, + RkStyleSheet, + RkTheme, +} from 'react-native-ui-kitten'; + +export class Walkthrough2 extends React.Component { + getThemeImageSource = (theme) => ( + theme.name === 'light' ? + require('../../assets/images/screensImage.png') : require('../../assets/images/screensImageDark.png') + ); + + renderImage = () => ( + <Image + style={{ width: Dimensions.get('window').width }} + source={this.getThemeImageSource(RkTheme.current)} + /> + ); + + render = () => ( + <View style={styles.screen}> + {this.renderImage()} + <RkText rkType='header2' style={styles.text}>Share info about ecological problems around!</RkText> + </View> + ) +} + +const styles = RkStyleSheet.create(theme => ({ + screen: { + backgroundColor: theme.colors.screen.base, + alignItems: 'center', + justifyContent: 'center', + flex: 1, + }, + text: { + textAlign: 'center', + marginTop: 20, + marginHorizontal: 30, + }, +})); diff --git a/frontend/app/screens/walkthroughs/walkthroughScreen.js b/frontend/app/screens/walkthroughs/walkthroughScreen.js new file mode 100644 index 0000000..f0c21b5 --- /dev/null +++ b/frontend/app/screens/walkthroughs/walkthroughScreen.js @@ -0,0 +1,61 @@ +import React from 'react'; +import { View } from 'react-native'; +import { RkStyleSheet } from 'react-native-ui-kitten'; +import { + GradientButton, + PaginationIndicator, +} from '../../components/'; +import { Walkthrough } from '../../components/walkthrough'; +import { Walkthrough1 } from './walkthrough1'; +import { Walkthrough2 } from './walkthrough2'; +import NavigationType from '../../config/navigation/propTypes'; + +export class WalkthroughScreen extends React.Component { + static propTypes = { + navigation: NavigationType.isRequired, + }; + static navigationOptions = { + header: null, + }; + + state = { + index: 0, + }; + + onWalkThroughIndexChanged = (index) => { + this.setState({ index }); + }; + + onStartButtonPressed = () => { + this.props.navigation.navigate('Articles2'); + }; + + render = () => ( + <View style={styles.screen}> + <Walkthrough onChanged={this.onWalkThroughIndexChanged}> + <Walkthrough1 /> + <Walkthrough2 /> + </Walkthrough> + <PaginationIndicator length={2} current={this.state.index} /> + <GradientButton + rkType='large' + style={styles.button} + text="GET STARTED" + onPress={this.onStartButtonPressed} + /> + </View> + ) +} + +const styles = RkStyleSheet.create(theme => ({ + screen: { + backgroundColor: theme.colors.screen.base, + paddingVertical: 28, + alignItems: 'center', + flex: 1, + }, + button: { + marginTop: 25, + marginHorizontal: 16, + }, +})); diff --git a/frontend/app/utils/scale.js b/frontend/app/utils/scale.js new file mode 100644 index 0000000..f754d48 --- /dev/null +++ b/frontend/app/utils/scale.js @@ -0,0 +1,13 @@ +import { Dimensions } from 'react-native'; + +const { width, height } = Dimensions.get('window'); + +// Guideline sizes are based on standard ~5" screen mobile device +const guidelineBaseWidth = 350; +const guidelineBaseHeight = 680; + +const scale = size => (width / guidelineBaseWidth) * size; +const scaleVertical = size => (height / guidelineBaseHeight) * size; +const scaleModerate = (size, factor = 0.5) => size + ((scale(size) - size) * factor); + +export { scale, scaleVertical, scaleModerate }; diff --git a/frontend/app/utils/textUtils.js b/frontend/app/utils/textUtils.js new file mode 100644 index 0000000..589f48f --- /dev/null +++ b/frontend/app/utils/textUtils.js @@ -0,0 +1,5 @@ +function formatNumber(num) { + return num > 999 ? `${(num / 1000).toFixed(1)}k` : num; +} + +export default formatNumber; |