Archived
🎨 Add prettier and eslint, then format
This commit is contained in:
1 parent
ab77f9dea9
commit
148ea8e7c2
33 files changed
+2709
-2118
No files matched your search
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 120,
|
||||
"arrowParens": "always"
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
+22
-23
@@ -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 (
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<ApolloProvider client={apolloClient}>
|
||||
<Dusken />
|
||||
</ApolloProvider>
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
)
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<PersistGate loading={null} persistor={persistor}>
|
||||
<ApolloProvider client={apolloClient}>
|
||||
<Dusken />
|
||||
</ApolloProvider>
|
||||
</PersistGate>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
}
|
||||
+12
-11
@@ -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 (<Root>
|
||||
<DuskenNavigation/>
|
||||
</Root>)
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Root>
|
||||
<DuskenNavigation />
|
||||
</Root>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect()(DuskenContainer);
|
||||
export default connect()(DuskenContainer);
|
||||
+75
-62
@@ -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,
|
||||
};
|
||||
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,
|
||||
};
|
||||
+135
-129
@@ -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'));
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+172
-137
@@ -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 (
|
||||
<ScrollView>
|
||||
<View style={styles.card}>
|
||||
<Text style={[styles.header, {marginTop: 8}]}>Det Norske Studentersamfund</Text>
|
||||
<TouchableOpacity onPress={() => Linking.openURL('https://www.studentersamfundet.no/')}>
|
||||
<Image source={{uri: 'https://galtinn.neuf.no/static/dist/images/logo.png'}} style={styles.logo} resizeMode="contain" />
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.header}>Følg oss på</Text>
|
||||
<View style={[styles.section, {flex: 1, flexDirection: 'row'}]}>
|
||||
<TouchableOpacity style={styles.someBtn} onPress={this.openFacebook}>
|
||||
<Icon name="logo-facebook" style={styles.largeIcon} /><NBText>Facebook</NBText>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.someBtn} onPress={this.openInstagram}>
|
||||
<Icon name="logo-instagram" style={styles.largeIcon} /><NBText>Instagram</NBText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
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));
|
||||
}
|
||||
|
||||
<Text style={styles.header}>Adresse</Text>
|
||||
<Text style={[styles.paragraph, {textAlign: 'center'}]}>Det Norske Studentersamfund{"\n"}Slemdalsveien 15, 0369 Oslo</Text>
|
||||
<View style={styles.section}>
|
||||
<Button iconLeft onPress={this.openMap}>
|
||||
<Icon name="map" style={styles.icon} /><NBText> Vis i kart</NBText>
|
||||
</Button>
|
||||
</View>
|
||||
<Text style={styles.header}>Åpningstider</Text>
|
||||
<View style={styles.section}>
|
||||
<Button iconLeft onPress={() => Linking.openURL('https://studentersamfundet.no/aapningstider/')}>
|
||||
<Icon name="time" style={styles.icon} /><NBText>Vis Åpningstider</NBText>
|
||||
</Button>
|
||||
</View>
|
||||
<Text style={styles.header}>Ta kontakt</Text>
|
||||
<View style={[styles.section, {flex: 1, flexDirection: 'row'}]}>
|
||||
<View>
|
||||
<Button iconLeft onPress={() => Linking.openURL('tel:+4794430002')} style={{marginHorizontal: 10}}>
|
||||
<Icon name="call" style={styles.icon} /><NBText>Ring</NBText>
|
||||
</Button>
|
||||
</View>
|
||||
<View>
|
||||
<Button iconLeft onPress={() => Linking.openURL('mailto:ledelsen@studentersamfundet.no')} style={{marginHorizontal: 10}}>
|
||||
<Icon name="mail" style={styles.icon} /><NBText>Send e-post</NBText>
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
render() {
|
||||
return (
|
||||
<ScrollView>
|
||||
<View style={styles.card}>
|
||||
<Text style={[styles.header, { marginTop: 8 }]}>Det Norske Studentersamfund</Text>
|
||||
<TouchableOpacity onPress={() => Linking.openURL('https://www.studentersamfundet.no/')}>
|
||||
<Image
|
||||
source={{ uri: 'https://galtinn.neuf.no/static/dist/images/logo.png' }}
|
||||
style={styles.logo}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<Text style={styles.header}>Følg oss på</Text>
|
||||
<View style={[styles.section, { flex: 1, flexDirection: 'row' }]}>
|
||||
<TouchableOpacity style={styles.someBtn} onPress={this.openFacebook}>
|
||||
<Icon name="logo-facebook" style={styles.largeIcon} />
|
||||
<NBText>Facebook</NBText>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity style={styles.someBtn} onPress={this.openInstagram}>
|
||||
<Icon name="logo-instagram" style={styles.largeIcon} />
|
||||
<NBText>Instagram</NBText>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
<Text style={styles.header}>Chateau Neuf</Text>
|
||||
<Image source={{uri: 'https://studentersamfundet.no/wp-content/uploads/2015/04/26_KUL_Chateauneuf-1280x720.jpg'}} style={styles.featuredImage} resizeMode="contain" />
|
||||
<Text style={styles.paragraph}>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.</Text>
|
||||
<Text style={styles.paragraph}>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.</Text>
|
||||
<Text style={styles.paragraph}>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.</Text>
|
||||
<Text style={styles.header}>Adresse</Text>
|
||||
<Text style={[styles.paragraph, { textAlign: 'center' }]}>
|
||||
Det Norske Studentersamfund{'\n'}Slemdalsveien 15, 0369 Oslo
|
||||
</Text>
|
||||
<View style={styles.section}>
|
||||
<Button iconLeft onPress={this.openMap}>
|
||||
<Icon name="map" style={styles.icon} />
|
||||
<NBText> Vis i kart</NBText>
|
||||
</Button>
|
||||
</View>
|
||||
<Text style={styles.header}>Åpningstider</Text>
|
||||
<View style={styles.section}>
|
||||
<Button iconLeft onPress={() => Linking.openURL('https://studentersamfundet.no/aapningstider/')}>
|
||||
<Icon name="time" style={styles.icon} />
|
||||
<NBText>Vis Åpningstider</NBText>
|
||||
</Button>
|
||||
</View>
|
||||
<Text style={styles.header}>Ta kontakt</Text>
|
||||
<View style={[styles.section, { flex: 1, flexDirection: 'row' }]}>
|
||||
<View>
|
||||
<Button iconLeft onPress={() => Linking.openURL('tel:+4794430002')} style={{ marginHorizontal: 10 }}>
|
||||
<Icon name="call" style={styles.icon} />
|
||||
<NBText>Ring</NBText>
|
||||
</Button>
|
||||
</View>
|
||||
<View>
|
||||
<Button
|
||||
iconLeft
|
||||
onPress={() => Linking.openURL('mailto:ledelsen@studentersamfundet.no')}
|
||||
style={{ marginHorizontal: 10 }}
|
||||
>
|
||||
<Icon name="mail" style={styles.icon} />
|
||||
<NBText>Send e-post</NBText>
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/*<HTMLView style={styles.content} stylesheet={HTMLStyles} value={this.props.openingHours}/>*/}
|
||||
</View>
|
||||
</ScrollView>
|
||||
)
|
||||
}
|
||||
<Text style={styles.header}>Chateau Neuf</Text>
|
||||
<Image
|
||||
source={{ uri: 'https://studentersamfundet.no/wp-content/uploads/2015/04/26_KUL_Chateauneuf-1280x720.jpg' }}
|
||||
style={styles.featuredImage}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
<Text style={styles.paragraph}>
|
||||
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.
|
||||
</Text>
|
||||
<Text style={styles.paragraph}>
|
||||
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.
|
||||
</Text>
|
||||
<Text style={styles.paragraph}>
|
||||
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.
|
||||
</Text>
|
||||
|
||||
{/* <HTMLView style={styles.content} stylesheet={HTMLStyles} value={this.props.openingHours}/> */}
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
// },
|
||||
// });
|
||||
// });
|
||||
@@ -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 <About {...this.state} />;
|
||||
}
|
||||
render() {
|
||||
return <About {...this.state} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default connect()(AboutContainer);
|
||||
export default connect()(AboutContainer);
|
||||
+292
-272
@@ -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 (
|
||||
<TouchableOpacity style={styles.metaInner} onPress={() => this.onTicketUrlPress(item.ticket_url)}>
|
||||
<View style={styles.metaIcon}>
|
||||
<Icon name="card" style={styles.icons} />
|
||||
</View>
|
||||
<View style={{ flexDirection: 'column' }}>
|
||||
<Text style={styles.metaText}>{text}</Text>
|
||||
<Text style={styles.metaSubtitle}>Kjøp billett</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
onTicketUrlPress(link) {
|
||||
Linking.openURL(link);
|
||||
return (
|
||||
<View style={styles.metaInner}>
|
||||
<View style={styles.metaIcon}>
|
||||
<Icon name="card" style={styles.icons} />
|
||||
</View>
|
||||
<View style={{ flexDirection: 'column' }}>
|
||||
<Text style={styles.metaText}>{text}</Text>
|
||||
<Text style={styles.metaSubtitle}>Billett i døra</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<TouchableOpacity onPress={() => this.onFacebookUrlPress(item.facebook_url)} style={styles.metaInner}>
|
||||
<View style={[styles.metaIcon, { marginTop: 0 }]}>
|
||||
<Icon name="logo-facebook" style={styles.icons} />
|
||||
</View>
|
||||
<View>
|
||||
<Text style={[styles.metaText, { paddingTop: 2 }]}>På Facebook</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
showImage() {
|
||||
const { item } = this.props;
|
||||
|
||||
if (item.thumbnail.medium_large) {
|
||||
return <Image source={{ uri: item.thumbnail.medium_large }} style={styles.image} />;
|
||||
}
|
||||
}
|
||||
|
||||
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 <Text style={styles.year}>{this._formatYear(item.start_time)}</Text>;
|
||||
}
|
||||
}
|
||||
|
||||
showPriceAndTicket() {
|
||||
const {item} = this.props;
|
||||
const text = this._formatTicketText();
|
||||
render() {
|
||||
const { item } = this.props;
|
||||
|
||||
if(text === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
if(item.ticket_url) {
|
||||
return (
|
||||
<TouchableOpacity style={styles.metaInner} onPress={() => this.onTicketUrlPress(item.ticket_url)}>
|
||||
<View style={styles.metaIcon}><Icon name="card" style={styles.icons} /></View>
|
||||
<View style={{flexDirection: 'column'}}>
|
||||
<Text style={styles.metaText}>{text}</Text>
|
||||
<Text style={styles.metaSubtitle}>Kjøp billett</Text>
|
||||
</View>
|
||||
</TouchableOpacity>);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.metaInner}>
|
||||
<View style={styles.metaIcon}><Icon name="card" style={styles.icons} /></View>
|
||||
<View style={{flexDirection: 'column'}}>
|
||||
<Text style={styles.metaText}>{text}</Text>
|
||||
<Text style={styles.metaSubtitle}>Billett i døra</Text>
|
||||
</View>
|
||||
</View>)
|
||||
}
|
||||
|
||||
showFacebutton() {
|
||||
const {item} = this.props;
|
||||
if (!item.facebook_url) { return;}
|
||||
|
||||
return (
|
||||
<TouchableOpacity onPress={() => this.onFacebookUrlPress(item.facebook_url)} style={styles.metaInner}>
|
||||
<View style={[styles.metaIcon, {marginTop: 0}]}><Icon name="logo-facebook" style={styles.icons} /></View>
|
||||
<View>
|
||||
<Text style={[styles.metaText, {paddingTop: 2}]}>På Facebook</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
showImage() {
|
||||
const {item} = this.props;
|
||||
|
||||
if (item.thumbnail.medium_large) {
|
||||
return <Image source={{uri: item.thumbnail.medium_large}} style={styles.image} />;
|
||||
}
|
||||
}
|
||||
|
||||
showYear() {
|
||||
const {item} = this.props;
|
||||
|
||||
if(moment().year() !== moment(item.start_time).year()) {
|
||||
return <Text style={styles.year}>{this._formatYear(item.start_time)}</Text>
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {item} = this.props;
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
{this.showImage()}
|
||||
<View style={styles.card}>
|
||||
{/* Date and title */}
|
||||
<View style={styles.dateAndTitle}>
|
||||
<View style={styles.dateContainer}>
|
||||
<View style={[styles.card, styles.dateCard]}>
|
||||
<Text style={styles.day}>{this._formatDay(item.start_time)}</Text>
|
||||
<Text style={styles.month}>{this._formatMonth(item.start_time)}</Text>
|
||||
{this.showYear()}
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.titleContainer}>
|
||||
<Text style={styles.title} numberOfLines={2}>{item.title.decoded}</Text>
|
||||
</View>
|
||||
</View>
|
||||
{/* Time and calendar */}
|
||||
<TouchableOpacity style={styles.metaInner}>
|
||||
<View style={styles.metaIcon}><Icon name="time" style={styles.icons} /></View>
|
||||
<View style={{flexDirection: 'column'}}>
|
||||
<Text style={styles.metaText}>{this._formatLocalDay(item.start_time)} kl. {this._formatTime(item.start_time)} - {this._formatTime(item.end_time)}</Text>
|
||||
<Text style={styles.metaSubtitle}>{this._formatRelative(item.start_time)}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
{/* Location and maps */}
|
||||
<TouchableOpacity style={styles.metaInner} onPress={() => this.onLocationPress(item.venue)}>
|
||||
<View style={[styles.metaIcon, {paddingLeft: 1}]}><Icon name="pin" style={styles.icons} /></View>
|
||||
<View style={{flexDirection: 'column'}}>
|
||||
<Text style={styles.metaText}>{item.venue}</Text>
|
||||
<Text style={styles.metaSubtitle} numberOfLines={1}>{this._formatLocation()}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
{this.showPriceAndTicket()}
|
||||
{this.showFacebutton()}
|
||||
<HTMLView style={styles.content} stylesheet={HTMLStyles} value={item.content.rendered}/>
|
||||
</View>
|
||||
</ScrollView>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<ScrollView>
|
||||
{this.showImage()}
|
||||
<View style={styles.card}>
|
||||
{/* Date and title */}
|
||||
<View style={styles.dateAndTitle}>
|
||||
<View style={styles.dateContainer}>
|
||||
<View style={[styles.card, styles.dateCard]}>
|
||||
<Text style={styles.day}>{this._formatDay(item.start_time)}</Text>
|
||||
<Text style={styles.month}>{this._formatMonth(item.start_time)}</Text>
|
||||
{this.showYear()}
|
||||
</View>
|
||||
</View>
|
||||
<View style={styles.titleContainer}>
|
||||
<Text style={styles.title} numberOfLines={2}>
|
||||
{item.title.decoded}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
{/* Time and calendar */}
|
||||
<TouchableOpacity style={styles.metaInner}>
|
||||
<View style={styles.metaIcon}>
|
||||
<Icon name="time" style={styles.icons} />
|
||||
</View>
|
||||
<View style={{ flexDirection: 'column' }}>
|
||||
<Text style={styles.metaText}>
|
||||
{this._formatLocalDay(item.start_time)} kl. {this._formatTime(item.start_time)} -{' '}
|
||||
{this._formatTime(item.end_time)}
|
||||
</Text>
|
||||
<Text style={styles.metaSubtitle}>{this._formatRelative(item.start_time)}</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
{/* Location and maps */}
|
||||
<TouchableOpacity style={styles.metaInner} onPress={() => this.onLocationPress(item.venue)}>
|
||||
<View style={[styles.metaIcon, { paddingLeft: 1 }]}>
|
||||
<Icon name="pin" style={styles.icons} />
|
||||
</View>
|
||||
<View style={{ flexDirection: 'column' }}>
|
||||
<Text style={styles.metaText}>{item.venue}</Text>
|
||||
<Text style={styles.metaSubtitle} numberOfLines={1}>
|
||||
{this._formatLocation()}
|
||||
</Text>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
{this.showPriceAndTicket()}
|
||||
{this.showFacebutton()}
|
||||
<HTMLView style={styles.content} stylesheet={HTMLStyles} value={item.content.rendered} />
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
});
|
||||
p: {
|
||||
fontSize: 16,
|
||||
marginBottom: -30,
|
||||
color: '#000',
|
||||
},
|
||||
ul: {
|
||||
fontSize: 16,
|
||||
color: '#000',
|
||||
marginTop: -20,
|
||||
marginBottom: -30,
|
||||
},
|
||||
});
|
||||
+128
-106
@@ -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}) => (
|
||||
<ListItem button onPress={() => { this._onPressItem(item); }} style={styles.listItem}>
|
||||
<Body style={{flex: 5}}>
|
||||
<Text style={styles.listItemTitle} numberOfLines={1}>{item.title.decoded}</Text>
|
||||
<Text style={styles.listItemTime}>{this._formatTime(item.start_time)}</Text>
|
||||
</Body>
|
||||
<Right style={{flex: 1}}>
|
||||
<Icon name="arrow-forward" />
|
||||
</Right>
|
||||
</ListItem>
|
||||
_renderItem = ({ item }) => (
|
||||
<ListItem
|
||||
button
|
||||
onPress={() => {
|
||||
this._onPressItem(item);
|
||||
}}
|
||||
style={styles.listItem}
|
||||
>
|
||||
<Body style={{ flex: 5 }}>
|
||||
<Text style={styles.listItemTitle} numberOfLines={1}>
|
||||
{item.title.decoded}
|
||||
</Text>
|
||||
<Text style={styles.listItemTime}>{this._formatTime(item.start_time)}</Text>
|
||||
</Body>
|
||||
<Right style={{ flex: 1 }}>
|
||||
<Icon name="arrow-forward" />
|
||||
</Right>
|
||||
</ListItem>
|
||||
);
|
||||
|
||||
_renderSectionHeader = ({ section }) => {
|
||||
return (
|
||||
<ListItem style={styles.sectionHeader}>
|
||||
<Body>
|
||||
<Text style={styles.sectionTitle}>{section.title}</Text>
|
||||
</Body>
|
||||
</ListItem>
|
||||
);
|
||||
};
|
||||
|
||||
_renderSectionHeader = ({section}) => {
|
||||
return (<ListItem style={styles.sectionHeader}>
|
||||
<Body>
|
||||
<Text style={styles.sectionTitle}>{section.title}</Text>
|
||||
</Body>
|
||||
</ListItem>);
|
||||
};
|
||||
|
||||
_renderFooter = () => {
|
||||
if(!this.props.loading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (<View style={{paddingVertical: 20}}>
|
||||
<Spinner color="#f58220"/>
|
||||
</View>);
|
||||
};
|
||||
|
||||
_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 (<Content>
|
||||
<Card style={{margin: 4}}>
|
||||
<CardItem onPress={() => { this._onPressItem(item); }}>
|
||||
<Text style={styles.loadingText}>Kunne ikke hente programmet...</Text>
|
||||
</CardItem>
|
||||
<CardItem>
|
||||
<Button onPress={this.props.handleRefresh}><Text>Prøv igjen</Text></Button>
|
||||
</CardItem>
|
||||
</Card>
|
||||
</Content>)
|
||||
}
|
||||
return (
|
||||
<View style={{ paddingVertical: 20 }}>
|
||||
<Spinner color="#f58220" />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
return (<SectionList
|
||||
sections={this.props.eventsSectioned}
|
||||
renderItem={this._renderItem}
|
||||
renderSectionHeader={this._renderSectionHeader}
|
||||
keyExtractor={(item) => 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 <View style={styles.listHeader}/> }}
|
||||
/>);
|
||||
_onPressItem = (item) => {
|
||||
this.props.showEvent(item);
|
||||
};
|
||||
|
||||
_formatTime(time) {
|
||||
return moment(time).format('llll');
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.props.error) {
|
||||
return (
|
||||
<Content>
|
||||
<Card style={{ margin: 4 }}>
|
||||
<CardItem
|
||||
onPress={() => {
|
||||
this._onPressItem(item);
|
||||
}}
|
||||
>
|
||||
<Text style={styles.loadingText}>Kunne ikke hente programmet...</Text>
|
||||
</CardItem>
|
||||
<CardItem>
|
||||
<Button onPress={this.props.handleRefresh}>
|
||||
<Text>Prøv igjen</Text>
|
||||
</Button>
|
||||
</CardItem>
|
||||
</Card>
|
||||
</Content>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionList
|
||||
sections={this.props.eventsSectioned}
|
||||
renderItem={this._renderItem}
|
||||
renderSectionHeader={this._renderSectionHeader}
|
||||
keyExtractor={(item) => 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 <View style={styles.listHeader} />;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
},
|
||||
});
|
||||
@@ -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 (<EventList
|
||||
showEvent={this.showEvent}
|
||||
handleRefresh={this.handleRefresh}
|
||||
handleLoadMore={this.handleLoadMore}
|
||||
{...this.state}
|
||||
/>)
|
||||
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 (
|
||||
<EventList
|
||||
showEvent={this.showEvent}
|
||||
handleRefresh={this.handleRefresh}
|
||||
handleLoadMore={this.handleLoadMore}
|
||||
{...this.state}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect()(EventListContainer);
|
||||
export default connect()(EventListContainer);
|
||||
+157
-146
@@ -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 <Item stackedLabel last error>
|
||||
<Label>E-post / Brukernavn</Label>
|
||||
<Input
|
||||
keyboardType="email-address"
|
||||
autoFocus={true}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
returnKeyType="next"
|
||||
onChangeText={this.handleEmail}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.passwordInput._root.focus()
|
||||
}}
|
||||
/>
|
||||
</Item>;
|
||||
}
|
||||
|
||||
return <Item stackedLabel last>
|
||||
<Label>E-post / Brukernavn</Label>
|
||||
<Input
|
||||
keyboardType="email-address"
|
||||
autoFocus={true}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
returnKeyType="next"
|
||||
onChangeText={this.handleEmail}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.passwordInput._root.focus()
|
||||
}}
|
||||
/>
|
||||
</Item>;
|
||||
}
|
||||
|
||||
passwordInput() {
|
||||
if (this.props.loginError && this.props.loginError.password) {
|
||||
return <Item stackedLabel last error>
|
||||
<Label>Passord</Label>
|
||||
<Input
|
||||
ref="passwordInput"
|
||||
secureTextEntry={true}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
onChangeText={this.handlePassword}
|
||||
onSubmitEditing={this.onLoginPress}
|
||||
/>
|
||||
</Item>
|
||||
}
|
||||
return <Item stackedLabel last>
|
||||
<Label>Passord</Label>
|
||||
<Input
|
||||
ref="passwordInput"
|
||||
secureTextEntry={true}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
onChangeText={this.handlePassword}
|
||||
onSubmitEditing={this.onLoginPress}
|
||||
/>
|
||||
emailInput() {
|
||||
if (this.props.loginError && this.props.loginError.username) {
|
||||
return (
|
||||
<Item stackedLabel last error>
|
||||
<Label>E-post / Brukernavn</Label>
|
||||
<Input
|
||||
keyboardType="email-address"
|
||||
autoFocus={true}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
returnKeyType="next"
|
||||
onChangeText={this.handleEmail}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.passwordInput._root.focus();
|
||||
}}
|
||||
/>
|
||||
</Item>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container style={styles.container}>
|
||||
<Content keyboardShouldPersistTaps='always'>
|
||||
<Form style={styles.card}>
|
||||
{this.emailInput()}
|
||||
{this.passwordInput()}
|
||||
{this.showError()}
|
||||
<Button full onPress={this.onLoginPress} style={styles.loginButton}>
|
||||
<Text>Logg inn</Text>
|
||||
</Button>
|
||||
<Button
|
||||
small
|
||||
light
|
||||
onPress={() => { Linking.openURL(Config.DUSKEN_FORGOT_PASSWORD_URL); } }
|
||||
style={styles.forgotPasswordButton}
|
||||
>
|
||||
<Text>Glemt passord?</Text>
|
||||
</Button>
|
||||
{this.showSpinner()}
|
||||
</Form>
|
||||
</Content>
|
||||
</Container>
|
||||
);
|
||||
return (
|
||||
<Item stackedLabel last>
|
||||
<Label>E-post / Brukernavn</Label>
|
||||
<Input
|
||||
keyboardType="email-address"
|
||||
autoFocus={true}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
returnKeyType="next"
|
||||
onChangeText={this.handleEmail}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.passwordInput._root.focus();
|
||||
}}
|
||||
/>
|
||||
</Item>
|
||||
);
|
||||
}
|
||||
|
||||
passwordInput() {
|
||||
if (this.props.loginError && this.props.loginError.password) {
|
||||
return (
|
||||
<Item stackedLabel last error>
|
||||
<Label>Passord</Label>
|
||||
<Input
|
||||
ref="passwordInput"
|
||||
secureTextEntry={true}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
onChangeText={this.handlePassword}
|
||||
onSubmitEditing={this.onLoginPress}
|
||||
/>
|
||||
</Item>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Item stackedLabel last>
|
||||
<Label>Passord</Label>
|
||||
<Input
|
||||
ref="passwordInput"
|
||||
secureTextEntry={true}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
onChangeText={this.handlePassword}
|
||||
onSubmitEditing={this.onLoginPress}
|
||||
/>
|
||||
</Item>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container style={styles.container}>
|
||||
<Content keyboardShouldPersistTaps="always">
|
||||
<Form style={styles.card}>
|
||||
{this.emailInput()}
|
||||
{this.passwordInput()}
|
||||
{this.showError()}
|
||||
<Button full onPress={this.onLoginPress} style={styles.loginButton}>
|
||||
<Text>Logg inn</Text>
|
||||
</Button>
|
||||
<Button
|
||||
small
|
||||
light
|
||||
onPress={() => {
|
||||
Linking.openURL(Config.DUSKEN_FORGOT_PASSWORD_URL);
|
||||
}}
|
||||
style={styles.forgotPasswordButton}
|
||||
>
|
||||
<Text>Glemt passord?</Text>
|
||||
</Button>
|
||||
{this.showSpinner()}
|
||||
</Form>
|
||||
</Content>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<View style={styles.errorBox}>
|
||||
<Text style={styles.errorMessage}>{errorFormatted}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
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 <View style={styles.errorBox}><Text style={styles.errorMessage}>{errorFormatted}</Text></View>
|
||||
}
|
||||
else {
|
||||
return <View style={styles.errorBox}/>
|
||||
}
|
||||
};
|
||||
return <View style={styles.errorBox} />;
|
||||
};
|
||||
|
||||
showSpinner = () => {
|
||||
if (this.props.isLoggingIn) {
|
||||
return <Spinner color="#f58220"/>
|
||||
}
|
||||
};
|
||||
showSpinner = () => {
|
||||
if (this.props.isLoggingIn) {
|
||||
return <Spinner color="#f58220" />;
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
});
|
||||
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,
|
||||
});
|
||||
@@ -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 <Login {...this.props} />
|
||||
}
|
||||
render() {
|
||||
return <Login {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -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 (
|
||||
<View style={styles.card}>
|
||||
<Spinner color="#f58220"/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.headerTitle}>Hei 👋</Text>
|
||||
<Text style={styles.paragraph}>Her kan du bli medlem i Det Norske Studentersamfund - Chateau Neuf og ha medlemsbeviset ditt på telefonen</Text>
|
||||
<View style={styles.button}>
|
||||
<Button onPress={this.props.onRegisterPress} full><NBText>Bli medlem</NBText></Button>
|
||||
</View>
|
||||
<Text style={styles.header}>Hva får jeg?</Text>
|
||||
<Text style={[styles.paragraph, {textAlign: 'left', paddingLeft: 4}]}>
|
||||
- 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</Text>
|
||||
<Text style={styles.em}>Allerede medlem?</Text>
|
||||
<View style={styles.button}>
|
||||
<Button onPress={this.props.onLoginPress} full><NBText>Logg inn</NBText></Button>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
)
|
||||
};
|
||||
render() {
|
||||
if (this.props.isFetchingUserData) {
|
||||
return (
|
||||
<View style={styles.card}>
|
||||
<Spinner color="#f58220" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
<View style={styles.card}>
|
||||
<Text style={styles.headerTitle}>Hei 👋</Text>
|
||||
<Text style={styles.paragraph}>
|
||||
Her kan du bli medlem i Det Norske Studentersamfund - Chateau Neuf og ha medlemsbeviset ditt på telefonen
|
||||
</Text>
|
||||
<View style={styles.button}>
|
||||
<Button onPress={this.props.onRegisterPress} full>
|
||||
<NBText>Bli medlem</NBText>
|
||||
</Button>
|
||||
</View>
|
||||
<Text style={styles.header}>Hva får jeg?</Text>
|
||||
<Text style={[styles.paragraph, { textAlign: 'left', paddingLeft: 4 }]}>
|
||||
- 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
|
||||
</Text>
|
||||
<Text style={styles.em}>Allerede medlem?</Text>
|
||||
<View style={styles.button}>
|
||||
<Button onPress={this.props.onLoginPress} full>
|
||||
<NBText>Logg inn</NBText>
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
},
|
||||
});
|
||||
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,
|
||||
},
|
||||
});
|
||||
@@ -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 <ProofContainer
|
||||
user={this.props.user}
|
||||
isAuthenticated={this.props.isAuthenticated}
|
||||
userToken={this.state.userToken}
|
||||
fetchUser={this.fetchUser}
|
||||
onLogoutPress={this.onLogoutPress}
|
||||
/>;
|
||||
}
|
||||
return <Membership
|
||||
isFetchingUserData={this.props.isFetchingUserData}
|
||||
onLoginPress={this.onLoginPress}
|
||||
onRegisterPress={this.onRegisterPress}
|
||||
/>;
|
||||
render() {
|
||||
if (this.props.user) {
|
||||
return (
|
||||
<ProofContainer
|
||||
user={this.props.user}
|
||||
isAuthenticated={this.props.isAuthenticated}
|
||||
userToken={this.state.userToken}
|
||||
fetchUser={this.fetchUser}
|
||||
onLogoutPress={this.onLogoutPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Membership
|
||||
isFetchingUserData={this.props.isFetchingUserData}
|
||||
onLoginPress={this.onLoginPress}
|
||||
onRegisterPress={this.onRegisterPress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
+236
-223
@@ -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 (
|
||||
<View style={{ alignItems: 'center', flex: 1 }}>
|
||||
<Text>Gyldig til: </Text>
|
||||
<Text style={styles.validToValue}>{validTo}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<View style={{alignItems: 'center', flex: 1}}>
|
||||
<Text>Gyldig til: </Text>
|
||||
<Text style={styles.validToValue}>{validTo}</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
membershipStatus() {
|
||||
if (!this.props.user.is_member) {
|
||||
if ( !this.props.user.last_membership ) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.expired}>
|
||||
<Text style={styles.expiredText}>Utløpt</Text>
|
||||
</View>)
|
||||
}
|
||||
|
||||
const statusText = this.props.user.is_volunteer ? '❤️ AKTIV ❤️ ' : 'MEDLEM';
|
||||
|
||||
return (
|
||||
<View style={styles.membershipStatus}>
|
||||
<TouchableOpacity onPress={this.onMembershipPress} style={styles.valid}>
|
||||
<Text style={styles.validText}>{statusText}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>);
|
||||
}
|
||||
|
||||
purchaseButton() {
|
||||
if (this.props.user.is_member) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
{this.props.isChargingMembership && <Spinner color="#f58220"/>}
|
||||
<Button
|
||||
onPress={this.props.onChargePress}
|
||||
full
|
||||
disabled={this.props.isChargingMembership || this.props.isLoadingMembershipType}
|
||||
style={styles.purchaseButton}
|
||||
>
|
||||
<NBText>{`Kjøp medlemskap (${this.props.membershipPrice} NOK)`}</NBText>
|
||||
</Button>
|
||||
<View style={styles.purchaseText}>
|
||||
<Text style={styles.purchaseTextInner}>Medlemskapet er gyldig i ett år</Text>
|
||||
</View>
|
||||
</View>)
|
||||
}
|
||||
|
||||
confetti() {
|
||||
if (this.props.user.is_member) {
|
||||
// Confetti!!!!
|
||||
return <Confetti ref={(node) => this._confettiView = node} confettiCount={Number.MAX_SAFE_INTEGER} />
|
||||
}
|
||||
}
|
||||
|
||||
logo() {
|
||||
if (!this.props.user.is_member) {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: URL to config
|
||||
return (
|
||||
<View style={{marginTop: 8}}>
|
||||
<Image
|
||||
style={{width: null, height: 100}}
|
||||
source={{uri: 'https://galtinn.neuf.no/static/dist/images/logo.png'}}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</View>)
|
||||
}
|
||||
|
||||
chargeError() {
|
||||
if (!this.props.chargeError) {
|
||||
return;
|
||||
}
|
||||
|
||||
return <View><Text style={styles.errorMessage}>{this.props.chargeError}</Text></View>
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
{this.confetti()}
|
||||
<ScrollView>
|
||||
<View style={[styles.card, {paddingVertical: 16}]}>
|
||||
{this.membershipName()}
|
||||
{this.logo()}
|
||||
{this.membershipStatus()}
|
||||
{this.chargeError()}
|
||||
{this.purchaseButton()}
|
||||
{this.membershipValidTo()}
|
||||
</View>
|
||||
<View>
|
||||
<Button onPress={this.props.onLogoutPress} style={styles.logoutButton} small>
|
||||
<NBText>Logg ut</NBText>
|
||||
</Button>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</View>
|
||||
)
|
||||
};
|
||||
|
||||
membershipName() {
|
||||
let name = `${this.props.user.first_name} ${this.props.user.last_name}`;
|
||||
if(name === ' ') {
|
||||
name = this.props.user.email;
|
||||
}
|
||||
return <View>
|
||||
<Text style={styles.nameText}>{name}</Text>
|
||||
return (
|
||||
<View style={styles.expired}>
|
||||
<Text style={styles.expiredText}>Utløpt</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const statusText = this.props.user.is_volunteer ? '❤️ AKTIV ❤️ ' : 'MEDLEM';
|
||||
|
||||
return (
|
||||
<View style={styles.membershipStatus}>
|
||||
<TouchableOpacity onPress={this.onMembershipPress} style={styles.valid}>
|
||||
<Text style={styles.validText}>{statusText}</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
purchaseButton() {
|
||||
if (this.props.user.is_member) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
{this.props.isChargingMembership && <Spinner color="#f58220" />}
|
||||
<Button
|
||||
onPress={this.props.onChargePress}
|
||||
full
|
||||
disabled={this.props.isChargingMembership || this.props.isLoadingMembershipType}
|
||||
style={styles.purchaseButton}
|
||||
>
|
||||
<NBText>{`Kjøp medlemskap (${this.props.membershipPrice} NOK)`}</NBText>
|
||||
</Button>
|
||||
<View style={styles.purchaseText}>
|
||||
<Text style={styles.purchaseTextInner}>Medlemskapet er gyldig i ett år</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
confetti() {
|
||||
if (this.props.user.is_member) {
|
||||
// Confetti!!!!
|
||||
return <Confetti ref={(node) => (this._confettiView = node)} confettiCount={Number.MAX_SAFE_INTEGER} />;
|
||||
}
|
||||
}
|
||||
|
||||
logo() {
|
||||
if (!this.props.user.is_member) {
|
||||
return;
|
||||
}
|
||||
|
||||
// FIXME: URL to config
|
||||
return (
|
||||
<View style={{ marginTop: 8 }}>
|
||||
<Image
|
||||
style={{ width: null, height: 100 }}
|
||||
source={{ uri: 'https://galtinn.neuf.no/static/dist/images/logo.png' }}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
chargeError() {
|
||||
if (!this.props.chargeError) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
<Text style={styles.errorMessage}>{this.props.chargeError}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
{this.confetti()}
|
||||
<ScrollView>
|
||||
<View style={[styles.card, { paddingVertical: 16 }]}>
|
||||
{this.membershipName()}
|
||||
{this.logo()}
|
||||
{this.membershipStatus()}
|
||||
{this.chargeError()}
|
||||
{this.purchaseButton()}
|
||||
{this.membershipValidTo()}
|
||||
</View>
|
||||
<View>
|
||||
<Button onPress={this.props.onLogoutPress} style={styles.logoutButton} small>
|
||||
<NBText>Logg ut</NBText>
|
||||
</Button>
|
||||
</View>
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
membershipName() {
|
||||
let name = `${this.props.user.first_name} ${this.props.user.last_name}`;
|
||||
if (name === ' ') {
|
||||
name = this.props.user.email;
|
||||
}
|
||||
return (
|
||||
<View>
|
||||
<Text style={styles.nameText}>{name}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
});
|
||||
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,
|
||||
},
|
||||
});
|
||||
@@ -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 (
|
||||
<Proof
|
||||
{...this.state}
|
||||
user={this.props.user}
|
||||
isChargingMembership={this.props.isChargingMembership}
|
||||
isLoadingMembershipType={this.props.data.loading}
|
||||
chargeError={this.props.chargeError ? this.props.chargeError : ''}
|
||||
onChargePress={this.openStripe}
|
||||
onLogoutPress={this.props.onLogoutPress}
|
||||
membershipPrice={this.getPrice(this.props.data.membershipTypes)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <Proof
|
||||
{...this.state}
|
||||
user={this.props.user}
|
||||
isChargingMembership={this.props.isChargingMembership}
|
||||
isLoadingMembershipType={this.props.data.loading}
|
||||
chargeError={this.props.chargeError ? this.props.chargeError : ''}
|
||||
onChargePress={this.openStripe}
|
||||
onLogoutPress={this.props.onLogoutPress}
|
||||
membershipPrice={this.getPrice(this.props.data.membershipTypes)}
|
||||
/>
|
||||
}
|
||||
|
||||
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 (
|
||||
// <RefreshControl
|
||||
// refreshing={this.state.refreshing}
|
||||
// onRefresh={this._onRefresh.bind(this)}
|
||||
// />)
|
||||
// }
|
||||
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);
|
||||
@@ -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 <Item error={this.fieldHasError('firstName')}>
|
||||
<Label>Fornavn</Label>
|
||||
<Input
|
||||
autoFocus={true}
|
||||
returnKeyType="next"
|
||||
onChangeText={(firstName) => {
|
||||
this.setState({firstName}, () => { this.validateForm('firstName'); });
|
||||
}}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.lastNameInput._root.focus()
|
||||
}}
|
||||
value={this.state.firstName}
|
||||
/>
|
||||
{this.fieldHasError('firstName') && <Icon name='close-circle' />}
|
||||
</Item>;
|
||||
}
|
||||
fieldHasError(field) {
|
||||
return this.state.touched.has(field) && field in this.state.errors;
|
||||
}
|
||||
|
||||
lastNameInput() {
|
||||
return <Item error={this.fieldHasError('lastName')}>
|
||||
<Label>Etternavn</Label>
|
||||
<Input
|
||||
ref="lastNameInput"
|
||||
returnKeyType="next"
|
||||
onChangeText={(lastName) => {
|
||||
this.setState({lastName}, () => { this.validateForm('lastName'); });
|
||||
}}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.emailInput._root.focus()
|
||||
}}
|
||||
value={this.state.lastName}
|
||||
/>
|
||||
{this.fieldHasError('lastName') && <Icon name='close-circle' />}
|
||||
</Item>;
|
||||
}
|
||||
canSubmitForm() {
|
||||
return Object.keys(this.state.errors).length === 0;
|
||||
}
|
||||
|
||||
emailInput() {
|
||||
return <Item error={this.fieldHasError('email')}>
|
||||
<Label>E-post</Label>
|
||||
<Input
|
||||
ref="emailInput"
|
||||
keyboardType="email-address"
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
returnKeyType="next"
|
||||
onChangeText={(email) => {
|
||||
this.setState({email}, () => { this.validateForm('email'); });
|
||||
}}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.phoneInput._root.focus()
|
||||
}}
|
||||
value={this.state.email}
|
||||
/>
|
||||
{this.fieldHasError('email') && <Icon name='close-circle' />}
|
||||
</Item>;
|
||||
}
|
||||
|
||||
phoneNumberInput() {
|
||||
return <Item error={this.fieldHasError('phoneNumber')}>
|
||||
<Label>Mobilnummer</Label>
|
||||
<Input
|
||||
ref="phoneInput"
|
||||
returnKeyType="next"
|
||||
keyboardType="phone-pad"
|
||||
onChangeText={(phoneNumber) => {
|
||||
this.setState({phoneNumber}, () => { this.validateForm('phoneNumber'); });
|
||||
}}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.passwordInput._root.focus()
|
||||
}}
|
||||
value={this.state.phoneNumber}
|
||||
/>
|
||||
{this.fieldHasError('phoneNumber') && <Icon name='close-circle' />}
|
||||
</Item>;
|
||||
}
|
||||
|
||||
passwordInput() {
|
||||
return <Item error={this.fieldHasError('password')}>
|
||||
<Label>Passord</Label>
|
||||
<Input
|
||||
ref="passwordInput"
|
||||
secureTextEntry={true}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
onChangeText={(password) => {
|
||||
this.setState({password}, () => { this.validateForm('password'); });
|
||||
}}
|
||||
onSubmitEditing={this.onRegisterPress}
|
||||
value={this.state.password}
|
||||
/>
|
||||
{this.fieldHasError('password') && <Icon name='close-circle' />}
|
||||
</Item>
|
||||
}
|
||||
|
||||
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 <View style={styles.errorBox}><Text style={styles.errorMessage}>{errorFormatted}</Text></View>
|
||||
|
||||
} 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 <View style={styles.errorBox}><Text style={styles.errorMessage}>{errorFormatted}</Text></View>
|
||||
}
|
||||
|
||||
return <View style={styles.errorBox}/>
|
||||
};
|
||||
|
||||
showFieldError = (field) => {
|
||||
if(!this.fieldHasError(field) ) {
|
||||
return;
|
||||
}
|
||||
return <Item style={styles.errorText}><Text style={styles.errorMessage}>{this.state.errors[field]}</Text></Item>;
|
||||
};
|
||||
|
||||
showSpinner = () => {
|
||||
if (this.props.isRegisteringUser) {
|
||||
return <Spinner color="#f58220"/>
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container style={styles.container}>
|
||||
<Content keyboardShouldPersistTaps='always'>
|
||||
<Form style={styles.card}>
|
||||
{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()}
|
||||
</Form>
|
||||
</Content>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
onRegisterPress = () => {
|
||||
if( !this.canSubmitForm() ) {
|
||||
Toast.show({
|
||||
text: 'Noen av feltene er ikke fylt ut riktig',
|
||||
position: 'bottom',
|
||||
buttonText: 'OK',
|
||||
duration: 1500,
|
||||
firstNameInput() {
|
||||
return (
|
||||
<Item error={this.fieldHasError('firstName')}>
|
||||
<Label>Fornavn</Label>
|
||||
<Input
|
||||
autoFocus={true}
|
||||
returnKeyType="next"
|
||||
onChangeText={(firstName) => {
|
||||
this.setState({ firstName }, () => {
|
||||
this.validateForm('firstName');
|
||||
});
|
||||
return;
|
||||
}
|
||||
}}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.lastNameInput._root.focus();
|
||||
}}
|
||||
value={this.state.firstName}
|
||||
/>
|
||||
{this.fieldHasError('firstName') && <Icon name="close-circle" />}
|
||||
</Item>
|
||||
);
|
||||
}
|
||||
|
||||
this.props.onRegisterPress(
|
||||
this.state.firstName,
|
||||
this.state.lastName,
|
||||
this.state.email,
|
||||
this.state.phoneNumber,
|
||||
this.state.password,
|
||||
);
|
||||
};
|
||||
lastNameInput() {
|
||||
return (
|
||||
<Item error={this.fieldHasError('lastName')}>
|
||||
<Label>Etternavn</Label>
|
||||
<Input
|
||||
ref="lastNameInput"
|
||||
returnKeyType="next"
|
||||
onChangeText={(lastName) => {
|
||||
this.setState({ lastName }, () => {
|
||||
this.validateForm('lastName');
|
||||
});
|
||||
}}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.emailInput._root.focus();
|
||||
}}
|
||||
value={this.state.lastName}
|
||||
/>
|
||||
{this.fieldHasError('lastName') && <Icon name="close-circle" />}
|
||||
</Item>
|
||||
);
|
||||
}
|
||||
|
||||
registerbutton() {
|
||||
const isDisabled = this.state.touched.size === 0 || this.props.isRegisteringUser || !this.canSubmitForm();
|
||||
emailInput() {
|
||||
return (
|
||||
<Item error={this.fieldHasError('email')}>
|
||||
<Label>E-post</Label>
|
||||
<Input
|
||||
ref="emailInput"
|
||||
keyboardType="email-address"
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
returnKeyType="next"
|
||||
onChangeText={(email) => {
|
||||
this.setState({ email }, () => {
|
||||
this.validateForm('email');
|
||||
});
|
||||
}}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.phoneInput._root.focus();
|
||||
}}
|
||||
value={this.state.email}
|
||||
/>
|
||||
{this.fieldHasError('email') && <Icon name="close-circle" />}
|
||||
</Item>
|
||||
);
|
||||
}
|
||||
|
||||
return (<Button
|
||||
full
|
||||
disabled={isDisabled}
|
||||
onPress={this.onRegisterPress}
|
||||
style={styles.registerButton}
|
||||
>
|
||||
<Text>Registrer meg</Text>
|
||||
</Button>);
|
||||
phoneNumberInput() {
|
||||
return (
|
||||
<Item error={this.fieldHasError('phoneNumber')}>
|
||||
<Label>Mobilnummer</Label>
|
||||
<Input
|
||||
ref="phoneInput"
|
||||
returnKeyType="next"
|
||||
keyboardType="phone-pad"
|
||||
onChangeText={(phoneNumber) => {
|
||||
this.setState({ phoneNumber }, () => {
|
||||
this.validateForm('phoneNumber');
|
||||
});
|
||||
}}
|
||||
onSubmitEditing={() => {
|
||||
this.refs.passwordInput._root.focus();
|
||||
}}
|
||||
value={this.state.phoneNumber}
|
||||
/>
|
||||
{this.fieldHasError('phoneNumber') && <Icon name="close-circle" />}
|
||||
</Item>
|
||||
);
|
||||
}
|
||||
|
||||
passwordInput() {
|
||||
return (
|
||||
<Item error={this.fieldHasError('password')}>
|
||||
<Label>Passord</Label>
|
||||
<Input
|
||||
ref="passwordInput"
|
||||
secureTextEntry={true}
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
onChangeText={(password) => {
|
||||
this.setState({ password }, () => {
|
||||
this.validateForm('password');
|
||||
});
|
||||
}}
|
||||
onSubmitEditing={this.onRegisterPress}
|
||||
value={this.state.password}
|
||||
/>
|
||||
{this.fieldHasError('password') && <Icon name="close-circle" />}
|
||||
</Item>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<View style={styles.errorBox}>
|
||||
<Text style={styles.errorMessage}>{errorFormatted}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
if (Object.keys(errors).length !== 0 && 'detail' in errors) {
|
||||
const err = errors.detail;
|
||||
const errorFormatted = err || 'Kunne ikke registrere bruker, prøv igjen...';
|
||||
return (
|
||||
<View style={styles.errorBox}>
|
||||
<Text style={styles.errorMessage}>{errorFormatted}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return <View style={styles.errorBox} />;
|
||||
};
|
||||
|
||||
showFieldError = (field) => {
|
||||
if (!this.fieldHasError(field)) {
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<Item style={styles.errorText}>
|
||||
<Text style={styles.errorMessage}>{this.state.errors[field]}</Text>
|
||||
</Item>
|
||||
);
|
||||
};
|
||||
|
||||
showSpinner = () => {
|
||||
if (this.props.isRegisteringUser) {
|
||||
return <Spinner color="#f58220" />;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container style={styles.container}>
|
||||
<Content keyboardShouldPersistTaps="always">
|
||||
<Form style={styles.card}>
|
||||
{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()}
|
||||
</Form>
|
||||
</Content>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<Button full disabled={isDisabled} onPress={this.onRegisterPress} style={styles.registerButton}>
|
||||
<Text>Registrer meg</Text>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
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,
|
||||
},
|
||||
});
|
||||
@@ -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 <UserRegister
|
||||
{...this.state}
|
||||
onRegisterPress={this.onRegisterPress}
|
||||
registerError={this.props.registerError}
|
||||
isRegisteringUser={this.props.isRegisteringUser}
|
||||
/>;
|
||||
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 (
|
||||
<UserRegister
|
||||
{...this.state}
|
||||
onRegisterPress={this.onRegisterPress}
|
||||
registerError={this.props.registerError}
|
||||
isRegisteringUser={this.props.isRegisteringUser}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
+55
-50
@@ -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);
|
||||
+107
-107
@@ -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;
|
||||
}
|
||||
}
|
||||
+5
-14
@@ -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);
|
||||
+13
-14
@@ -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 }) => (
|
||||
<Icon name="info" size={16} color={tintColor} style={{marginTop: 5}} />
|
||||
),
|
||||
};
|
||||
static navigationOptions = {
|
||||
title: 'Om oss',
|
||||
tabBarIcon: ({ tintColor }) => <Icon name="info" size={16} color={tintColor} style={{ marginTop: 5 }} />,
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<StyleProvider style={getTheme()}>
|
||||
<AboutContainer />
|
||||
</StyleProvider>);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<StyleProvider style={getTheme()}>
|
||||
<AboutContainer />
|
||||
</StyleProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 (
|
||||
<StyleProvider style={getTheme()}>
|
||||
<EventDetail item={params.item}/>
|
||||
</StyleProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const { params } = this.props.navigation.state;
|
||||
return (
|
||||
<StyleProvider style={getTheme()}>
|
||||
<EventDetail item={params.item} />
|
||||
</StyleProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 }) => (
|
||||
<Icon name="event" size={16} color={tintColor} style={{marginTop: 5}}/>
|
||||
),
|
||||
};
|
||||
static navigationOptions = {
|
||||
title: 'Program',
|
||||
tabBarIcon: ({ tintColor }) => <Icon name="event" size={16} color={tintColor} style={{ marginTop: 5 }} />,
|
||||
};
|
||||
|
||||
showEvent = (item) => {
|
||||
const { navigate } = this.props.navigation;
|
||||
navigate('EventDetail', {item: item})
|
||||
};
|
||||
showEvent = (item) => {
|
||||
const { navigate } = this.props.navigation;
|
||||
navigate('EventDetail', { item });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<StyleProvider style={getTheme()}>
|
||||
<EventListContainer showEvent={this.showEvent}/>
|
||||
</StyleProvider>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<StyleProvider style={getTheme()}>
|
||||
<EventListContainer showEvent={this.showEvent} />
|
||||
</StyleProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
+24
-24
@@ -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 (
|
||||
<StyleProvider style={getTheme()}>
|
||||
<LoginContainer onLogin={this.onLogin} />
|
||||
</StyleProvider>
|
||||
)
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<StyleProvider style={getTheme()}>
|
||||
<LoginContainer onLogin={this.onLogin} />
|
||||
</StyleProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 }) => (
|
||||
<Icon name="user" size={16} color={tintColor} style={{marginTop: 5}} />
|
||||
),
|
||||
};
|
||||
static navigationOptions = {
|
||||
title: 'Medlemskap',
|
||||
tabBarIcon: ({ tintColor }) => <Icon name="user" size={16} color={tintColor} style={{ marginTop: 5 }} />,
|
||||
};
|
||||
|
||||
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 (
|
||||
<StyleProvider style={getTheme()}>
|
||||
<MembershipContainer
|
||||
onLoginPress={this.loginNavigate}
|
||||
onLogoutPress={this.logoutNavigate}
|
||||
onRegisterPress={this.registerNavigate} />
|
||||
</StyleProvider>
|
||||
)
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<StyleProvider style={getTheme()}>
|
||||
<MembershipContainer
|
||||
onLoginPress={this.loginNavigate}
|
||||
onLogoutPress={this.logoutNavigate}
|
||||
onRegisterPress={this.registerNavigate}
|
||||
/>
|
||||
</StyleProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 (
|
||||
<StyleProvider style={getTheme()}>
|
||||
<UserRegisterContainer onRegister={this.onRegister} />
|
||||
</StyleProvider>
|
||||
)
|
||||
}
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<StyleProvider style={getTheme()}>
|
||||
<UserRegisterContainer onRegister={this.onRegister} />
|
||||
</StyleProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
+18
-18
@@ -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};
|
||||
export default { card, colors };
|
||||
+10
-10
@@ -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)),
|
||||
]);
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
Reference in new issue
Block a user