From 148ea8e7c202d630b63c0408dbc7ad6eccb99c6e Mon Sep 17 00:00:00 2001 From: Nikolai R Kristiansen Date: Sun, 5 May 2019 12:13:55 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=A8=20Add=20prettier=20and=20eslint,?= =?UTF-8?q?=20then=20format?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc | 31 + .prettierrc | 6 + android/app/build.gradle | 11 +- package.json | 11 + src/App.js | 45 +- src/Dusken.js | 23 +- src/actions.js | 137 +++-- src/api.js | 264 ++++---- src/modules/about/About.js | 309 +++++----- src/modules/about/AboutContainer.js | 47 +- src/modules/events/EventDetail.js | 564 +++++++++--------- src/modules/events/EventList.js | 234 ++++---- src/modules/events/EventListContainer.js | 208 +++---- src/modules/login/Login.js | 303 +++++----- src/modules/login/LoginContainer.js | 38 +- src/modules/membership/Membership.js | 148 ++--- src/modules/membership/MembershipContainer.js | 133 +++-- src/modules/membership/Proof.js | 459 +++++++------- src/modules/membership/ProofContainer.js | 145 ++--- src/modules/register/UserRegister.js | 513 ++++++++-------- src/modules/register/UserRegisterContainer.js | 75 +-- src/navigation.js | 105 ++-- src/reducers.js | 214 +++---- src/reduxStore.js | 19 +- src/screens/AboutScreen.js | 27 +- src/screens/EventDetailScreen.js | 42 +- src/screens/EventListScreen.js | 36 +- src/screens/LoginScreen.js | 48 +- src/screens/MembershipScreen.js | 53 +- src/screens/UserRegisterScreen.js | 48 +- src/theme.js | 36 +- src/utils.js | 20 +- yarn.lock | 475 ++++++++++++++- 33 files changed, 2709 insertions(+), 2118 deletions(-) create mode 100644 .eslintrc create mode 100644 .prettierrc diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..8c3380c --- /dev/null +++ b/.eslintrc @@ -0,0 +1,31 @@ +{ + "extends": [ + "airbnb-base", + "plugin:prettier/recommended", + "plugin:react/recommended", + "plugin:import/errors", + "plugin:import/warnings", + ], + "rules": { + "no-plusplus": 0, + "no-console": 0, + "no-underscore-dangle": 0, + "no-param-reassign": 0, + "react/prefer-stateless-function": 1, + "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], + "react/sort-comp": 0, + "max-len": 0, + "no-use-before-define": ["error", { "functions": true, "classes": true, "variables": false }] // disable the rule for variables, but enable it for functions and classes + + }, + "plugins": [ + "react", + "prettier" + ], + "parser": "babel-eslint", + "settings": { + "react": { + "version": "16.8" + } + } +} diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..85a8c01 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "singleQuote": true, + "trailingComma": "es5", + "printWidth": 120, + "arrowParens": "always" +} diff --git a/android/app/build.gradle b/android/app/build.gradle index e8964bc..f41abc4 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -119,10 +119,13 @@ android { } signingConfigs { release { - storeFile file(DUSKEN_RELEASE_STORE_FILE) - storePassword DUSKEN_RELEASE_STORE_PASSWORD - keyAlias DUSKEN_RELEASE_KEY_ALIAS - keyPassword DUSKEN_RELEASE_KEY_PASSWORD + // These variables are in ~/.gradle/gradle.properties + if (project.hasProperty('DUSKEN_RELEASE_STORE_FILE')) { + storeFile file(DUSKEN_RELEASE_STORE_FILE) + storePassword DUSKEN_RELEASE_STORE_PASSWORD + keyAlias DUSKEN_RELEASE_KEY_ALIAS + keyPassword DUSKEN_RELEASE_KEY_PASSWORD + } } } buildTypes { diff --git a/package.json b/package.json index ea4c696..d881b15 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,9 @@ "android": "react-native run-android", "build:android": "cd android && ./gradlew assembleRelease", "emulator": "emulator -avd $(emulator -list-avds | head)&", + "format": "eslint --fix \"src/**/*.js\"", "ios": "react-native run-ios", + "lint:format": "eslint --quiet \"src/**/*.js\"", "release:android": "cd android && bundle exec fastlane beta", "release:ios": "cd ios && bundle exec fastlane beta", "start": "node node_modules/react-native/local-cli/cli.js start", @@ -44,10 +46,19 @@ "devDependencies": { "@babel/core": "^7.4.4", "@babel/runtime": "^7.4.4", + "babel-eslint": "^10.0.1", "babel-jest": "^24.7.1", + "eslint": "^5.16.0", + "eslint-config-airbnb": "^17.1.0", + "eslint-config-prettier": "^4.0.0", + "eslint-plugin-import": "^2.16.0", + "eslint-plugin-jsx-a11y": "^6.2.1", + "eslint-plugin-prettier": "^3.0.1", + "eslint-plugin-react": "^7.12.4", "jest": "^24.7.1", "jest-fetch-mock": "^1.6.5", "metro-react-native-babel-preset": "^0.54.0", + "prettier": "^1.17.0", "react-dom": "16.8.3", "react-test-renderer": "16.8.3" } diff --git a/src/App.js b/src/App.js index 81def68..4e3b4e3 100644 --- a/src/App.js +++ b/src/App.js @@ -1,31 +1,30 @@ -import React, {Component} from 'react'; -import {Provider} from 'react-redux'; -import {PersistGate} from 'redux-persist/es/integration/react' +import React, { Component } from 'react'; +import { Provider } from 'react-redux'; +import { PersistGate } from 'redux-persist/es/integration/react'; import Config from 'react-native-config'; -import {ApolloClient} from 'apollo-client'; -import {HttpLink} from 'apollo-link-http'; -import {InMemoryCache} from 'apollo-cache-inmemory'; -import {ApolloProvider} from "react-apollo"; - -import Dusken from "./Dusken"; -import {store, persistor} from './reduxStore'; +import { ApolloClient } from 'apollo-client'; +import { HttpLink } from 'apollo-link-http'; +import { InMemoryCache } from 'apollo-cache-inmemory'; +import { ApolloProvider } from 'react-apollo'; +import Dusken from './Dusken'; +import { store, persistor } from './reduxStore'; const apolloClient = new ApolloClient({ - link: new HttpLink({uri: `${Config.DUSKEN_API_URL}/api/graphql/`}), - cache: new InMemoryCache() + link: new HttpLink({ uri: `${Config.DUSKEN_API_URL}/api/graphql/` }), + cache: new InMemoryCache(), }); export default class App extends Component { - render() { - return ( - - - - - - - - ) - } + render() { + return ( + + + + + + + + ); + } } diff --git a/src/Dusken.js b/src/Dusken.js index c88f052..7744392 100644 --- a/src/Dusken.js +++ b/src/Dusken.js @@ -1,15 +1,16 @@ -import React, {Component} from 'react'; -import {connect} from "react-redux"; -import {Root} from 'native-base'; -import DuskenNavigation from "./navigation"; - +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import { Root } from 'native-base'; +import DuskenNavigation from './navigation'; class DuskenContainer extends Component { - render() { - return ( - - ) - } + render() { + return ( + + + + ); + } } -export default connect()(DuskenContainer); \ No newline at end of file +export default connect()(DuskenContainer); diff --git a/src/actions.js b/src/actions.js index 55f4c85..a344129 100644 --- a/src/actions.js +++ b/src/actions.js @@ -13,103 +13,116 @@ const MEMBERSHIP_CHARGE_SUCCESS = 'MEMBERSHIP_CHARGE_SUCCESS'; const MEMBERSHIP_CHARGE_FAILURE = 'MEMBERSHIP_CHARGE_FAILURE'; function loginRequest() { - return { - type: LOGIN_REQUEST - } + return { + type: LOGIN_REQUEST, + }; } function loginSuccess(username, token) { - return { - type: LOGIN_SUCCESS, - username, - token - } + return { + type: LOGIN_SUCCESS, + username, + token, + }; } function loginFailure(loginError) { - return { - type: LOGIN_FAILURE, - loginError - } + return { + type: LOGIN_FAILURE, + loginError, + }; } function logout() { - return { - type: LOGOUT - } + return { + type: LOGOUT, + }; } function userDataRequest() { - return { - type: USER_DATA_REQUEST - } + return { + type: USER_DATA_REQUEST, + }; } function userDataSuccess(data) { - return { - type: USER_DATA_SUCCESS, - data - } + return { + type: USER_DATA_SUCCESS, + data, + }; } function userDataFailure(userError) { - return { - type: USER_DATA_FAILURE, - userError - } + return { + type: USER_DATA_FAILURE, + userError, + }; } function registerUserRequest() { - return { - type: REGISTER_USER_REQUEST - } + return { + type: REGISTER_USER_REQUEST, + }; } function registerUserSuccess(data) { - return { - type: REGISTER_USER_SUCCESS, - data - } + return { + type: REGISTER_USER_SUCCESS, + data, + }; } function registerUserFailure(registerError) { - return { - type: REGISTER_USER_FAILURE, - registerError - } + return { + type: REGISTER_USER_FAILURE, + registerError, + }; } function membershipChargeRequest() { - return { - type: MEMBERSHIP_CHARGE_REQUEST - } + return { + type: MEMBERSHIP_CHARGE_REQUEST, + }; } function membershipChargeSuccess(data) { - return { - type: MEMBERSHIP_CHARGE_SUCCESS, - data - } + return { + type: MEMBERSHIP_CHARGE_SUCCESS, + data, + }; } function membershipChargeFailure(chargeError) { - return { - type: MEMBERSHIP_CHARGE_FAILURE, - chargeError - } + return { + type: MEMBERSHIP_CHARGE_FAILURE, + chargeError, + }; } export { - LOGIN_REQUEST, loginRequest, - LOGIN_SUCCESS, loginSuccess, - LOGIN_FAILURE, loginFailure, - LOGOUT, logout, - USER_DATA_REQUEST, userDataRequest, - USER_DATA_SUCCESS, userDataSuccess, - USER_DATA_FAILURE, userDataFailure, - REGISTER_USER_REQUEST, registerUserRequest, - REGISTER_USER_SUCCESS, registerUserSuccess, - REGISTER_USER_FAILURE, registerUserFailure, - MEMBERSHIP_CHARGE_REQUEST, membershipChargeRequest, - MEMBERSHIP_CHARGE_SUCCESS, membershipChargeSuccess, - MEMBERSHIP_CHARGE_FAILURE, membershipChargeFailure, -}; \ No newline at end of file + LOGIN_REQUEST, + loginRequest, + LOGIN_SUCCESS, + loginSuccess, + LOGIN_FAILURE, + loginFailure, + LOGOUT, + logout, + USER_DATA_REQUEST, + userDataRequest, + USER_DATA_SUCCESS, + userDataSuccess, + USER_DATA_FAILURE, + userDataFailure, + REGISTER_USER_REQUEST, + registerUserRequest, + REGISTER_USER_SUCCESS, + registerUserSuccess, + REGISTER_USER_FAILURE, + registerUserFailure, + MEMBERSHIP_CHARGE_REQUEST, + membershipChargeRequest, + MEMBERSHIP_CHARGE_SUCCESS, + membershipChargeSuccess, + MEMBERSHIP_CHARGE_FAILURE, + membershipChargeFailure, +}; diff --git a/src/api.js b/src/api.js index 3c13963..dd96f18 100644 --- a/src/api.js +++ b/src/api.js @@ -1,148 +1,154 @@ import Config from 'react-native-config'; import { - loginFailure, loginRequest, loginSuccess, - registerUserFailure, registerUserRequest, registerUserSuccess, - userDataFailure, userDataRequest, userDataSuccess, - membershipChargeFailure, membershipChargeRequest, membershipChargeSuccess, -} from "./actions"; -import { fetchWithTimeout } from "./utils"; + loginFailure, + loginRequest, + loginSuccess, + registerUserFailure, + registerUserRequest, + registerUserSuccess, + userDataFailure, + userDataRequest, + userDataSuccess, + membershipChargeFailure, + membershipChargeRequest, + membershipChargeSuccess, +} from './actions'; +import { fetchWithTimeout } from './utils'; export function requestLogin(username, password) { - return (dispatch) => { - // We are now logging in - dispatch(loginRequest()); + return (dispatch) => { + // We are now logging in + dispatch(loginRequest()); - return fetchWithTimeout(`${Config.DUSKEN_API_URL}/auth/obtain-token/`, { - method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - 'username': username, - 'password': password - }) - }) - .then(response => response.json().then(json => ({ json, response }))) - .then(({json, response}) => { - if (response.ok === false) { - return Promise.reject(json) - } - return json - }) - .then( - data => { - dispatch(loginSuccess(username, data.token)) - }, - data => { - console.log(data); - dispatch(loginFailure(data || {'non_field_errors': ['Kunne ikke logge inn, prøv igjen']})) - } - ) - } + return fetchWithTimeout(`${Config.DUSKEN_API_URL}/auth/obtain-token/`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + username, + password, + }), + }) + .then((response) => response.json().then((json) => ({ json, response }))) + .then(({ json, response }) => { + if (response.ok === false) { + return Promise.reject(json); + } + return json; + }) + .then( + (data) => { + dispatch(loginSuccess(username, data.token)); + }, + (data) => { + console.log(data); + dispatch(loginFailure(data || { non_field_errors: ['Kunne ikke logge inn, prøv igjen'] })); + } + ); + }; } export function requestUserData(auth_token) { - return (dispatch) => { - // We are now fetching user data - dispatch(userDataRequest()); + return (dispatch) => { + // We are now fetching user data + dispatch(userDataRequest()); - return fetchWithTimeout(`${Config.DUSKEN_API_URL}/api/me/`, { - method: 'GET', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - 'Authorization': `Token ${auth_token}` - } - }) - .then(response => response.json().then(json => ({ json, response }))) - .then(({json, response}) => { - if (response.ok === false) { - return Promise.reject(json) - } - return json - }) - .then( - data => { - dispatch(userDataSuccess(data)) - }, - data => { - dispatch(userDataFailure(data || {'non_field_errors': ['Kunne ikke hente brukerdata']})) - } - ) - } + return fetchWithTimeout(`${Config.DUSKEN_API_URL}/api/me/`, { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + Authorization: `Token ${auth_token}`, + }, + }) + .then((response) => response.json().then((json) => ({ json, response }))) + .then(({ json, response }) => { + if (response.ok === false) { + return Promise.reject(json); + } + return json; + }) + .then( + (data) => { + dispatch(userDataSuccess(data)); + }, + (data) => { + dispatch(userDataFailure(data || { non_field_errors: ['Kunne ikke hente brukerdata'] })); + } + ); + }; } export function requestRegisterUser(firstName, lastName, email, phoneNumber, password) { - return (dispatch) => { - dispatch(registerUserRequest()); + return (dispatch) => { + dispatch(registerUserRequest()); - return fetchWithTimeout(`${Config.DUSKEN_API_URL}/api/user/register/`, { - method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - 'first_name': firstName, - 'last_name': lastName, - 'email': email, - 'phone_number': phoneNumber, - 'password': password - }) - }) - .then(response => response.json().then(json => ({ json, response }))) - .then(({json, response}) => { - if (response.ok === false) { - return Promise.reject(json) - } - return json - }) - .then( - data => { - dispatch(registerUserSuccess(data)) - }, - data => { - dispatch(registerUserFailure(data || {'non_field_errors': ['Kunne ikke registrere bruker, prøv igjen']})) - } - ) - } + return fetchWithTimeout(`${Config.DUSKEN_API_URL}/api/user/register/`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + first_name: firstName, + last_name: lastName, + email, + phone_number: phoneNumber, + password, + }), + }) + .then((response) => response.json().then((json) => ({ json, response }))) + .then(({ json, response }) => { + if (response.ok === false) { + return Promise.reject(json); + } + return json; + }) + .then( + (data) => { + dispatch(registerUserSuccess(data)); + }, + (data) => { + dispatch(registerUserFailure(data || { non_field_errors: ['Kunne ikke registrere bruker, prøv igjen'] })); + } + ); + }; } export function requestMembershipCharge(auth_token, stripe_token, membership_type) { - return (dispatch) => { - dispatch(membershipChargeRequest()); + return (dispatch) => { + dispatch(membershipChargeRequest()); - return fetchWithTimeout(`${Config.DUSKEN_API_URL}/api/membership/charge/`, { - method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - 'Authorization': `Token ${auth_token}` - }, - body: JSON.stringify({ - 'stripe_token': stripe_token, - 'membership_type': membership_type, - 'payment_method': 'app', - }) - }) - .then(response => response.json().then(json => ({ json, response }))) - .then(({json, response}) => { - if (response.ok === false) { - return Promise.reject(json) - } - return json - }) - .then( - data => { - dispatch(membershipChargeSuccess(data)) - }, - data => { - dispatch(membershipChargeFailure(data['detail'] || 'Kunne ikke belaste bankkortet, prøv igjen')) - } - ) - } + return fetchWithTimeout(`${Config.DUSKEN_API_URL}/api/membership/charge/`, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + Authorization: `Token ${auth_token}`, + }, + body: JSON.stringify({ + stripe_token, + membership_type, + payment_method: 'app', + }), + }) + .then((response) => response.json().then((json) => ({ json, response }))) + .then(({ json, response }) => { + if (response.ok === false) { + return Promise.reject(json); + } + return json; + }) + .then( + (data) => { + dispatch(membershipChargeSuccess(data)); + }, + (data) => { + dispatch(membershipChargeFailure(data.detail || 'Kunne ikke belaste bankkortet, prøv igjen')); + } + ); + }; } - - diff --git a/src/modules/about/About.js b/src/modules/about/About.js index 8301c7d..3fcb7cd 100644 --- a/src/modules/about/About.js +++ b/src/modules/about/About.js @@ -1,156 +1,191 @@ import React, { Component } from 'react'; -import {Text, ScrollView, View, Platform, StyleSheet, Image, Linking, TouchableOpacity} from "react-native"; +import { Text, ScrollView, View, Platform, StyleSheet, Image, Linking, TouchableOpacity } from 'react-native'; // import HTMLView from 'react-native-htmlview'; -import {Button, Text as NBText, Icon} from 'native-base'; -import theme from "../../theme"; +import { Button, Text as NBText, Icon } from 'native-base'; +import theme from '../../theme'; const CHATEAU_NEUF_ADDRESS = 'Det Norske Studentersamfund, Slemdalsveien 15, 0369 Oslo'; export default class About extends Component { - /* FIXME: Values here are pretty hardcoded. Should fetch config from a JSON-file or API */ + /* FIXME: Values here are pretty hardcoded. Should fetch config from a JSON-file or API */ - openFacebook() { - /* Ref: https://stackoverflow.com/questions/4810803/open-facebook-page-from-android-app */ - const url = Platform.select({ - ios: 'fb://page/?id=23664032291', - android: 'fb://page/23664032291/' - }); - Linking.canOpenURL(url).then(supported => { - if (!supported) { - return Linking.openURL('https://www.facebook.com/studentersamfundet/'); - } else { - return Linking.openURL(url); - } - }).catch(err => console.error('An error occurred', err)); - } - openInstagram() { - /* Ref: */ - const url = Platform.select({ - ios: 'instagram://user?username=studentersamfundet', - android: 'http://instagram.com/_u/studentersamfundet' - }); + openFacebook() { + /* Ref: https://stackoverflow.com/questions/4810803/open-facebook-page-from-android-app */ + const url = Platform.select({ + ios: 'fb://page/?id=23664032291', + android: 'fb://page/23664032291/', + }); + Linking.canOpenURL(url) + .then((supported) => { + if (!supported) { + return Linking.openURL('https://www.facebook.com/studentersamfundet/'); + } + return Linking.openURL(url); + }) + .catch((err) => console.error('An error occurred', err)); + } - Linking.canOpenURL(url).then(supported => { - if (!supported) { - return Linking.openURL('https://www.instagram.com/studentersamfundet/'); - } else { - return Linking.openURL(url); - } - }).catch(err => console.error('An error occurred', err)); - } + openInstagram() { + /* Ref: */ + const url = Platform.select({ + ios: 'instagram://user?username=studentersamfundet', + android: 'http://instagram.com/_u/studentersamfundet', + }); - openMap() { - const url = Platform.select({ - ios: `comgooglemaps://?q=${CHATEAU_NEUF_ADDRESS}`, - android: `https://www.google.com/maps/search/?api=1&query=${CHATEAU_NEUF_ADDRESS}` - }); - Linking.canOpenURL(url).then(supported => { - if (!supported) { - return Linking.openURL(`https://www.google.com/maps/search/?api=1&query=${CHATEAU_NEUF_ADDRESS}`); - } else { - return Linking.openURL(url); - } - }).catch(err => console.error('An error occurred', err)); - } + Linking.canOpenURL(url) + .then((supported) => { + if (!supported) { + return Linking.openURL('https://www.instagram.com/studentersamfundet/'); + } + return Linking.openURL(url); + }) + .catch((err) => console.error('An error occurred', err)); + } - render() { - return ( - - - Det Norske Studentersamfund - Linking.openURL('https://www.studentersamfundet.no/')}> - - - Følg oss på - - - Facebook - - - Instagram - - + openMap() { + const url = Platform.select({ + ios: `comgooglemaps://?q=${CHATEAU_NEUF_ADDRESS}`, + android: `https://www.google.com/maps/search/?api=1&query=${CHATEAU_NEUF_ADDRESS}`, + }); + Linking.canOpenURL(url) + .then((supported) => { + if (!supported) { + return Linking.openURL(`https://www.google.com/maps/search/?api=1&query=${CHATEAU_NEUF_ADDRESS}`); + } + return Linking.openURL(url); + }) + .catch((err) => console.error('An error occurred', err)); + } - Adresse - Det Norske Studentersamfund{"\n"}Slemdalsveien 15, 0369 Oslo - - - - Åpningstider - - - - Ta kontakt - - - - - - - - + render() { + return ( + + + Det Norske Studentersamfund + Linking.openURL('https://www.studentersamfundet.no/')}> + + + Følg oss på + + + + Facebook + + + + Instagram + + - Chateau Neuf - - Studentene i Oslo har sitt naturlige tilholdssted på Det Norske Studentersamfund, i hyggelige lokaler på Chateau Neuf øverst på Majorstuen. Her er det åpent alle dager unntatt søndag, og hver dag skjer det en rekke ting i våre mange arrangementslokaler. Enten man ønsker en tur i baren, på kafé, på debatt, på konsert, teater eller kino, har man muligheten på Chateau Neuf. - Chateau Neuf er et frivillig drevet studenthus. Studenter over alt i Oslo kan komme hit og få nyttig og spennende erfaring med å drive et av Oslos største kulturhus. Her kan du stå i bar, sette lys og lyd, jobbe med markedsføring, produsere konserter, bli kjent med hundrevis av bra mennesker og få minner for livet. - Alle studentforeninger i Oslo kan arrangere gratis på Chateau Neuf. Skal du arrangere en fest, sette en konsert, holde et foredrag eller gjennomføre et møte, har vi lokaler som kan brukes til dette. + Adresse + + Det Norske Studentersamfund{'\n'}Slemdalsveien 15, 0369 Oslo + + + + + Åpningstider + + + + Ta kontakt + + + + + + + + - {/**/} - - - ) - } + Chateau Neuf + + + Studentene i Oslo har sitt naturlige tilholdssted på Det Norske Studentersamfund, i hyggelige lokaler på + Chateau Neuf øverst på Majorstuen. Her er det åpent alle dager unntatt søndag, og hver dag skjer det en + rekke ting i våre mange arrangementslokaler. Enten man ønsker en tur i baren, på kafé, på debatt, på + konsert, teater eller kino, har man muligheten på Chateau Neuf. + + + Chateau Neuf er et frivillig drevet studenthus. Studenter over alt i Oslo kan komme hit og få nyttig og + spennende erfaring med å drive et av Oslos største kulturhus. Her kan du stå i bar, sette lys og lyd, jobbe + med markedsføring, produsere konserter, bli kjent med hundrevis av bra mennesker og få minner for livet. + + + Alle studentforeninger i Oslo kan arrangere gratis på Chateau Neuf. Skal du arrangere en fest, sette en + konsert, holde et foredrag eller gjennomføre et møte, har vi lokaler som kan brukes til dette. + + + {/* */} + + + ); + } } - const styles = StyleSheet.create({ - header: { - fontSize: 20, - fontWeight: 'bold', - color: '#000', - textAlign: 'center', - }, - paragraph: { - fontSize: 16, - color: '#000', - paddingBottom: 10, - lineHeight: 25, - }, - card: theme.card, - featuredImage: { - height: 230, - width: null, - }, - logo: { - height: 128, - width: null, - marginBottom: 10, - }, - icon: { - fontSize: 16, - }, - largeIcon: { - fontSize: 40, - }, - section: { - marginTop: 8, - marginBottom: 20, - alignSelf: 'center', - }, - someBtn: { - flex: 1, - alignItems: 'center', - } + header: { + fontSize: 20, + fontWeight: 'bold', + color: '#000', + textAlign: 'center', + }, + paragraph: { + fontSize: 16, + color: '#000', + paddingBottom: 10, + lineHeight: 25, + }, + card: theme.card, + featuredImage: { + height: 230, + width: null, + }, + logo: { + height: 128, + width: null, + marginBottom: 10, + }, + icon: { + fontSize: 16, + }, + largeIcon: { + fontSize: 40, + }, + section: { + marginTop: 8, + marginBottom: 20, + alignSelf: 'center', + }, + someBtn: { + flex: 1, + alignItems: 'center', + }, }); // const HTMLStyles = StyleSheet.create({ @@ -165,4 +200,4 @@ const styles = StyleSheet.create({ // marginTop: -20, // marginBottom: -30, // }, -// }); \ No newline at end of file +// }); diff --git a/src/modules/about/AboutContainer.js b/src/modules/about/AboutContainer.js index 7f4aaa4..dd811ce 100644 --- a/src/modules/about/AboutContainer.js +++ b/src/modules/about/AboutContainer.js @@ -1,45 +1,12 @@ -import {connect} from "react-redux"; -import React, {Component} from 'react'; -import Config from 'react-native-config'; +import { connect } from 'react-redux'; +import React, { Component } from 'react'; -import About from "./About"; +import About from './About'; class AboutContainer extends Component { - // constructor(props) { - // super(props); - // this.state = { - // openingHours: '', - // loading: false, - // error: null, - // }; - // } - // - // componentDidMount() { - // this.fetchOpeningHours(); - // } - // - // fetchOpeningHours() { - // const url = `${Config.EVENT_API_URL}/wp-json/wp/v2/pages/?slug=aapningstider`; - // this.setState({ loading: true }); - // - // fetch(url) - // .then(res => res.json()) - // .then(res => { - // this.setState({ - // openingHours: res ? res[0].content.rendered : '', - // error: res.error || null, - // loading: false, - // }); - // - // }) - // .catch(error => { - // this.setState({ error, loading: false }); - // }); - // } - - render() { - return ; - } + render() { + return ; + } } -export default connect()(AboutContainer); \ No newline at end of file +export default connect()(AboutContainer); diff --git a/src/modules/events/EventDetail.js b/src/modules/events/EventDetail.js index c27cd21..9426020 100644 --- a/src/modules/events/EventDetail.js +++ b/src/modules/events/EventDetail.js @@ -1,305 +1,325 @@ import React, { Component } from 'react'; -import {Linking, StyleSheet, ScrollView, Text, Platform, View, Image, TouchableOpacity} from "react-native"; +import { Linking, StyleSheet, ScrollView, Text, Platform, View, Image, TouchableOpacity } from 'react-native'; import HTMLView from 'react-native-htmlview'; -import moment from "moment"; -import {Icon} from "native-base"; -import theme from "../../theme"; +import moment from 'moment'; +import { Icon } from 'native-base'; +import theme from '../../theme'; moment.locale('nb'); const CHATEAU_NEUF_ADDRESS = 'Chateau Neuf, Slemdalsveien 15, 0369 Oslo'; export default class EventDetail extends Component { + _formatTime(time) { + return moment(time).format('HH:mm'); + } - _formatTime(time) { - return moment(time).format('HH:mm'); + _formatMonth(time) { + return moment(time).format('MMM'); + } + + _formatDay(time) { + return moment(time).format('DD.'); + } + + _formatLocalDay(time) { + return moment(time).format('ddd'); + } + + _formatYear(time) { + return moment(time).format('YYYY'); + } + + _formatRelative(time) { + return moment(time).fromNow(); + } + + _formatLocation() { + if (this.props.item.venue !== 'Annetsteds') { + return CHATEAU_NEUF_ADDRESS; + } + return 'Ukjent adresse'; + } + + _formatTicketText() { + /* FIXME: This should be handled by API */ + const { item } = this.props; + let reg_kr = ''; + let mem_kr = ''; + if (item.price_regular === '0') { + item.price_regular = 'Gratis'; + } + if (item.price_member === '0') { + item.price_member = 'Gratis'; + } + if (item.price_regular !== 'Gratis') { + reg_kr = ' kr'; + } + if (item.price_member !== 'Gratis') { + mem_kr = ' kr'; } - _formatMonth(time) { - return moment(time).format('MMM'); + let text = ''; + if (item.price_regular && item.price_member) { + text = `Pris: ${item.price_regular}${reg_kr} / Medlemmer: ${item.price_member}${mem_kr}`; + } else if (item.price_regular) { + text = `Pris: ${item.price_regular}${reg_kr}`; + } else if (item.price_member) { + text = `Pris (Medlemmer): ${item.price_member}${mem_kr}`; + } else { + return ''; } + return text; + } - _formatDay(time) { - return moment(time).format('DD.'); - } - _formatLocalDay(time) { - return moment(time).format('ddd') - } + onTicketUrlPress(link) { + Linking.openURL(link); + } - _formatYear(time) { - return moment(time).format('YYYY'); + onFacebookUrlPress(link) { + const fbEventPattern = /https?:\/\/www\.facebook\.com\/events\/(\d+)\/?/i; + const matches = fbEventPattern.exec(link); + let iosURL = link; + if (matches && matches.length) { + iosURL = `fb://event/?id=${matches[1]}`; } - - _formatRelative(time) { - return moment(time).fromNow(); - } - _formatLocation() { - if(this.props.item.venue !== 'Annetsteds') { - return CHATEAU_NEUF_ADDRESS; + const url = Platform.select({ + ios: iosURL, + android: link, + }); + Linking.canOpenURL(url) + .then((supported) => { + if (!supported) { + return Linking.openURL(link); } - return 'Ukjent adresse' + return Linking.openURL(url); + }) + .catch((err) => console.error('An error occurred', err)); + } + + onLocationPress(venue) { + if (venue !== 'Annetsteds') { + Linking.openURL(`https://www.google.com/maps/dir/?api=1&destination=${CHATEAU_NEUF_ADDRESS}`); + } + } + + showPriceAndTicket() { + const { item } = this.props; + const text = this._formatTicketText(); + + if (text === '') { + return; } - _formatTicketText() { - /* FIXME: This should be handled by API */ - const {item} = this.props; - let reg_kr = ''; - let mem_kr = ''; - if (item.price_regular === '0') { - item.price_regular = 'Gratis' - } - if (item.price_member === '0') { - item.price_member = 'Gratis' - } - if (item.price_regular !== 'Gratis') { - reg_kr = " kr" - } - if (item.price_member !== 'Gratis') { - mem_kr = " kr" - } - - let text = ''; - if (item.price_regular && item.price_member) { - text = `Pris: ${item.price_regular}${reg_kr} / Medlemmer: ${item.price_member}${mem_kr}`; - } - else if (item.price_regular) { - text = `Pris: ${item.price_regular}${reg_kr}`; - } - else if (item.price_member) { - text = `Pris (Medlemmer): ${item.price_member}${mem_kr}`; - } else { - return ''; - } - return text; + if (item.ticket_url) { + return ( + this.onTicketUrlPress(item.ticket_url)}> + + + + + {text} + Kjøp billett + + + ); } - onTicketUrlPress(link) { - Linking.openURL(link); + return ( + + + + + + {text} + Billett i døra + + + ); + } + + showFacebutton() { + const { item } = this.props; + if (!item.facebook_url) { + return; } - onFacebookUrlPress(link) { - const fbEventPattern = /https?:\/\/www\.facebook\.com\/events\/(\d+)\/?/i; - const matches = fbEventPattern.exec(link); - let iosURL = link; - if( matches && matches.length ) { - iosURL = 'fb://event/?id=' + matches[1]; - } - const url = Platform.select({ - ios: iosURL, - android: link - }); - Linking.canOpenURL(url).then(supported => { - if (!supported) { - return Linking.openURL(link); - } else { - return Linking.openURL(url); - } - }).catch(err => console.error('An error occurred', err)); + return ( + this.onFacebookUrlPress(item.facebook_url)} style={styles.metaInner}> + + + + + På Facebook + + + ); + } + + showImage() { + const { item } = this.props; + + if (item.thumbnail.medium_large) { + return ; } + } - onLocationPress(venue) { - if( venue !== 'Annetsteds' ) { - Linking.openURL(`https://www.google.com/maps/dir/?api=1&destination=${CHATEAU_NEUF_ADDRESS}`); - } + showYear() { + const { item } = this.props; + + if (moment().year() !== moment(item.start_time).year()) { + return {this._formatYear(item.start_time)}; } + } - showPriceAndTicket() { - const {item} = this.props; - const text = this._formatTicketText(); + render() { + const { item } = this.props; - if(text === '') { - return; - } - - if(item.ticket_url) { - return ( - this.onTicketUrlPress(item.ticket_url)}> - - - {text} - Kjøp billett - - ); - } - - return ( - - - - {text} - Billett i døra - - ) - } - - showFacebutton() { - const {item} = this.props; - if (!item.facebook_url) { return;} - - return ( - this.onFacebookUrlPress(item.facebook_url)} style={styles.metaInner}> - - - På Facebook - - - ); - } - - showImage() { - const {item} = this.props; - - if (item.thumbnail.medium_large) { - return ; - } - } - - showYear() { - const {item} = this.props; - - if(moment().year() !== moment(item.start_time).year()) { - return {this._formatYear(item.start_time)} - } - } - - render() { - const {item} = this.props; - - return ( - - {this.showImage()} - - {/* Date and title */} - - - - {this._formatDay(item.start_time)} - {this._formatMonth(item.start_time)} - {this.showYear()} - - - - {item.title.decoded} - - - {/* Time and calendar */} - - - - {this._formatLocalDay(item.start_time)} kl. {this._formatTime(item.start_time)} - {this._formatTime(item.end_time)} - {this._formatRelative(item.start_time)} - - - {/* Location and maps */} - this.onLocationPress(item.venue)}> - - - {item.venue} - {this._formatLocation()} - - - {this.showPriceAndTicket()} - {this.showFacebutton()} - - - - ) - } + return ( + + {this.showImage()} + + {/* Date and title */} + + + + {this._formatDay(item.start_time)} + {this._formatMonth(item.start_time)} + {this.showYear()} + + + + + {item.title.decoded} + + + + {/* Time and calendar */} + + + + + + + {this._formatLocalDay(item.start_time)} kl. {this._formatTime(item.start_time)} -{' '} + {this._formatTime(item.end_time)} + + {this._formatRelative(item.start_time)} + + + {/* Location and maps */} + this.onLocationPress(item.venue)}> + + + + + {item.venue} + + {this._formatLocation()} + + + + {this.showPriceAndTicket()} + {this.showFacebutton()} + + + + ); + } } const styles = StyleSheet.create({ - dateAndTitle: { - flexDirection: 'row', - marginBottom: 8 - }, - titleContainer: { - paddingLeft: 10, - justifyContent: 'center', - flex: 4, - }, - title: { - fontSize: 20, - fontWeight: 'bold', - color: '#000', - }, - card: theme.card, - image: { - height: 260, - width: null - }, - time: { - fontSize: 14, - color: '#666' - }, - metaInner: { - flexDirection: 'row', - marginVertical: 8, - }, - metaIcon: { - width: 16, - paddingTop: 3, - marginHorizontal: 8, - marginVertical: 8, - }, - metaText: { - color: '#000', - }, - metaSubtitle: { - color: '#666', - }, - content: { - borderTopWidth: StyleSheet.hairlineWidth, - borderColor: '#ccc', - marginTop: 8, - paddingTop: 10, - }, - dateContainer: { - flex: 1, - justifyContent: 'center', - }, - dateCard: { - margin: 0, - padding: 0, - paddingBottom: 6, - width: 62, - }, - day: { - paddingTop: 5, - fontSize: 20, - color: '#000', - textAlign: 'center', - }, - month: { - textAlign: 'center', - paddingHorizontal: 16, - }, - year: { - fontSize: 9, - color: '#fff', - backgroundColor: '#f58220', - textAlign: 'center', - paddingRight: 20, - paddingBottom: 3, - paddingLeft: 20, - paddingTop: 3, - }, - icons: { - fontSize: 16, - }, - + dateAndTitle: { + flexDirection: 'row', + marginBottom: 8, + }, + titleContainer: { + paddingLeft: 10, + justifyContent: 'center', + flex: 4, + }, + title: { + fontSize: 20, + fontWeight: 'bold', + color: '#000', + }, + card: theme.card, + image: { + height: 260, + width: null, + }, + time: { + fontSize: 14, + color: '#666', + }, + metaInner: { + flexDirection: 'row', + marginVertical: 8, + }, + metaIcon: { + width: 16, + paddingTop: 3, + marginHorizontal: 8, + marginVertical: 8, + }, + metaText: { + color: '#000', + }, + metaSubtitle: { + color: '#666', + }, + content: { + borderTopWidth: StyleSheet.hairlineWidth, + borderColor: '#ccc', + marginTop: 8, + paddingTop: 10, + }, + dateContainer: { + flex: 1, + justifyContent: 'center', + }, + dateCard: { + margin: 0, + padding: 0, + paddingBottom: 6, + width: 62, + }, + day: { + paddingTop: 5, + fontSize: 20, + color: '#000', + textAlign: 'center', + }, + month: { + textAlign: 'center', + paddingHorizontal: 16, + }, + year: { + fontSize: 9, + color: '#fff', + backgroundColor: '#f58220', + textAlign: 'center', + paddingRight: 20, + paddingBottom: 3, + paddingLeft: 20, + paddingTop: 3, + }, + icons: { + fontSize: 16, + }, }); -//FIXME: Ninja margin +// FIXME: Ninja margin const HTMLStyles = StyleSheet.create({ - p: { - fontSize: 16, - marginBottom: -30, - color: '#000', - }, - ul: { - fontSize: 16, - color: '#000', - marginTop: -20, - marginBottom: -30, - }, -}); \ No newline at end of file + p: { + fontSize: 16, + marginBottom: -30, + color: '#000', + }, + ul: { + fontSize: 16, + color: '#000', + marginTop: -20, + marginBottom: -30, + }, +}); diff --git a/src/modules/events/EventList.js b/src/modules/events/EventList.js index 3b7921c..57cd24c 100644 --- a/src/modules/events/EventList.js +++ b/src/modules/events/EventList.js @@ -1,122 +1,144 @@ -import {StyleSheet, View, Platform} from "react-native"; -import {SectionList} from 'react-navigation'; -import {Card, ListItem, CardItem, Body, Text as Text, Left, Right, Icon, Button, Content, Spinner} from 'native-base'; +import { StyleSheet, View, Platform } from 'react-native'; +import { SectionList } from 'react-navigation'; +import { Card, ListItem, CardItem, Body, Text, Left, Right, Icon, Button, Content, Spinner } from 'native-base'; import React, { Component } from 'react'; -import moment from "moment"; +import moment from 'moment'; import 'moment/locale/nb'; -import theme from "../../theme"; +import theme from '../../theme'; moment.locale('nb'); export default class EventList extends Component { - _renderItem = ({item}) => ( - { this._onPressItem(item); }} style={styles.listItem}> - - {item.title.decoded} - {this._formatTime(item.start_time)} - - - - - + _renderItem = ({ item }) => ( + { + this._onPressItem(item); + }} + style={styles.listItem} + > + + + {item.title.decoded} + + {this._formatTime(item.start_time)} + + + + + + ); + + _renderSectionHeader = ({ section }) => { + return ( + + + {section.title} + + ); + }; - _renderSectionHeader = ({section}) => { - return ( - - {section.title} - - ); - }; - - _renderFooter = () => { - if(!this.props.loading) { - return null; - } - - return ( - - ); - }; - - _onPressItem = (item) => { - this.props.showEvent(item); - }; - - - _formatTime(time) { - return moment(time).format('llll'); + _renderFooter = () => { + if (!this.props.loading) { + return null; } - render() { - if (this.props.error) { - return ( - - { this._onPressItem(item); }}> - Kunne ikke hente programmet... - - - - - - ) - } + return ( + + + + ); + }; - return ( item.id} - refreshing={this.props.refreshing} - onRefresh={this.props.handleRefresh} - onEndReached={this.props.handleLoadMore} - onEndReachedThreshould={10} - initialNumToRender={10} - ListFooterComponent={this._renderFooter} - style={[styles.card, styles.list]} - ListHeaderComponent={() => { return }} - />); + _onPressItem = (item) => { + this.props.showEvent(item); + }; + + _formatTime(time) { + return moment(time).format('llll'); + } + + render() { + if (this.props.error) { + return ( + + + { + this._onPressItem(item); + }} + > + Kunne ikke hente programmet... + + + + + + + ); } + + return ( + item.id} + refreshing={this.props.refreshing} + onRefresh={this.props.handleRefresh} + onEndReached={this.props.handleLoadMore} + onEndReachedThreshould={10} + initialNumToRender={10} + ListFooterComponent={this._renderFooter} + style={[styles.card, styles.list]} + ListHeaderComponent={() => { + return ; + }} + /> + ); + } } - const styles = StyleSheet.create({ - listItem: { - backgroundColor: 'white', - marginLeft: 0 - }, - listItemTitle: { - fontSize: 14, - }, - listItemTime: { - fontSize: 14, - color: '#666' - }, - sectionHeader: { - backgroundColor: 'white', - marginLeft: 0, - ...Platform.select({ - 'ios': {marginBottom: -8}, - }), - paddingBottom: 0, - borderBottomWidth: 0, - }, - sectionTitle: { - fontSize: 16, - fontWeight: 'bold', - }, - loadingText: { - textAlign: 'center', - paddingBottom: 8 - }, - card: theme.card, - list: { - padding: 0, - marginTop: 0, - marginBottom: 0, - backgroundColor: 'transparent', - }, - listHeader: { - paddingTop: 8, - } + listItem: { + backgroundColor: 'white', + marginLeft: 0, + }, + listItemTitle: { + fontSize: 14, + }, + listItemTime: { + fontSize: 14, + color: '#666', + }, + sectionHeader: { + backgroundColor: 'white', + marginLeft: 0, + ...Platform.select({ + ios: { marginBottom: -8 }, + }), + paddingBottom: 0, + borderBottomWidth: 0, + }, + sectionTitle: { + fontSize: 16, + fontWeight: 'bold', + }, + loadingText: { + textAlign: 'center', + paddingBottom: 8, + }, + card: theme.card, + list: { + padding: 0, + marginTop: 0, + marginBottom: 0, + backgroundColor: 'transparent', + }, + listHeader: { + paddingTop: 8, + }, }); diff --git a/src/modules/events/EventListContainer.js b/src/modules/events/EventListContainer.js index 8fda34a..680cc03 100644 --- a/src/modules/events/EventListContainer.js +++ b/src/modules/events/EventListContainer.js @@ -1,119 +1,127 @@ -import {connect} from "react-redux"; +import { connect } from 'react-redux'; -import React, {Component} from 'react'; +import React, { Component } from 'react'; import Config from 'react-native-config'; -import moment from "moment"; +import moment from 'moment'; import 'moment/locale/nb'; -import EventList from "./EventList"; +import EventList from './EventList'; moment.locale('nb'); /* Ref: https://medium.com/react-native-development/how-to-use-the-flatlist-component-react-native-basics-92c482816fe6 */ class EventListContainer extends Component { - constructor(props) { - super(props); + constructor(props) { + super(props); - this.state = { - loading: false, - events: [], - eventsSectioned: [], - page: 1, - totalPages: 0, - error: null, - refreshing: false, - }; - } - - _formatDate(time) { - const m = moment(time); - if(moment().year() === m.year() ) { - return m.format('dddd D. MMM') - } - return m.format('ll'); - } - - _toSectionFormat(events) { - let sectioned = {}; - events.map((event) => { - const title = this._formatDate(event.start_time); - if( title in sectioned ) { - sectioned[title].push(event); - } else { - sectioned[title] = [event]; - } - }); - - let sectionedList = []; - Object.entries(sectioned).forEach(([title, section]) => { - sectionedList.push({data: section, title: title}); - }); - - return sectionedList; - } - - handleRefresh = () => { - this.setState({ - page: 1, - refreshing: true, - totalPages: 1, - }, () => { - this.fetchEvents() - }); + this.state = { + loading: false, + events: [], + eventsSectioned: [], + page: 1, + totalPages: 0, + error: null, + refreshing: false, }; + } - handleLoadMore = () => { - if(this.state.page >= this.state.totalPages || this.state.loading) { - return; - } - this.setState({ - page: this.state.page + 1, - }, () => { - this.fetchEvents(); - }) - }; - - fetchEvents() { - const { page } = this.state; - const url = `${Config.EVENT_API_URL}/wp-json/wp/v2/events?page=${page}&future=1`; - this.setState({ loading: true }); - - fetch(url) - .then(res => { - this.setState({totalPages: parseInt(res.headers.get('x-wp-totalpages'))}); - return res.json(); - }) - .then(res => { - this.setState({ - events: page === 1 ? res : [...this.state.events, ...res], - eventsSectioned: page === 1 ? this._toSectionFormat(res) : this._toSectionFormat([...this.state.events, ...res]), - error: res.error || null, - loading: false, - refreshing: false - }); - - }) - .catch(error => { - this.setState({ error, loading: false }); - }); + _formatDate(time) { + const m = moment(time); + if (moment().year() === m.year()) { + return m.format('dddd D. MMM'); } + return m.format('ll'); + } - componentDidMount() { + _toSectionFormat(events) { + const sectioned = {}; + events.map((event) => { + const title = this._formatDate(event.start_time); + if (title in sectioned) { + sectioned[title].push(event); + } else { + sectioned[title] = [event]; + } + }); + + const sectionedList = []; + Object.entries(sectioned).forEach(([title, section]) => { + sectionedList.push({ data: section, title }); + }); + + return sectionedList; + } + + handleRefresh = () => { + this.setState( + { + page: 1, + refreshing: true, + totalPages: 1, + }, + () => { this.fetchEvents(); - } + } + ); + }; - showEvent = (item) => { - this.props.showEvent(item); - }; - - render() { - return () + handleLoadMore = () => { + if (this.state.page >= this.state.totalPages || this.state.loading) { + return; } + this.setState( + { + page: this.state.page + 1, + }, + () => { + this.fetchEvents(); + } + ); + }; + + fetchEvents() { + const { page } = this.state; + const url = `${Config.EVENT_API_URL}/wp-json/wp/v2/events?page=${page}&future=1`; + this.setState({ loading: true }); + + fetch(url) + .then((res) => { + this.setState({ totalPages: parseInt(res.headers.get('x-wp-totalpages')) }); + return res.json(); + }) + .then((res) => { + this.setState({ + events: page === 1 ? res : [...this.state.events, ...res], + eventsSectioned: + page === 1 ? this._toSectionFormat(res) : this._toSectionFormat([...this.state.events, ...res]), + error: res.error || null, + loading: false, + refreshing: false, + }); + }) + .catch((error) => { + this.setState({ error, loading: false }); + }); + } + + componentDidMount() { + this.fetchEvents(); + } + + showEvent = (item) => { + this.props.showEvent(item); + }; + + render() { + return ( + + ); + } } -export default connect()(EventListContainer); \ No newline at end of file +export default connect()(EventListContainer); diff --git a/src/modules/login/Login.js b/src/modules/login/Login.js index 90c2ed2..0f945da 100644 --- a/src/modules/login/Login.js +++ b/src/modules/login/Login.js @@ -1,162 +1,173 @@ -import React, {Component} from 'react'; -import {Linking, StyleSheet, View, Platform} from 'react-native'; -import {Container, Header, Content, Form, Item, Input, Label, Spinner, Button, Text, - Icon} from 'native-base'; +import React, { Component } from 'react'; +import { Linking, StyleSheet, View, Platform } from 'react-native'; +import { Container, Header, Content, Form, Item, Input, Label, Spinner, Button, Text, Icon } from 'native-base'; import Config from 'react-native-config'; -import theme from "../../theme"; +import theme from '../../theme'; export default class Login extends Component { - constructor(props) { - super(props); - this.state = { - email: '', - password: '' - } - } + constructor(props) { + super(props); + this.state = { + email: '', + password: '', + }; + } - emailInput() { - if (this.props.loginError && this.props.loginError.username) { - return - - { - this.refs.passwordInput._root.focus() - }} - /> - ; - } - - return - - { - this.refs.passwordInput._root.focus() - }} - /> - ; - } - - passwordInput() { - if (this.props.loginError && this.props.loginError.password) { - return - - - - } - return - - + emailInput() { + if (this.props.loginError && this.props.loginError.username) { + return ( + + + { + this.refs.passwordInput._root.focus(); + }} + /> + ); } - render() { - return ( - - -
- {this.emailInput()} - {this.passwordInput()} - {this.showError()} - - - {this.showSpinner()} -
-
-
- ); + return ( + + + { + this.refs.passwordInput._root.focus(); + }} + /> + + ); + } + + passwordInput() { + if (this.props.loginError && this.props.loginError.password) { + return ( + + + + + ); + } + return ( + + + + + ); + } + + render() { + return ( + + +
+ {this.emailInput()} + {this.passwordInput()} + {this.showError()} + + + {this.showSpinner()} +
+
+
+ ); + } + + showError = () => { + if (this.props.loginError) { + const err = this.props.loginError.non_field_errors; + const errorFormatted = err ? err[0] : 'Feil brukernavn eller passord, prøv igjen...'; + // TODO: Format these and highlight error fields + return ( + + {errorFormatted} + + ); } - showError = () => { - if (this.props.loginError) { - const err = this.props.loginError.non_field_errors; - let errorFormatted = err ? err[0] : 'Feil brukernavn eller passord, prøv igjen...'; - // TODO: Format these and highlight error fields - return {errorFormatted} - } - else { - return - } - }; + return ; + }; - showSpinner = () => { - if (this.props.isLoggingIn) { - return - } - }; + showSpinner = () => { + if (this.props.isLoggingIn) { + return ; + } + }; - handleEmail = (text) => { - this.setState({email: text}) - }; + handleEmail = (text) => { + this.setState({ email: text }); + }; - handlePassword = (text) => { - this.setState({password: text}) - }; - - onLoginPress = () => { - this.props.requestLogin(this.state.email, this.state.password) - }; + handlePassword = (text) => { + this.setState({ password: text }); + }; + onLoginPress = () => { + this.props.requestLogin(this.state.email, this.state.password); + }; } const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - }, - input: { - width: 320, - fontSize: 20, - height: 52, - }, - loginButton: { - marginBottom: 16, - }, - forgotPasswordButton: { - alignSelf: 'center', - marginTop: 32, - marginBottom: 16, - }, - errorBox: { - marginVertical: 10, - }, - errorMessage: { - textAlign: 'center', - color: 'red', - }, - card: theme.card, -}); \ No newline at end of file + container: { + flex: 1, + justifyContent: 'center', + }, + input: { + width: 320, + fontSize: 20, + height: 52, + }, + loginButton: { + marginBottom: 16, + }, + forgotPasswordButton: { + alignSelf: 'center', + marginTop: 32, + marginBottom: 16, + }, + errorBox: { + marginVertical: 10, + }, + errorMessage: { + textAlign: 'center', + color: 'red', + }, + card: theme.card, +}); diff --git a/src/modules/login/LoginContainer.js b/src/modules/login/LoginContainer.js index 181d17a..49d11dc 100644 --- a/src/modules/login/LoginContainer.js +++ b/src/modules/login/LoginContainer.js @@ -1,27 +1,27 @@ -import React, {Component} from 'react'; -import {connect} from "react-redux"; -import Login from "./Login"; -import {requestLogin} from "../../api"; +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import Login from './Login'; +import { requestLogin } from '../../api'; class LoginContainer extends Component { - componentWillReceiveProps(nextProps) { - if (this.props.isAuthenticated !== nextProps.isAuthenticated) { - if (nextProps.isAuthenticated) { - this.props.onLogin(); - } - } + componentWillReceiveProps(nextProps) { + if (this.props.isAuthenticated !== nextProps.isAuthenticated) { + if (nextProps.isAuthenticated) { + this.props.onLogin(); + } } + } - render() { - return - } + render() { + return ; + } } export default connect( - (store) => ({ - isAuthenticated: store.isAuthenticated, - isLoggingIn: store.isLoggingIn, - loginError: store.loginError, - }), - {requestLogin} + (store) => ({ + isAuthenticated: store.isAuthenticated, + isLoggingIn: store.isLoggingIn, + loginError: store.loginError, + }), + { requestLogin } )(LoginContainer); diff --git a/src/modules/membership/Membership.js b/src/modules/membership/Membership.js index dba79e2..a95be92 100644 --- a/src/modules/membership/Membership.js +++ b/src/modules/membership/Membership.js @@ -1,80 +1,82 @@ -import React, {Component} from 'react'; -import {ScrollView, Text, View, StyleSheet} from "react-native"; -import {Button, Text as NBText, Spinner} from "native-base"; - -import theme from "../../theme"; +import React, { Component } from 'react'; +import { ScrollView, Text, View, StyleSheet } from 'react-native'; +import { Button, Text as NBText, Spinner } from 'native-base'; +import theme from '../../theme'; export default class Membership extends Component { - render() { - if (this.props.isFetchingUserData) { - return ( - - - - ); - } - - return ( - - - Hei 👋 - Her kan du bli medlem i Det Norske Studentersamfund - Chateau Neuf og ha medlemsbeviset ditt på telefonen - - - - Hva får jeg? - - - Minst 25% rabatt på de fleste arrangement{"\n"} - - Gratis inngang på enkelte arrangement{"\n"} - - Store rabatter i barene{"\n"} - - Egne medlemsarrangement og -tilbud{"\n"} - - Mulighet til å delta i landets eldste studentdemokrati - Allerede medlem? - - - - - - ) - }; + render() { + if (this.props.isFetchingUserData) { + return ( + + + + ); + } + return ( + + + Hei 👋 + + Her kan du bli medlem i Det Norske Studentersamfund - Chateau Neuf og ha medlemsbeviset ditt på telefonen + + + + + Hva får jeg? + + - Minst 25% rabatt på de fleste arrangement{'\n'}- Gratis inngang på enkelte arrangement{'\n'}- Store + rabatter i barene{'\n'}- Egne medlemsarrangement og -tilbud{'\n'}- Mulighet til å delta i landets eldste + studentdemokrati + + Allerede medlem? + + + + + + ); + } } - const styles = StyleSheet.create({ - card: theme.card, - paragraph: { - textAlign: 'center', - fontSize: 16, - color: '#000', - paddingBottom: 10, - lineHeight: 25, - }, - button: { - marginTop: 6, - marginBottom: 16, - }, - em: { - fontSize: 16, - color: '#000', - fontStyle: 'italic', - textAlign: 'center', - marginVertical: 10, - }, - headerTitle: { - textAlign: 'center', - fontWeight: 'bold', - fontSize: 20, - color: '#000', - paddingVertical: 10, - lineHeight: 25, - }, - header: { - fontSize: 16, - fontWeight: 'bold', - color: '#000', - lineHeight: 25, - paddingLeft: 2 - }, -}); \ No newline at end of file + card: theme.card, + paragraph: { + textAlign: 'center', + fontSize: 16, + color: '#000', + paddingBottom: 10, + lineHeight: 25, + }, + button: { + marginTop: 6, + marginBottom: 16, + }, + em: { + fontSize: 16, + color: '#000', + fontStyle: 'italic', + textAlign: 'center', + marginVertical: 10, + }, + headerTitle: { + textAlign: 'center', + fontWeight: 'bold', + fontSize: 20, + color: '#000', + paddingVertical: 10, + lineHeight: 25, + }, + header: { + fontSize: 16, + fontWeight: 'bold', + color: '#000', + lineHeight: 25, + paddingLeft: 2, + }, +}); diff --git a/src/modules/membership/MembershipContainer.js b/src/modules/membership/MembershipContainer.js index fae9bf5..f36a689 100644 --- a/src/modules/membership/MembershipContainer.js +++ b/src/modules/membership/MembershipContainer.js @@ -1,86 +1,89 @@ -import React, {Component} from 'react'; -import {connect} from "react-redux"; +import React, { Component } from 'react'; +import { connect } from 'react-redux'; -import Membership from "./Membership"; -import ProofContainer from "./ProofContainer"; -import { logout } from "../../actions"; -import {requestUserData} from "../../api"; +import Membership from './Membership'; +import ProofContainer from './ProofContainer'; +import { logout } from '../../actions'; +import { requestUserData } from '../../api'; class MembershipContainer extends Component { - constructor(props) { - super(props); + constructor(props) { + super(props); - this.state = { - isFetchingUserData: props.isFetchingUserData, - userToken: props.userToken, - lastOrder: props.lastOrder, + this.state = { + isFetchingUserData: props.isFetchingUserData, + userToken: props.userToken, + lastOrder: props.lastOrder, + }; + } + + componentWillReceiveProps(nextProps) { + if (this.state.userToken !== nextProps.userToken) { + this.setState({ userToken: nextProps.userToken }, () => { + if (nextProps.userToken) { + this.fetchUser(); } + }); } - - componentWillReceiveProps(nextProps) { - if(this.state.userToken !== nextProps.userToken) { - this.setState({userToken: nextProps.userToken}, () => { - if(nextProps.userToken) { - this.fetchUser(); - } - }); - - } - if(this.state.lastOrder !== nextProps.lastOrder) { - this.setState({lastOrder: nextProps.lastOrder}, () => { - if(nextProps.userToken) { - this.fetchUser(); - } - }); + if (this.state.lastOrder !== nextProps.lastOrder) { + this.setState({ lastOrder: nextProps.lastOrder }, () => { + if (nextProps.userToken) { + this.fetchUser(); } + }); } + } - fetchUser = () => { - this.props.requestUserData(this.state.userToken) - }; + fetchUser = () => { + this.props.requestUserData(this.state.userToken); + }; - onLoginPress = () => { - this.props.onLoginPress(); - }; + onLoginPress = () => { + this.props.onLoginPress(); + }; - onRegisterPress = () => { - this.props.onRegisterPress(); - }; + onRegisterPress = () => { + this.props.onRegisterPress(); + }; - onLogoutPress = () => { - this.props.logout(); - this.props.onLogoutPress(); - }; + onLogoutPress = () => { + this.props.logout(); + this.props.onLogoutPress(); + }; - render() { - if (this.props.user) { - return ; - } - return ; + render() { + if (this.props.user) { + return ( + + ); } + return ( + + ); + } } function storeToProps(store) { - return { - isAuthenticated: store.isAuthenticated, - isFetchingUserData: store.isFetchingUserData, - userToken: store.userToken, - user: store.user, - lastOrder: store.lastOrder - }; + return { + isAuthenticated: store.isAuthenticated, + isFetchingUserData: store.isFetchingUserData, + userToken: store.userToken, + user: store.user, + lastOrder: store.lastOrder, + }; } export default connect( - storeToProps, - { requestUserData, logout } + storeToProps, + { requestUserData, logout } )(MembershipContainer); diff --git a/src/modules/membership/Proof.js b/src/modules/membership/Proof.js index 2a8b343..03f7011 100644 --- a/src/modules/membership/Proof.js +++ b/src/modules/membership/Proof.js @@ -1,241 +1,254 @@ -import React, {Component} from 'react'; -import {Image, ScrollView, StyleSheet, Text, TouchableOpacity, View} from "react-native"; -import {Button, Text as NBText, Spinner} from 'native-base'; +import React, { Component } from 'react'; +import { Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import { Button, Text as NBText, Spinner } from 'native-base'; import Confetti from 'react-native-confetti'; -import theme from "../../theme"; +import theme from '../../theme'; export default class Proof extends Component { - CONFETTI_TIMEOUT = 15000; + CONFETTI_TIMEOUT = 15000; - constructor(props) { - super(props); - this.state = { - stopConfetti: false, - } + constructor(props) { + super(props); + this.state = { + stopConfetti: false, }; + } - componentDidUpdate () { - if (this._confettiView && this.state.stopConfetti) { - this._confettiView.stopConfetti(); - this.setState({stopConfetti: false}); - } + componentDidUpdate() { + if (this._confettiView && this.state.stopConfetti) { + this._confettiView.stopConfetti(); + this.setState({ stopConfetti: false }); + } + } + + componentWillUnmount() { + if (this._confettiView) { + this._confettiView.stopConfetti(); + } + } + + onMembershipPress = () => { + this.setState({ showConfetti: true }, () => { + if (this._confettiView) { + this._confettiView.startConfetti(); + setTimeout( + function() { + this.setState({ stopConfetti: true }); + }.bind(this), + this.CONFETTI_TIMEOUT + ); + } + }); + }; + + membershipValidTo() { + if (!this.props.user.last_membership) { + return; } - componentWillUnmount () { - if (this._confettiView) { - this._confettiView.stopConfetti(); - } + let validTo = this.props.user.last_membership.end_date; + if (this.props.user.last_membership.membership_type === 'lifelong') { + validTo = 'Livsvarig'; } - onMembershipPress = () => { - this.setState({showConfetti: true}, () => { - if(this._confettiView) { - this._confettiView.startConfetti(); - setTimeout(function() { - this.setState({stopConfetti: true}); - }.bind(this), this.CONFETTI_TIMEOUT); - } - }) - }; + return ( + + Gyldig til: + {validTo} + + ); + } - membershipValidTo() { - if( !this.props.user.last_membership) { - return; - } + membershipStatus() { + if (!this.props.user.is_member) { + if (!this.props.user.last_membership) { + return; + } - let validTo = this.props.user.last_membership.end_date; - if (this.props.user.last_membership.membership_type === 'lifelong') { - validTo = 'Livsvarig' - } - - return ( - - Gyldig til: - {validTo} - - ) - } - - membershipStatus() { - if (!this.props.user.is_member) { - if ( !this.props.user.last_membership ) { - return; - } - - return ( - - Utløpt - ) - } - - const statusText = this.props.user.is_volunteer ? '❤️ AKTIV ❤️ ' : 'MEDLEM'; - - return ( - - - {statusText} - - ); - } - - purchaseButton() { - if (this.props.user.is_member) { - return; - } - - return ( - - {this.props.isChargingMembership && } - - - Medlemskapet er gyldig i ett år - - ) - } - - confetti() { - if (this.props.user.is_member) { - // Confetti!!!! - return this._confettiView = node} confettiCount={Number.MAX_SAFE_INTEGER} /> - } - } - - logo() { - if (!this.props.user.is_member) { - return; - } - - // FIXME: URL to config - return ( - - - ) - } - - chargeError() { - if (!this.props.chargeError) { - return; - } - - return {this.props.chargeError} - } - - render() { - return ( - - {this.confetti()} - - - {this.membershipName()} - {this.logo()} - {this.membershipStatus()} - {this.chargeError()} - {this.purchaseButton()} - {this.membershipValidTo()} - - - - - - - ) - }; - - membershipName() { - let name = `${this.props.user.first_name} ${this.props.user.last_name}`; - if(name === ' ') { - name = this.props.user.email; - } - return - {name} + return ( + + Utløpt + ); } + + const statusText = this.props.user.is_volunteer ? '❤️ AKTIV ❤️ ' : 'MEDLEM'; + + return ( + + + {statusText} + + + ); + } + + purchaseButton() { + if (this.props.user.is_member) { + return; + } + + return ( + + {this.props.isChargingMembership && } + + + Medlemskapet er gyldig i ett år + + + ); + } + + confetti() { + if (this.props.user.is_member) { + // Confetti!!!! + return (this._confettiView = node)} confettiCount={Number.MAX_SAFE_INTEGER} />; + } + } + + logo() { + if (!this.props.user.is_member) { + return; + } + + // FIXME: URL to config + return ( + + + + ); + } + + chargeError() { + if (!this.props.chargeError) { + return; + } + + return ( + + {this.props.chargeError} + + ); + } + + render() { + return ( + + {this.confetti()} + + + {this.membershipName()} + {this.logo()} + {this.membershipStatus()} + {this.chargeError()} + {this.purchaseButton()} + {this.membershipValidTo()} + + + + + + + ); + } + + membershipName() { + let name = `${this.props.user.first_name} ${this.props.user.last_name}`; + if (name === ' ') { + name = this.props.user.email; + } + return ( + + {name} + + ); + } } const styles = StyleSheet.create({ - card: theme.card, - membershipStatus: { - marginVertical: 16, - }, - nameText: { - flex: 1, - textAlign: 'center', - fontSize: 20, - fontWeight: 'bold', - }, - valid: { - backgroundColor: '#5cb85c', - flex: 1, - marginHorizontal: -16, - padding: 16 - }, - validText: { - color: 'white', - textAlign: 'center', - fontSize: 30, - fontWeight: "800" - }, - expired: { - backgroundColor: '#d9534f', - flex: 1, - marginHorizontal: -16, - padding: 16, - marginVertical: 16, - }, - expiredText: { - color: 'white', - textAlign: 'center', - fontSize: 24 - }, - notMember: { - backgroundColor: '#62B1F6', - flex: 1, - marginHorizontal: -16, - padding: 16 - }, - notMemberText: { - color: 'white', - textAlign: 'center', - fontSize: 24 - }, - validToValue: { - fontWeight: 'bold', - fontSize: 18, - }, - purchaseButton: { - marginTop: 16, - }, - logoutButton: { - alignSelf: 'center', - marginVertical: 20, - }, - purchaseText: { - alignItems: 'center' - }, - purchaseTextInner: { - fontStyle: 'italic', - fontSize: 16, - textAlign: 'center', - paddingTop: 10, - paddingBottom: 8 - }, - errorMessage: { - paddingTop: 10, - textAlign: 'center', - color: theme.colors.danger - } -}); \ No newline at end of file + card: theme.card, + membershipStatus: { + marginVertical: 16, + }, + nameText: { + flex: 1, + textAlign: 'center', + fontSize: 20, + fontWeight: 'bold', + }, + valid: { + backgroundColor: '#5cb85c', + flex: 1, + marginHorizontal: -16, + padding: 16, + }, + validText: { + color: 'white', + textAlign: 'center', + fontSize: 30, + fontWeight: '800', + }, + expired: { + backgroundColor: '#d9534f', + flex: 1, + marginHorizontal: -16, + padding: 16, + marginVertical: 16, + }, + expiredText: { + color: 'white', + textAlign: 'center', + fontSize: 24, + }, + notMember: { + backgroundColor: '#62B1F6', + flex: 1, + marginHorizontal: -16, + padding: 16, + }, + notMemberText: { + color: 'white', + textAlign: 'center', + fontSize: 24, + }, + validToValue: { + fontWeight: 'bold', + fontSize: 18, + }, + purchaseButton: { + marginTop: 16, + }, + logoutButton: { + alignSelf: 'center', + marginVertical: 20, + }, + purchaseText: { + alignItems: 'center', + }, + purchaseTextInner: { + fontStyle: 'italic', + fontSize: 16, + textAlign: 'center', + paddingTop: 10, + paddingBottom: 8, + }, + errorMessage: { + paddingTop: 10, + textAlign: 'center', + color: theme.colors.danger, + }, +}); diff --git a/src/modules/membership/ProofContainer.js b/src/modules/membership/ProofContainer.js index d0e17ac..06c722a 100644 --- a/src/modules/membership/ProofContainer.js +++ b/src/modules/membership/ProofContainer.js @@ -1,105 +1,90 @@ -import React, {Component} from 'react'; -import {connect} from "react-redux"; -import stripe from 'tipsi-stripe' +import React, { Component } from 'react'; +import { connect } from 'react-redux'; +import stripe from 'tipsi-stripe'; import Config from 'react-native-config'; import { graphql } from 'react-apollo'; import gql from 'graphql-tag'; -import Proof from "./Proof"; -import {requestMembershipCharge} from "../../api"; - +import Proof from './Proof'; +import { requestMembershipCharge } from '../../api'; stripe.setOptions({ - publishableKey: Config.STRIPE_PUBLISHABLE_KEY || '', + publishableKey: Config.STRIPE_PUBLISHABLE_KEY || '', }); - class ProofContainer extends Component { - constructor(props) { - super(props); - this.state = { - refreshing: false, - } + constructor(props) { + super(props); + this.state = { + refreshing: false, + }; + } + + getPrice(membershipTypes) { + if (!membershipTypes) { + return 0; } - getPrice(membershipTypes) { - if (!membershipTypes) { - return 0; - } + return membershipTypes[0].price / 100; + } - return membershipTypes[0].price / 100; - } + render() { + return ( + + ); + } - render() { - return - } - - openStripe = () => { - const options = { - prefilledInformation: { - email: this.props.user.email, - }, - smsAutofillDisabled: true, - }; - - const membershipTypeSlug = this.props.data.membershipTypes ? this.props.data.membershipTypes[0].slug : 'standard'; - - stripe.paymentRequestWithCardForm(options).then( - (token) => { - // returns a token.tokenId which should be saved - this.props.requestMembershipCharge( - this.props.userToken, - {id: token.tokenId, email: this.props.user.email}, - membershipTypeSlug); - }, - (error) => { /* FIXME: Happens only if card dialog is canceled? */ } - ); + openStripe = () => { + const options = { + prefilledInformation: { + email: this.props.user.email, + }, + smsAutofillDisabled: true, }; - // fetchUser = () => { - // this.props.fetchUser().then(() => { - // this.setState({refreshing: false}); - // }); - // }; + const membershipTypeSlug = this.props.data.membershipTypes ? this.props.data.membershipTypes[0].slug : 'standard'; - // _onRefresh() { - // console.log('onRefresh') - // this.setState({refreshing: true}); - // this.fetchUser() - // } - - // refreshControl() { - // return ( - // ) - // } + stripe.paymentRequestWithCardForm(options).then( + (token) => { + // returns a token.tokenId which should be saved + this.props.requestMembershipCharge( + this.props.userToken, + { id: token.tokenId, email: this.props.user.email }, + membershipTypeSlug + ); + }, + (error) => { + /* FIXME: Happens only if card dialog is canceled? */ + } + ); + }; } -const PROOF_QUERY = gql`{ +const PROOF_QUERY = gql` + { membershipTypes(isDefault: true) { - slug - price + slug + price } -}`; + } +`; const proofContainerWithState = connect( - (store) => ({ - userToken: store.userToken, - isChargingMembership: store.isChargingMembership, - chargeError: store.chargeError - }), - {requestMembershipCharge} + (store) => ({ + userToken: store.userToken, + isChargingMembership: store.isChargingMembership, + chargeError: store.chargeError, + }), + { requestMembershipCharge } )(ProofContainer); export default graphql(PROOF_QUERY)(proofContainerWithState); diff --git a/src/modules/register/UserRegister.js b/src/modules/register/UserRegister.js index 00e89ac..71babfb 100644 --- a/src/modules/register/UserRegister.js +++ b/src/modules/register/UserRegister.js @@ -1,264 +1,293 @@ -import React, {Component} from 'react'; -import {Container, Header, Content, Form, Item, Input, Label, Spinner, Button, Text, Icon, Toast} from 'native-base'; -import {StyleSheet, View} from "react-native"; +import React, { Component } from 'react'; +import { Container, Header, Content, Form, Item, Input, Label, Spinner, Button, Text, Icon, Toast } from 'native-base'; +import { StyleSheet, View } from 'react-native'; import EmailValidator from 'email-validator'; -import theme from "../../theme"; +import theme from '../../theme'; export default class UserRegister extends Component { - constructor(props) { - super(props); + constructor(props) { + super(props); - this.state = { - firstName: '', - lastName: '', - email: '', - phoneNumber: '', - password: '', - errors: {}, - touched: new Set() - } + this.state = { + firstName: '', + lastName: '', + email: '', + phoneNumber: '', + password: '', + errors: {}, + touched: new Set(), }; + } - validateForm(triggeredBy) { - const fieldNames = ['firstName', 'lastName', 'email', 'phoneNumber', 'password']; + validateForm(triggeredBy) { + const fieldNames = ['firstName', 'lastName', 'email', 'phoneNumber', 'password']; - let errors = Object.assign({}, this.state.errors); - let touched = new Set(this.state.touched).add(triggeredBy); + const errors = Object.assign({}, this.state.errors); + const touched = new Set(this.state.touched).add(triggeredBy); - /* No empty fields */ - for(let key of fieldNames) { - const value = this.state[key]; - if(value === '') { - errors[key] = 'kan ikke være tomt'; - } else if(key === 'email' && !EmailValidator.validate(value)) { - errors[key] = 'er ikke en gyldig e-post'; - } else { - delete errors[key]; - } - } - - this.setState({errors: errors, touched}); - }; - - fieldHasError(field) { - return this.state.touched.has(field) && field in this.state.errors; + /* No empty fields */ + for (const key of fieldNames) { + const value = this.state[key]; + if (value === '') { + errors[key] = 'kan ikke være tomt'; + } else if (key === 'email' && !EmailValidator.validate(value)) { + errors[key] = 'er ikke en gyldig e-post'; + } else { + delete errors[key]; + } } - canSubmitForm() { - return Object.keys(this.state.errors).length === 0; - } + this.setState({ errors, touched }); + } - firstNameInput() { - return - - { - this.setState({firstName}, () => { this.validateForm('firstName'); }); - }} - onSubmitEditing={() => { - this.refs.lastNameInput._root.focus() - }} - value={this.state.firstName} - /> - {this.fieldHasError('firstName') && } - ; - } + fieldHasError(field) { + return this.state.touched.has(field) && field in this.state.errors; + } - lastNameInput() { - return - - { - this.setState({lastName}, () => { this.validateForm('lastName'); }); - }} - onSubmitEditing={() => { - this.refs.emailInput._root.focus() - }} - value={this.state.lastName} - /> - {this.fieldHasError('lastName') && } - ; - } + canSubmitForm() { + return Object.keys(this.state.errors).length === 0; + } - emailInput() { - return - - { - this.setState({email}, () => { this.validateForm('email'); }); - }} - onSubmitEditing={() => { - this.refs.phoneInput._root.focus() - }} - value={this.state.email} - /> - {this.fieldHasError('email') && } - ; - } - - phoneNumberInput() { - return - - { - this.setState({phoneNumber}, () => { this.validateForm('phoneNumber'); }); - }} - onSubmitEditing={() => { - this.refs.passwordInput._root.focus() - }} - value={this.state.phoneNumber} - /> - {this.fieldHasError('phoneNumber') && } - ; - } - - passwordInput() { - return - - { - this.setState({password}, () => { this.validateForm('password'); }); - }} - onSubmitEditing={this.onRegisterPress} - value={this.state.password} - /> - {this.fieldHasError('password') && } - - } - - componentWillReceiveProps(nextProps) { - if(this.props.registerError !== nextProps.registerError) { - this.setState({'errors': nextProps.registerError}) - } - } - - showNonFieldError = () => { - const errors = this.props.registerError; - if (Object.keys(errors).length !== 0 && 'non_field_errors' in errors) { - const err = errors.non_field_errors; - let errorFormatted = err ? err[0] : 'Kunne ikke registrere bruker, prøv igjen...'; - return {errorFormatted} - - } else if (Object.keys(errors).length !== 0 && 'detail' in errors) { - const err = errors.detail; - let errorFormatted = err ? err : 'Kunne ikke registrere bruker, prøv igjen...'; - return {errorFormatted} - } - - return - }; - - showFieldError = (field) => { - if(!this.fieldHasError(field) ) { - return; - } - return {this.state.errors[field]}; - }; - - showSpinner = () => { - if (this.props.isRegisteringUser) { - return - } - }; - - render() { - return ( - - -
- {this.firstNameInput()} - {this.showFieldError('firstName')} - {this.lastNameInput()} - {this.showFieldError('lastName')} - {this.emailInput()} - {this.showFieldError('email')} - {this.phoneNumberInput()} - {this.showFieldError('phoneNumber')} - {this.passwordInput()} - {this.showFieldError('password')} - {this.showNonFieldError()} - {this.registerbutton()} - {this.showSpinner()} -
-
-
- ); - } - - onRegisterPress = () => { - if( !this.canSubmitForm() ) { - Toast.show({ - text: 'Noen av feltene er ikke fylt ut riktig', - position: 'bottom', - buttonText: 'OK', - duration: 1500, + firstNameInput() { + return ( + + + { + this.setState({ firstName }, () => { + this.validateForm('firstName'); }); - return; - } + }} + onSubmitEditing={() => { + this.refs.lastNameInput._root.focus(); + }} + value={this.state.firstName} + /> + {this.fieldHasError('firstName') && } + + ); + } - this.props.onRegisterPress( - this.state.firstName, - this.state.lastName, - this.state.email, - this.state.phoneNumber, - this.state.password, - ); - }; + lastNameInput() { + return ( + + + { + this.setState({ lastName }, () => { + this.validateForm('lastName'); + }); + }} + onSubmitEditing={() => { + this.refs.emailInput._root.focus(); + }} + value={this.state.lastName} + /> + {this.fieldHasError('lastName') && } + + ); + } - registerbutton() { - const isDisabled = this.state.touched.size === 0 || this.props.isRegisteringUser || !this.canSubmitForm(); + emailInput() { + return ( + + + { + this.setState({ email }, () => { + this.validateForm('email'); + }); + }} + onSubmitEditing={() => { + this.refs.phoneInput._root.focus(); + }} + value={this.state.email} + /> + {this.fieldHasError('email') && } + + ); + } - return (); + phoneNumberInput() { + return ( + + + { + this.setState({ phoneNumber }, () => { + this.validateForm('phoneNumber'); + }); + }} + onSubmitEditing={() => { + this.refs.passwordInput._root.focus(); + }} + value={this.state.phoneNumber} + /> + {this.fieldHasError('phoneNumber') && } + + ); + } + + passwordInput() { + return ( + + + { + this.setState({ password }, () => { + this.validateForm('password'); + }); + }} + onSubmitEditing={this.onRegisterPress} + value={this.state.password} + /> + {this.fieldHasError('password') && } + + ); + } + + componentWillReceiveProps(nextProps) { + if (this.props.registerError !== nextProps.registerError) { + this.setState({ errors: nextProps.registerError }); } + } + + showNonFieldError = () => { + const errors = this.props.registerError; + if (Object.keys(errors).length !== 0 && 'non_field_errors' in errors) { + const err = errors.non_field_errors; + const errorFormatted = err ? err[0] : 'Kunne ikke registrere bruker, prøv igjen...'; + return ( + + {errorFormatted} + + ); + } + if (Object.keys(errors).length !== 0 && 'detail' in errors) { + const err = errors.detail; + const errorFormatted = err || 'Kunne ikke registrere bruker, prøv igjen...'; + return ( + + {errorFormatted} + + ); + } + + return ; + }; + + showFieldError = (field) => { + if (!this.fieldHasError(field)) { + return; + } + return ( + + {this.state.errors[field]} + + ); + }; + + showSpinner = () => { + if (this.props.isRegisteringUser) { + return ; + } + }; + + render() { + return ( + + +
+ {this.firstNameInput()} + {this.showFieldError('firstName')} + {this.lastNameInput()} + {this.showFieldError('lastName')} + {this.emailInput()} + {this.showFieldError('email')} + {this.phoneNumberInput()} + {this.showFieldError('phoneNumber')} + {this.passwordInput()} + {this.showFieldError('password')} + {this.showNonFieldError()} + {this.registerbutton()} + {this.showSpinner()} +
+
+
+ ); + } + + onRegisterPress = () => { + if (!this.canSubmitForm()) { + Toast.show({ + text: 'Noen av feltene er ikke fylt ut riktig', + position: 'bottom', + buttonText: 'OK', + duration: 1500, + }); + return; + } + + this.props.onRegisterPress( + this.state.firstName, + this.state.lastName, + this.state.email, + this.state.phoneNumber, + this.state.password + ); + }; + + registerbutton() { + const isDisabled = this.state.touched.size === 0 || this.props.isRegisteringUser || !this.canSubmitForm(); + + return ( + + ); + } } const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: 'center', - }, - input: { - width: 320, - fontSize: 20, - height: 52, - }, - registerButton: { - marginBottom: 16, - }, - errorBox: { - marginVertical: 10, - }, - errorMessage: { - textAlign: 'center', - color: theme.colors.danger, - fontSize: 14, - }, - card: theme.card, - errorText: { - paddingTop: 2, - paddingBottom: 12, - } + container: { + flex: 1, + justifyContent: 'center', + }, + input: { + width: 320, + fontSize: 20, + height: 52, + }, + registerButton: { + marginBottom: 16, + }, + errorBox: { + marginVertical: 10, + }, + errorMessage: { + textAlign: 'center', + color: theme.colors.danger, + fontSize: 14, + }, + card: theme.card, + errorText: { + paddingTop: 2, + paddingBottom: 12, + }, }); diff --git a/src/modules/register/UserRegisterContainer.js b/src/modules/register/UserRegisterContainer.js index bbf0dd6..d576b75 100644 --- a/src/modules/register/UserRegisterContainer.js +++ b/src/modules/register/UserRegisterContainer.js @@ -1,49 +1,50 @@ -import React, {Component} from 'react'; -import {connect} from "react-redux"; +import React, { Component } from 'react'; +import { connect } from 'react-redux'; -import UserRegister from "./UserRegister"; -import {requestRegisterUser} from "../../api"; +import UserRegister from './UserRegister'; +import { requestRegisterUser } from '../../api'; class UserRegisterContainer extends Component { - - constructor(state) { - super(state); - this.state = { - isAuthenticated: false, - } - } - - componentWillReceiveProps(nextProps) { - if (this.state.isAuthenticated !== nextProps.isAuthenticated) { - if (nextProps.isAuthenticated) { - this.props.onRegister(); - } - } - } - - onRegisterPress = (firstName, lastName, email, phoneNumber, password) => { - this.props.requestRegisterUser(firstName, lastName, email, phoneNumber, password); + constructor(state) { + super(state); + this.state = { + isAuthenticated: false, }; + } - render() { - return ; + componentWillReceiveProps(nextProps) { + if (this.state.isAuthenticated !== nextProps.isAuthenticated) { + if (nextProps.isAuthenticated) { + this.props.onRegister(); + } } + } + + onRegisterPress = (firstName, lastName, email, phoneNumber, password) => { + this.props.requestRegisterUser(firstName, lastName, email, phoneNumber, password); + }; + + render() { + return ( + + ); + } } function storeToProps(store) { - const errors = store.registerError === null ? {} : store.registerError; - return { - isAuthenticated: store.isAuthenticated, - isRegisteringUser: store.isRegisteringUser, - registerError: errors - }; + const errors = store.registerError === null ? {} : store.registerError; + return { + isAuthenticated: store.isAuthenticated, + isRegisteringUser: store.isRegisteringUser, + registerError: errors, + }; } export default connect( - storeToProps, - {requestRegisterUser} + storeToProps, + { requestRegisterUser } )(UserRegisterContainer); diff --git a/src/navigation.js b/src/navigation.js index 4fc55ae..e04782e 100644 --- a/src/navigation.js +++ b/src/navigation.js @@ -1,67 +1,72 @@ -import {Platform, StyleSheet} from 'react-native'; -import {createBottomTabNavigator, createStackNavigator, createAppContainer, getActiveChildNavigationOptions} from "react-navigation"; +import { Platform, StyleSheet } from 'react-native'; +import { + createBottomTabNavigator, + createStackNavigator, + createAppContainer, + getActiveChildNavigationOptions, +} from 'react-navigation'; -import AboutScreen from "./screens/AboutScreen"; -import EventDetailScreen from "./screens/EventDetailScreen"; -import EventListScreen from "./screens/EventListScreen"; -import LoginScreen from "./screens/LoginScreen"; -import MembershipScreen from "./screens/MembershipScreen"; -import UserRegisterScreen from "./screens/UserRegisterScreen"; +import AboutScreen from './screens/AboutScreen'; +import EventDetailScreen from './screens/EventDetailScreen'; +import EventListScreen from './screens/EventListScreen'; +import LoginScreen from './screens/LoginScreen'; +import MembershipScreen from './screens/MembershipScreen'; +import UserRegisterScreen from './screens/UserRegisterScreen'; const tabBarRoutes = { - EventList: EventListScreen, - Membership: MembershipScreen, - About: AboutScreen, + EventList: EventListScreen, + Membership: MembershipScreen, + About: AboutScreen, }; const tabBarOptions = { - tabBarPosition: 'bottom', - tabBarOptions: { - style: { - ...Platform.select({ - android: { - backgroundColor: "#fff", - borderTopWidth: StyleSheet.hairlineWidth, - borderTopColor: 'rgba(0, 0, 0, 0.1)' - } - }) - }, - indicatorStyle: { - backgroundColor: "#f58220" - }, - activeTintColor: '#f58220', - inactiveTintColor: '#333333', - showIcon: true, - labelStyle: { - fontSize: 10, - marginBottom: 0, - ...Platform.select({ - android: { - fontWeight: 'bold', - } - }), + tabBarPosition: 'bottom', + tabBarOptions: { + style: { + ...Platform.select({ + android: { + backgroundColor: '#fff', + borderTopWidth: StyleSheet.hairlineWidth, + borderTopColor: 'rgba(0, 0, 0, 0.1)', }, + }), }, - navigationOptions: ({ navigation, screenProps }) => ({ - // get header title from active child screen - ...getActiveChildNavigationOptions(navigation, screenProps), - headerStyle: { - backgroundColor: '#f58220', + indicatorStyle: { + backgroundColor: '#f58220', + }, + activeTintColor: '#f58220', + inactiveTintColor: '#333333', + showIcon: true, + labelStyle: { + fontSize: 10, + marginBottom: 0, + ...Platform.select({ + android: { + fontWeight: 'bold', }, - headerTitleStyle: { - color: 'white', - }, - headerTintColor: 'white', - }) + }), + }, + }, + navigationOptions: ({ navigation, screenProps }) => ({ + // get header title from active child screen + ...getActiveChildNavigationOptions(navigation, screenProps), + headerStyle: { + backgroundColor: '#f58220', + }, + headerTitleStyle: { + color: 'white', + }, + headerTintColor: 'white', + }), }; const tabNav = createBottomTabNavigator(tabBarRoutes, tabBarOptions); const DuskenNavigation = createStackNavigator({ - Root: tabNav, - Login: LoginScreen, - UserRegister: UserRegisterScreen, - EventDetail: EventDetailScreen, + Root: tabNav, + Login: LoginScreen, + UserRegister: UserRegisterScreen, + EventDetail: EventDetailScreen, }); export default createAppContainer(DuskenNavigation); diff --git a/src/reducers.js b/src/reducers.js index 6eb9ea1..aaf9731 100644 --- a/src/reducers.js +++ b/src/reducers.js @@ -1,133 +1,133 @@ import { - LOGIN_REQUEST, - LOGIN_SUCCESS, - LOGIN_FAILURE, - LOGOUT, - USER_DATA_REQUEST, - USER_DATA_SUCCESS, - USER_DATA_FAILURE, - REGISTER_USER_REQUEST, - REGISTER_USER_FAILURE, - REGISTER_USER_SUCCESS, MEMBERSHIP_CHARGE_REQUEST, MEMBERSHIP_CHARGE_FAILURE, MEMBERSHIP_CHARGE_SUCCESS, + LOGIN_REQUEST, + LOGIN_SUCCESS, + LOGIN_FAILURE, + LOGOUT, + USER_DATA_REQUEST, + USER_DATA_SUCCESS, + USER_DATA_FAILURE, + REGISTER_USER_REQUEST, + REGISTER_USER_FAILURE, + REGISTER_USER_SUCCESS, + MEMBERSHIP_CHARGE_REQUEST, + MEMBERSHIP_CHARGE_FAILURE, + MEMBERSHIP_CHARGE_SUCCESS, } from './actions'; -import { snakeToCamelCase } from './utils' +import { snakeToCamelCase } from './utils'; const initialState = { - isAuthenticated: false, - isLoggingIn: false, - isFetchingUserData: false, - isRegisteringUser: false, - isChargingMembership: false, - userToken: null, - user: null, - loginError: null, - userError: null, - registerError: null, - chargeError: null, - lastOrder: null, + isAuthenticated: false, + isLoggingIn: false, + isFetchingUserData: false, + isRegisteringUser: false, + isChargingMembership: false, + userToken: null, + user: null, + loginError: null, + userError: null, + registerError: null, + chargeError: null, + lastOrder: null, }; - function formatErrors(errs) { - let _errs = {}; - for(let [key, value] of Object.entries(errs)) { - _errs[snakeToCamelCase(key)] = Array.isArray(value) ? value.join('\n') : value; - } - return _errs + const _errs = {}; + for (const [key, value] of Object.entries(errs)) { + _errs[snakeToCamelCase(key)] = Array.isArray(value) ? value.join('\n') : value; + } + return _errs; } - - export default function duskenApp(state = initialState, action) { - switch (action.type) { - /* Login */ - case LOGIN_REQUEST: - return Object.assign({}, state, { - isLoggingIn: true, - loginError: null, - }); + switch (action.type) { + /* Login */ + case LOGIN_REQUEST: + return Object.assign({}, state, { + isLoggingIn: true, + loginError: null, + }); - case LOGIN_FAILURE: - return Object.assign({}, state, { - isLoggingIn: false, - loginError: action.loginError - }); + case LOGIN_FAILURE: + return Object.assign({}, state, { + isLoggingIn: false, + loginError: action.loginError, + }); - case LOGIN_SUCCESS: - return Object.assign({}, state, { - isLoggingIn: false, - isAuthenticated: true, - userToken: action.token - }); + case LOGIN_SUCCESS: + return Object.assign({}, state, { + isLoggingIn: false, + isAuthenticated: true, + userToken: action.token, + }); - case LOGOUT: - return initialState; + case LOGOUT: + return initialState; - /* User data */ - case USER_DATA_REQUEST: - return Object.assign({}, state, { - isFetchingUserData: true, - userError: null - }); + /* User data */ + case USER_DATA_REQUEST: + return Object.assign({}, state, { + isFetchingUserData: true, + userError: null, + }); - case USER_DATA_FAILURE: - return Object.assign({}, state, { - isFetchingUserData: false, - userError: action.userError - }); + case USER_DATA_FAILURE: + return Object.assign({}, state, { + isFetchingUserData: false, + userError: action.userError, + }); - case USER_DATA_SUCCESS: - return Object.assign({}, state, { - isFetchingUserData: false, - user: action.data, - }); + case USER_DATA_SUCCESS: + return Object.assign({}, state, { + isFetchingUserData: false, + user: action.data, + }); - /* Register user */ - case REGISTER_USER_REQUEST: - return Object.assign({}, state, { - isRegisteringUser: true, - registerError: null, - }); + /* Register user */ + case REGISTER_USER_REQUEST: + return Object.assign({}, state, { + isRegisteringUser: true, + registerError: null, + }); - case REGISTER_USER_FAILURE: - return Object.assign({}, state, { - isRegisteringUser: false, - registerError: formatErrors(action.registerError) - }); + case REGISTER_USER_FAILURE: + return Object.assign({}, state, { + isRegisteringUser: false, + registerError: formatErrors(action.registerError), + }); - case REGISTER_USER_SUCCESS: - let userData = action.data; - const token = action.data.auth_token; - delete userData['auth_token']; + case REGISTER_USER_SUCCESS: + const userData = action.data; + const token = action.data.auth_token; + delete userData.auth_token; - return Object.assign({}, state, { - isRegisteringUser: false, - isAuthenticated: true, - user: userData, - userToken: token, - }); + return Object.assign({}, state, { + isRegisteringUser: false, + isAuthenticated: true, + user: userData, + userToken: token, + }); - /* User data */ - case MEMBERSHIP_CHARGE_REQUEST: - return Object.assign({}, state, { - isChargingMembership: true, - chargeError: null - }); + /* User data */ + case MEMBERSHIP_CHARGE_REQUEST: + return Object.assign({}, state, { + isChargingMembership: true, + chargeError: null, + }); - case MEMBERSHIP_CHARGE_FAILURE: - return Object.assign({}, state, { - isChargingMembership: false, - chargeError: action.chargeError - }); + case MEMBERSHIP_CHARGE_FAILURE: + return Object.assign({}, state, { + isChargingMembership: false, + chargeError: action.chargeError, + }); - case MEMBERSHIP_CHARGE_SUCCESS: - return Object.assign({}, state, { - isChargingMembership: false, - lastOrder: action.data, - }); + case MEMBERSHIP_CHARGE_SUCCESS: + return Object.assign({}, state, { + isChargingMembership: false, + lastOrder: action.data, + }); - default: - return state - } + default: + return state; + } } diff --git a/src/reduxStore.js b/src/reduxStore.js index 6142938..09d8bd3 100644 --- a/src/reduxStore.js +++ b/src/reduxStore.js @@ -1,26 +1,17 @@ import { applyMiddleware, createStore } from 'redux'; import { persistStore, persistReducer } from 'redux-persist'; import AsyncStorage from '@react-native-community/async-storage'; -import thunk from "redux-thunk"; - -import rootReducer from './reducers' +import thunk from 'redux-thunk'; +import rootReducer from './reducers'; const persistConfig = { key: 'root', storage: AsyncStorage, - blacklist: [ - 'isLoggingIn', - 'isFetchingUserData', - 'isRegisteringUser', - 'isChargingMembership' - ] + blacklist: ['isLoggingIn', 'isFetchingUserData', 'isRegisteringUser', 'isChargingMembership'], }; const persistedReducer = persistReducer(persistConfig, rootReducer); -export let store = createStore( - persistedReducer, - applyMiddleware(...[thunk]) -); -export let persistor = persistStore(store); +export const store = createStore(persistedReducer, applyMiddleware(...[thunk])); +export const persistor = persistStore(store); diff --git a/src/screens/AboutScreen.js b/src/screens/AboutScreen.js index 83062ca..65a4be9 100644 --- a/src/screens/AboutScreen.js +++ b/src/screens/AboutScreen.js @@ -1,22 +1,21 @@ import React, { Component } from 'react'; import Icon from 'react-native-vector-icons/SimpleLineIcons'; -import {StyleProvider} from "native-base"; +import { StyleProvider } from 'native-base'; -import AboutContainer from "../modules/about/AboutContainer"; +import AboutContainer from '../modules/about/AboutContainer'; import getTheme from '../../native-base-theme/components'; export default class AboutScreen extends Component { - static navigationOptions = { - title: 'Om oss', - tabBarIcon: ({ tintColor }) => ( - - ), - }; + static navigationOptions = { + title: 'Om oss', + tabBarIcon: ({ tintColor }) => , + }; - render() { - return ( - - - ); - } + render() { + return ( + + + + ); + } } diff --git a/src/screens/EventDetailScreen.js b/src/screens/EventDetailScreen.js index aa0d687..3697aeb 100644 --- a/src/screens/EventDetailScreen.js +++ b/src/screens/EventDetailScreen.js @@ -1,26 +1,26 @@ import React, { Component } from 'react'; -import {StyleProvider} from "native-base"; -import EventDetail from "../modules/events/EventDetail"; +import { StyleProvider } from 'native-base'; +import EventDetail from '../modules/events/EventDetail'; import getTheme from '../../native-base-theme/components'; export default class EventDetailScreen extends Component { - static navigationOptions = ({navigation}) => ({ - title: navigation.state.params.item.title.decoded, - headerStyle: { - backgroundColor: '#f58220', - }, - headerTitleStyle: { - color: 'white', - }, - headerTintColor: 'white', - }); + static navigationOptions = ({ navigation }) => ({ + title: navigation.state.params.item.title.decoded, + headerStyle: { + backgroundColor: '#f58220', + }, + headerTitleStyle: { + color: 'white', + }, + headerTintColor: 'white', + }); - render() { - const { params } = this.props.navigation.state; - return ( - - - - ); - } -} \ No newline at end of file + render() { + const { params } = this.props.navigation.state; + return ( + + + + ); + } +} diff --git a/src/screens/EventListScreen.js b/src/screens/EventListScreen.js index 95c2ca6..a911692 100644 --- a/src/screens/EventListScreen.js +++ b/src/screens/EventListScreen.js @@ -1,27 +1,25 @@ import React, { Component } from 'react'; -import EventListContainer from "../modules/events/EventListContainer"; import Icon from 'react-native-vector-icons/SimpleLineIcons'; -import {StyleProvider} from "native-base"; +import { StyleProvider } from 'native-base'; +import EventListContainer from '../modules/events/EventListContainer'; import getTheme from '../../native-base-theme/components'; export default class EventsScreen extends Component { - static navigationOptions = { - title: 'Program', - tabBarIcon: ({ tintColor }) => ( - - ), - }; + static navigationOptions = { + title: 'Program', + tabBarIcon: ({ tintColor }) => , + }; - showEvent = (item) => { - const { navigate } = this.props.navigation; - navigate('EventDetail', {item: item}) - }; + showEvent = (item) => { + const { navigate } = this.props.navigation; + navigate('EventDetail', { item }); + }; - render() { - return ( - - - - ); - } + render() { + return ( + + + + ); + } } diff --git a/src/screens/LoginScreen.js b/src/screens/LoginScreen.js index 8bbe737..a12ba87 100644 --- a/src/screens/LoginScreen.js +++ b/src/screens/LoginScreen.js @@ -1,30 +1,30 @@ import React, { Component } from 'react'; -import LoginContainer from "../modules/login/LoginContainer"; -import {StyleProvider} from "native-base"; +import { StyleProvider } from 'native-base'; +import LoginContainer from '../modules/login/LoginContainer'; import getTheme from '../../native-base-theme/components'; export default class LoginScreen extends Component { - static navigationOptions = { - title: 'Logg inn', - headerStyle: { - backgroundColor: '#f58220', - }, - headerTitleStyle: { - color: 'white', - }, - headerTintColor: 'white', - }; + static navigationOptions = { + title: 'Logg inn', + headerStyle: { + backgroundColor: '#f58220', + }, + headerTitleStyle: { + color: 'white', + }, + headerTintColor: 'white', + }; - onLogin = () => { - // This works since LoginScreen is allways navigated to from MembershipScreen - this.props.navigation.goBack(); - }; + onLogin = () => { + // This works since LoginScreen is allways navigated to from MembershipScreen + this.props.navigation.goBack(); + }; - render() { - return ( - - - - ) - } -} \ No newline at end of file + render() { + return ( + + + + ); + } +} diff --git a/src/screens/MembershipScreen.js b/src/screens/MembershipScreen.js index a112de4..72ce1b3 100644 --- a/src/screens/MembershipScreen.js +++ b/src/screens/MembershipScreen.js @@ -1,37 +1,36 @@ import React, { Component } from 'react'; -import MembershipContainer from "../modules/membership/MembershipContainer"; import Icon from 'react-native-vector-icons/SimpleLineIcons'; -import {StyleProvider} from "native-base"; +import { StyleProvider } from 'native-base'; +import MembershipContainer from '../modules/membership/MembershipContainer'; import getTheme from '../../native-base-theme/components'; export default class MembershipScreen extends Component { - static navigationOptions = { - title: 'Medlemskap', - tabBarIcon: ({ tintColor }) => ( - - ), - }; + static navigationOptions = { + title: 'Medlemskap', + tabBarIcon: ({ tintColor }) => , + }; - loginNavigate = () => { - this.props.navigation.navigate('Login'); - }; + loginNavigate = () => { + this.props.navigation.navigate('Login'); + }; - logoutNavigate = () => { - this.props.navigation.navigate('Membership'); - }; + logoutNavigate = () => { + this.props.navigation.navigate('Membership'); + }; - registerNavigate = () => { - this.props.navigation.navigate('UserRegister'); - }; + registerNavigate = () => { + this.props.navigation.navigate('UserRegister'); + }; - render() { - return ( - - - - ) - } + render() { + return ( + + + + ); + } } diff --git a/src/screens/UserRegisterScreen.js b/src/screens/UserRegisterScreen.js index 0b16965..8cfc626 100644 --- a/src/screens/UserRegisterScreen.js +++ b/src/screens/UserRegisterScreen.js @@ -1,30 +1,30 @@ import React, { Component } from 'react'; -import UserRegisterContainer from "../modules/register/UserRegisterContainer"; -import {StyleProvider} from "native-base"; +import { StyleProvider } from 'native-base'; +import UserRegisterContainer from '../modules/register/UserRegisterContainer'; import getTheme from '../../native-base-theme/components'; export default class UserRegisterScreen extends Component { - static navigationOptions = { - title: 'Bli medlem', - headerStyle: { - backgroundColor: '#f58220', - }, - headerTitleStyle: { - color: 'white', - }, - headerTintColor: 'white', - }; + static navigationOptions = { + title: 'Bli medlem', + headerStyle: { + backgroundColor: '#f58220', + }, + headerTitleStyle: { + color: 'white', + }, + headerTintColor: 'white', + }; - onRegister = () => { - // This works since UserRegisterScreen is allways navigated to from MembershipScreen - this.props.navigation.goBack(); - }; + onRegister = () => { + // This works since UserRegisterScreen is allways navigated to from MembershipScreen + this.props.navigation.goBack(); + }; - render() { - return ( - - - - ) - } -} \ No newline at end of file + render() { + return ( + + + + ); + } +} diff --git a/src/theme.js b/src/theme.js index 4b3c323..80c3788 100644 --- a/src/theme.js +++ b/src/theme.js @@ -1,24 +1,24 @@ -import { Platform } from "react-native"; -import platform from "native-base/src/theme/variables/platform"; +import { Platform } from 'react-native'; +import platform from 'native-base/src/theme/variables/platform'; const colors = { - danger: platform.btnDangerBg + danger: platform.btnDangerBg, }; const card = { - ...Platform.select({ - ios: { - shadowColor: 'rgba(0,0,0, .2)', - shadowOffset: {height: 0, width: 0}, - shadowOpacity: 1, - shadowRadius: 1, - }, - android: { - elevation: 1, - }, - }), - padding: 8, - margin: 8, - backgroundColor: '#fff', + ...Platform.select({ + ios: { + shadowColor: 'rgba(0,0,0, .2)', + shadowOffset: { height: 0, width: 0 }, + shadowOpacity: 1, + shadowRadius: 1, + }, + android: { + elevation: 1, + }, + }), + padding: 8, + margin: 8, + backgroundColor: '#fff', }; -export default {card, colors}; \ No newline at end of file +export default { card, colors }; diff --git a/src/utils.js b/src/utils.js index 87c209b..cd3930b 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,14 +1,14 @@ export function snakeToCamelCase(val) { - return val.replace(/_([a-z])/g, function (g) { return g[1].toUpperCase(); }); + return val.replace(/_([a-z])/g, function(g) { + return g[1].toUpperCase(); + }); } -export function fetchWithTimeout(url, options, timeout=10000) { - /** Wraps fetch to allow it to time out */ - // FIXME: show timeout message in UI - return Promise.race([ - fetch(url, options), - new Promise((_, reject) => - setTimeout(() => reject(new Error('Fetch request timed out')), timeout) - ) - ]); +export function fetchWithTimeout(url, options, timeout = 10000) { + /** Wraps fetch to allow it to time out */ + // FIXME: show timeout message in UI + return Promise.race([ + fetch(url, options), + new Promise((_, reject) => setTimeout(() => reject(new Error('Fetch request timed out')), timeout)), + ]); } diff --git a/yarn.lock b/yarn.lock index 7f37a93..75cb746 100644 --- a/yarn.lock +++ b/yarn.lock @@ -975,6 +975,11 @@ acorn-globals@^4.1.0: acorn "^6.0.1" acorn-walk "^6.0.1" +acorn-jsx@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" + integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg== + acorn-walk@^6.0.1: version "6.1.1" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.1.1.tgz#d363b66f5fac5f018ff9c3a1e7b6f8e310cc3913" @@ -985,7 +990,7 @@ acorn@^5.5.3: resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== -acorn@^6.0.1: +acorn@^6.0.1, acorn@^6.0.7: version "6.1.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA== @@ -997,7 +1002,7 @@ agent-base@^4.1.0: dependencies: es6-promisify "^5.0.0" -ajv@^6.5.5: +ajv@^6.5.5, ajv@^6.9.1: version "6.10.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg== @@ -1191,6 +1196,14 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +aria-query@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc" + integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w= + dependencies: + ast-types-flow "0.0.7" + commander "^2.11.0" + arr-diff@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-1.1.0.tgz#687c32758163588fef7de7b36fabe495eb1a399a" @@ -1236,6 +1249,14 @@ array-filter@~0.0.0: resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw= +array-includes@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" + integrity sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.7.0" + array-map@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" @@ -1288,6 +1309,11 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +ast-types-flow@0.0.7, ast-types-flow@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" + integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= + astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" @@ -1325,6 +1351,25 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== +axobject-query@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.2.tgz#ea187abe5b9002b377f925d8bf7d1c561adf38f9" + integrity sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww== + dependencies: + ast-types-flow "0.0.7" + +babel-eslint@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.1.tgz#919681dc099614cd7d31d45c8908695092a1faed" + integrity sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ== + dependencies: + "@babel/code-frame" "^7.0.0" + "@babel/parser" "^7.0.0" + "@babel/traverse" "^7.0.0" + "@babel/types" "^7.0.0" + eslint-scope "3.7.1" + eslint-visitor-keys "^1.0.0" + babel-jest@^24.7.1: version "24.7.1" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.7.1.tgz#73902c9ff15a7dfbdc9994b0b17fcefd96042178" @@ -1628,7 +1673,7 @@ chalk@^1.1.1: strip-ansi "^3.0.0" supports-color "^2.0.0" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1765,7 +1810,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.19.0, commander@^2.9.0, commander@~2.20.0: +commander@^2.11.0, commander@^2.19.0, commander@^2.9.0, commander@~2.20.0: version "2.20.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== @@ -1840,6 +1885,11 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= + convert-source-map@^1.1.0, convert-source-map@^1.4.0: version "1.6.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20" @@ -1908,7 +1958,7 @@ cross-spawn@^5.0.1, cross-spawn@^5.1.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^6.0.0: +cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -1931,6 +1981,11 @@ cssstyle@^1.0.0: dependencies: cssom "0.3.x" +damerau-levenshtein@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz#03191c432cb6eea168bb77f3a55ffdccb8978514" + integrity sha1-AxkcQyy27qFou3fzpV/9zLiXhRQ= + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -1947,7 +2002,7 @@ data-urls@^1.0.0: whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" -debug@2.6.9, debug@^2.2.0, debug@^2.3.3: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -1961,7 +2016,7 @@ debug@^3.1.0: dependencies: ms "^2.1.1" -debug@^4.1.0, debug@^4.1.1: +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -1995,7 +2050,7 @@ default-require-extensions@^2.0.0: dependencies: strip-bom "^3.0.0" -define-properties@^1.1.2: +define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== @@ -2064,6 +2119,28 @@ diff-sequences@^24.3.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.3.0.tgz#0f20e8a1df1abddaf4d9c226680952e64118b975" integrity sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw== +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + dom-serializer@0: version "0.1.1" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" @@ -2122,7 +2199,7 @@ email-validator@^2.0.4: resolved "https://registry.yarnpkg.com/email-validator/-/email-validator-2.0.4.tgz#b8dfaa5d0dae28f1b03c95881d904d4e40bfe7ed" integrity sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ== -emoji-regex@^7.0.1: +emoji-regex@^7.0.1, emoji-regex@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== @@ -2171,7 +2248,7 @@ errorhandler@^1.5.0: accepts "~1.3.3" escape-html "~1.0.3" -es-abstract@^1.5.1: +es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.5.1, es-abstract@^1.7.0: version "1.13.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== @@ -2226,6 +2303,180 @@ escodegen@^1.9.1: optionalDependencies: source-map "~0.6.1" +eslint-config-airbnb-base@^13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-13.1.0.tgz#b5a1b480b80dfad16433d6c4ad84e6605052c05c" + integrity sha512-XWwQtf3U3zIoKO1BbHh6aUhJZQweOwSt4c2JrPDg9FP3Ltv3+YfEv7jIDB8275tVnO/qOHbfuYg3kzw6Je7uWw== + dependencies: + eslint-restricted-globals "^0.1.1" + object.assign "^4.1.0" + object.entries "^1.0.4" + +eslint-config-airbnb@^17.1.0: + version "17.1.0" + resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-17.1.0.tgz#3964ed4bc198240315ff52030bf8636f42bc4732" + integrity sha512-R9jw28hFfEQnpPau01NO5K/JWMGLi6aymiF6RsnMURjTk+MqZKllCqGK/0tOvHkPi/NWSSOU2Ced/GX++YxLnw== + dependencies: + eslint-config-airbnb-base "^13.1.0" + object.assign "^4.1.0" + object.entries "^1.0.4" + +eslint-config-prettier@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-4.2.0.tgz#70b946b629cd0e3e98233fd9ecde4cb9778de96c" + integrity sha512-y0uWc/FRfrHhpPZCYflWC8aE0KRJRY04rdZVfl8cL3sEZmOYyaBdhdlQPjKZBnuRMyLVK+JUZr7HaZFClQiH4w== + dependencies: + get-stdin "^6.0.0" + +eslint-import-resolver-node@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" + integrity sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q== + dependencies: + debug "^2.6.9" + resolve "^1.5.0" + +eslint-module-utils@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.4.0.tgz#8b93499e9b00eab80ccb6614e69f03678e84e09a" + integrity sha512-14tltLm38Eu3zS+mt0KvILC3q8jyIAH518MlG+HO0p+yK885Lb1UHTY/UgR91eOyGdmxAPb+OLoW4znqIT6Ndw== + dependencies: + debug "^2.6.8" + pkg-dir "^2.0.0" + +eslint-plugin-import@^2.16.0: + version "2.17.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.17.2.tgz#d227d5c6dc67eca71eb590d2bb62fb38d86e9fcb" + integrity sha512-m+cSVxM7oLsIpmwNn2WXTJoReOF9f/CtLMo7qOVmKd1KntBy0hEcuNZ3erTmWjx+DxRO0Zcrm5KwAvI9wHcV5g== + dependencies: + array-includes "^3.0.3" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.4.0" + has "^1.0.3" + lodash "^4.17.11" + minimatch "^3.0.4" + read-pkg-up "^2.0.0" + resolve "^1.10.0" + +eslint-plugin-jsx-a11y@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.1.tgz#4ebba9f339b600ff415ae4166e3e2e008831cf0c" + integrity sha512-cjN2ObWrRz0TTw7vEcGQrx+YltMvZoOEx4hWU8eEERDnBIU00OTq7Vr+jA7DFKxiwLNv4tTh5Pq2GUNEa8b6+w== + dependencies: + aria-query "^3.0.0" + array-includes "^3.0.3" + ast-types-flow "^0.0.7" + axobject-query "^2.0.2" + damerau-levenshtein "^1.0.4" + emoji-regex "^7.0.2" + has "^1.0.3" + jsx-ast-utils "^2.0.1" + +eslint-plugin-prettier@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.0.1.tgz#19d521e3981f69dd6d14f64aec8c6a6ac6eb0b0d" + integrity sha512-/PMttrarPAY78PLvV3xfWibMOdMDl57hmlQ2XqFeA37wd+CJ7WSxV7txqjVPHi/AAFKd2lX0ZqfsOc/i5yFCSQ== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-plugin-react@^7.12.4: + version "7.13.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.13.0.tgz#bc13fd7101de67996ea51b33873cd9dc2b7e5758" + integrity sha512-uA5LrHylu8lW/eAH3bEQe9YdzpPaFd9yAJTwTi/i/BKTD7j6aQMKVAdGM/ML72zD6womuSK7EiGtMKuK06lWjQ== + dependencies: + array-includes "^3.0.3" + doctrine "^2.1.0" + has "^1.0.3" + jsx-ast-utils "^2.1.0" + object.fromentries "^2.0.0" + prop-types "^15.7.2" + resolve "^1.10.1" + +eslint-restricted-globals@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7" + integrity sha1-NfDVy8ZMLj7WLpO0saevBbp+1Nc= + +eslint-scope@3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" + integrity sha1-PWPD7f2gLgbgGkUq2IyqzHzctug= + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-scope@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" + integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.3.1.tgz#9a851ba89ee7c460346f97cf8939c7298827e512" + integrity sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q== + +eslint-visitor-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" + integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ== + +eslint@^5.16.0: + version "5.16.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" + integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.9.1" + chalk "^2.1.0" + cross-spawn "^6.0.5" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^4.0.3" + eslint-utils "^1.3.1" + eslint-visitor-keys "^1.0.0" + espree "^5.0.1" + esquery "^1.0.1" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.7.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^6.2.2" + js-yaml "^3.13.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.11" + minimatch "^3.0.4" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + progress "^2.0.0" + regexpp "^2.0.1" + semver "^5.5.1" + strip-ansi "^4.0.0" + strip-json-comments "^2.0.1" + table "^5.2.3" + text-table "^0.2.0" + +espree@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-5.0.1.tgz#5d6526fa4fc7f0788a5cf75b15f30323e2f81f7a" + integrity sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A== + dependencies: + acorn "^6.0.7" + acorn-jsx "^5.0.0" + eslint-visitor-keys "^1.0.0" + esprima@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" @@ -2236,7 +2487,21 @@ esprima@^4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -estraverse@^4.2.0: +esquery@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== + dependencies: + estraverse "^4.0.0" + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== + dependencies: + estraverse "^4.1.0" + +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" integrity sha1-De4/7TH81GlhjOc0IJn8GvoL2xM= @@ -2439,6 +2704,11 @@ fast-deep-equal@^2.0.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -2511,6 +2781,13 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + filename-regex@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" @@ -2567,7 +2844,7 @@ find-cache-dir@^2.0.0: make-dir "^2.0.0" pkg-dir "^3.0.0" -find-up@^2.0.0: +find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= @@ -2581,6 +2858,20 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916" + integrity sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg== + for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -2675,6 +2966,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + gauge@~1.2.5: version "1.2.7" resolved "https://registry.yarnpkg.com/gauge/-/gauge-1.2.7.tgz#e9cec5483d3d4ee0ef44b60a7d99e4935e136d93" @@ -2710,6 +3006,11 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -2769,7 +3070,7 @@ global@^4.3.0: min-document "^2.19.0" process "~0.5.1" -globals@^11.1.0: +globals@^11.1.0, globals@^11.7.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== @@ -2963,6 +3264,11 @@ ignore-walk@^3.0.1: dependencies: minimatch "^3.0.4" +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + image-size@^0.6.0: version "0.6.3" resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2" @@ -2981,6 +3287,14 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" +import-fresh@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.0.0.tgz#a3d897f420cab0e671236897f75bc14b4885c390" + integrity sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + import-local@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" @@ -3032,7 +3346,7 @@ inquirer@^3.0.6: strip-ansi "^4.0.0" through "^2.3.6" -inquirer@^6.2.0: +inquirer@^6.2.0, inquirer@^6.2.2: version "6.3.1" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.3.1.tgz#7a413b5e7950811013a3db491c61d1f3b776e8e7" integrity sha512-MmL624rfkFt4TG9y/Jvmt8vdmOo836U7Y0Hxr2aFk3RelZEGX4Igk0KabWrcaaZaTv9uzglOqWh1Vly+FAWAXA== @@ -3284,7 +3598,7 @@ isarray@0.0.1: resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= -isarray@1.0.0, isarray@~1.0.0: +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= @@ -3860,6 +4174,11 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + json-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" @@ -3908,6 +4227,13 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +jsx-ast-utils@^2.0.1, jsx-ast-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.1.0.tgz#0ee4e2c971fb9601c67b5641b71be80faecf0b36" + integrity sha512-yDGDG2DS4JcqhA6blsuYbtsT09xL8AoLuUR2Gb5exrw7UEM19sBcOTq+YBBhrNbl0PUC4R4LnFu+dHg2HKeVvA== + dependencies: + array-includes "^3.0.3" + kind-of@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-1.1.0.tgz#140a3d2d41a36d2efcfa9377b62c24f8495a5c44" @@ -3973,7 +4299,7 @@ leven@^2.1.0: resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580" integrity sha1-wuep93IJTe6dNCAq6KzORoeHVYA= -levn@~0.3.0: +levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= @@ -4852,7 +5178,7 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-keys@^1.0.12: +object-keys@^1.0.11, object-keys@^1.0.12: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -4864,6 +5190,36 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +object.entries@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.0.tgz#2024fc6d6ba246aee38bdb0ffd5cfbcf371b7519" + integrity sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.12.0" + function-bind "^1.1.1" + has "^1.0.3" + +object.fromentries@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.0.tgz#49a543d92151f8277b3ac9600f1e930b189d30ab" + integrity sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA== + dependencies: + define-properties "^1.1.2" + es-abstract "^1.11.0" + function-bind "^1.1.1" + has "^1.0.1" + object.getownpropertydescriptors@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" @@ -4942,7 +5298,7 @@ optimist@^0.6.1: minimist "~0.0.1" wordwrap "~0.0.2" -optionator@^0.8.1: +optionator@^0.8.1, optionator@^0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" integrity sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q= @@ -5060,6 +5416,13 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parse-glob@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" @@ -5115,6 +5478,11 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= +path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= + path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" @@ -5173,6 +5541,13 @@ pirates@^4.0.0, pirates@^4.0.1: dependencies: node-modules-regexp "^1.0.0" +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + pkg-dir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" @@ -5229,6 +5604,18 @@ preserve@^0.2.0: resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.0.tgz#53b303676eed22cc14a9f0cec09b477b3026c008" + integrity sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw== + pretty-format@24.0.0-alpha.6: version "24.0.0-alpha.6" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.0.0-alpha.6.tgz#25ad2fa46b342d6278bf241c5d2114d4376fbac1" @@ -5274,6 +5661,11 @@ progress@2.0.0: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" integrity sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8= +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + promise-polyfill@^7.1.1: version "7.1.2" resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-7.1.2.tgz#ab05301d8c28536301622d69227632269a70ca3b" @@ -5823,6 +6215,11 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexpp@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== + regexpu-core@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.4.tgz#080d9d02289aa87fe1667a4f5136bc98a6aebaae" @@ -5931,6 +6328,11 @@ resolve-from@^3.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" integrity sha1-six699nWiBvItuZTM17rywoYh0g= +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + resolve-url@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" @@ -5941,7 +6343,7 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@^1.10.0, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1: +resolve@^1.10.0, resolve@^1.10.1, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1: version "1.10.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.1.tgz#664842ac960795bbe758221cdccda61fb64b5f18" integrity sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA== @@ -5961,7 +6363,7 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3: +rimraf@2.6.3, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== @@ -6222,6 +6624,15 @@ slash@^2.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + slide@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" @@ -6471,7 +6882,7 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= -strip-json-comments@~2.0.1: +strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= @@ -6505,6 +6916,16 @@ symbol-tree@^3.2.2: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" integrity sha1-rifbOPZgp64uHDt9G8KQgZuFGeY= +table@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/table/-/table-5.2.3.tgz#cde0cc6eb06751c009efab27e8c820ca5b67b7f2" + integrity sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ== + dependencies: + ajv "^6.9.1" + lodash "^4.17.11" + slice-ansi "^2.1.0" + string-width "^3.0.0" + tar@^4: version "4.4.8" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" @@ -6536,6 +6957,11 @@ test-exclude@^5.2.3: read-pkg-up "^4.0.0" require-main-filename "^2.0.0" +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + throat@^4.0.0, throat@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" @@ -6977,6 +7403,13 @@ write-file-atomic@^1.2.0: imurmurhash "^0.1.4" slide "^1.1.5" +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + ws@^1.1.0, ws@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51"