Merge branch 'main' into 'leander/form-error-handling'
# Conflicts: # src/modules/login/Login.tsx
This commit is contained in:
commit
3561fe1ece
22 files changed
+607
-1967
No files matched your search
+1
-2
@@ -41,9 +41,8 @@ module.exports = {
|
||||
alphabetize: { order: 'asc' },
|
||||
},
|
||||
],
|
||||
'unused-imports/no-unused-imports': 'error',
|
||||
},
|
||||
plugins: ['prettier', 'unused-imports'],
|
||||
plugins: ['prettier'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.json'],
|
||||
|
||||
@@ -21,11 +21,8 @@ This is a fork of [dusken-client](https://git.neuf.no/edb/dusken-client) with so
|
||||
history, code and binary blobs related to React Native functionality filtered out before
|
||||
[commit 7f6b2c21](https://git.neuf.no/edb/cnapp/-/commit/7f6b2c2129068d2fe8623172582bffd31eb4a131).
|
||||
|
||||
|
||||
## TODO
|
||||
|
||||
- switch to [https://docs.expo.dev/versions/latest/sdk/stripe/](stripe-react-native)
|
||||
- add support for new stripe api version / payment flows in galtinn (gotta support old flow as well for outdated apps)
|
||||
- add functionality needed to pass new store requirements
|
||||
- privacy policy
|
||||
- delete my account
|
||||
@@ -33,4 +30,4 @@ history, code and binary blobs related to React Native functionality filtered ou
|
||||
- add some basic unit tests
|
||||
- test in android emulator? (`npm run android`)
|
||||
- test in ios emulator? (`npm run ios`, requires macOS)
|
||||
- use [Expo Application Services](https://expo.dev/eas) to build and submit app to stores
|
||||
- use [Expo Application Services](https://expo.dev/eas) to build and submit app to stores
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"expo": {
|
||||
"name": "Chateau Neuf",
|
||||
"slug": "neuf.chateau",
|
||||
"slug": "cnapp",
|
||||
"version": "1.0.0",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/icon.png",
|
||||
@@ -30,6 +30,12 @@
|
||||
},
|
||||
"plugins": [
|
||||
"expo-asset"
|
||||
]
|
||||
],
|
||||
"extra": {
|
||||
"eas": {
|
||||
"projectId": "908b7664-3796-4d49-b075-2847b0804c02"
|
||||
}
|
||||
},
|
||||
"owner": "neuf"
|
||||
}
|
||||
}
|
||||
Generated
+473
-1802
File diff suppressed because it is too large.
Load diff
+22
-24
@@ -1,35 +1,35 @@
|
||||
{
|
||||
"name": "cnapp",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"main": "node_modules/expo/AppEntry.js",
|
||||
"scripts": {
|
||||
"start": "expo start --port 19001",
|
||||
"android": "expo start --android",
|
||||
"ios": "expo start --ios",
|
||||
"web": "expo start --web",
|
||||
"lint": "eslint .",
|
||||
"check-types": "tsc",
|
||||
"format": "eslint --fix .",
|
||||
"check-types": "tsc"
|
||||
"ios": "expo start --ios",
|
||||
"lint": "eslint .",
|
||||
"start": "expo start --port 19001",
|
||||
"web": "expo start --web"
|
||||
},
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.7.17",
|
||||
"@apollo/client": "^3.10.8",
|
||||
"@babel/plugin-transform-private-methods": "^7.24.7",
|
||||
"@expo/vector-icons": "^14.0.2",
|
||||
"@react-native-async-storage/async-storage": "^1.23.1",
|
||||
"@react-navigation/bottom-tabs": "^6.5.8",
|
||||
"@react-navigation/native": "^6.1.7",
|
||||
"@react-navigation/native-stack": "^6.9.13",
|
||||
"@sentry/react-native": "^5.22.0",
|
||||
"@stripe/stripe-react-native": "^0.37.2",
|
||||
"@react-native-async-storage/async-storage": "1.23.1",
|
||||
"@react-navigation/bottom-tabs": "^6.6.1",
|
||||
"@react-navigation/native": "^6.1.18",
|
||||
"@react-navigation/native-stack": "^6.10.1",
|
||||
"@sentry/react-native": "~5.22.0",
|
||||
"@stripe/stripe-react-native": "0.37.2",
|
||||
"email-validator": "^2.0.4",
|
||||
"expo": "~51.0.17",
|
||||
"expo": "~51.0.21",
|
||||
"expo-asset": "~10.0.10",
|
||||
"expo-constants": "~16.0.2",
|
||||
"expo-status-bar": "^1.12.1",
|
||||
"graphql": "^15.8.0",
|
||||
"graphql": "^16.9.0",
|
||||
"metro-react-native-babel-preset": "^0.77.0",
|
||||
"moment": "^2.29.4",
|
||||
"native-base": "^3.4.28",
|
||||
"moment": "^2.30.1",
|
||||
"react": "18.2.0",
|
||||
"react-hook-form": "^7.52.1",
|
||||
"react-native": "0.74.3",
|
||||
@@ -37,10 +37,10 @@
|
||||
"react-native-gesture-handler": "~2.16.1",
|
||||
"react-native-reanimated": "^3.10.1",
|
||||
"react-native-render-html": "^6.3.4",
|
||||
"react-native-safe-area-context": "^4.10.1",
|
||||
"react-native-safe-area-context": "4.10.5",
|
||||
"react-native-screens": "^3.31.1",
|
||||
"react-native-svg": "^15.2.0",
|
||||
"react-native-ui-lib": "^7.8.0",
|
||||
"react-native-ui-lib": "^7.26.2",
|
||||
"react-redux": "^8.1.2",
|
||||
"redux": "^4.2.1",
|
||||
"redux-persist": "^6.0.0",
|
||||
@@ -48,8 +48,7 @@
|
||||
"styled-system": "^5.1.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.24.7",
|
||||
"@babel/eslint-parser": "^7.24.7",
|
||||
"@babel/core": "^7.24.9",
|
||||
"@react-native/eslint-config": "^0.74.85",
|
||||
"@testing-library/react-native": "^12.3.0",
|
||||
"@types/jest": "^29.5.12",
|
||||
@@ -65,9 +64,8 @@
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-react": "^7.34.3",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"eslint-plugin-unused-imports": "^3.2.0",
|
||||
"prettier": "^3.3.2",
|
||||
"expo-dev-client": "^4.0.20",
|
||||
"prettier": "^3.3.3",
|
||||
"typescript": "~5.3.3"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
}
|
||||
+2
-5
@@ -2,12 +2,11 @@ import { ApolloClient, HttpLink, InMemoryCache, ApolloProvider } from '@apollo/c
|
||||
import { StripeProvider } from '@stripe/stripe-react-native';
|
||||
import Constants from 'expo-constants';
|
||||
import React from 'react';
|
||||
import { LogBox } from 'react-native';
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||
import { Provider } from 'react-redux';
|
||||
import { PersistGate } from 'redux-persist/integration/react';
|
||||
|
||||
import Dusken from './Dusken';
|
||||
import AppInner from './AppInner';
|
||||
import { useDeepLinking } from './hooks';
|
||||
import { store, persistor } from './reduxStore';
|
||||
import { loadTheme } from './theme';
|
||||
@@ -16,8 +15,6 @@ const apolloClient = new ApolloClient({
|
||||
link: new HttpLink({ uri: `${process.env.EXPO_PUBLIC_DUSKEN_API_URL}/api/graphql/` }),
|
||||
cache: new InMemoryCache(),
|
||||
});
|
||||
// FIXME: Replace unmaintained native-base with https://wix.github.io/react-native-ui-lib/ which produces this warning
|
||||
LogBox.ignoreLogs(['In React 18, SSRProvider is not necessary and is a noop. You can remove it from your app.']);
|
||||
|
||||
loadTheme();
|
||||
|
||||
@@ -34,7 +31,7 @@ export default function App() {
|
||||
urlScheme={Constants.expoConfig?.scheme as string}
|
||||
merchantIdentifier="merchant.no.neuf.chateau" // https://developer.apple.com/account/resources/identifiers/list/merchant
|
||||
>
|
||||
<Dusken />
|
||||
<AppInner />
|
||||
</StripeProvider>
|
||||
</ApolloProvider>
|
||||
</PersistGate>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
|
||||
import Navigation from './Navigation';
|
||||
import { useLogoutUsersWithNoToken } from './hooks';
|
||||
|
||||
const AppInner = () => {
|
||||
useLogoutUsersWithNoToken();
|
||||
|
||||
return <Navigation />;
|
||||
};
|
||||
|
||||
export default AppInner;
|
||||
@@ -1,27 +0,0 @@
|
||||
import { NativeBaseProvider } from 'native-base';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import Navigation from './Navigation';
|
||||
import { logout } from './actions';
|
||||
import { useAppDispatch, useAppSelector } from './hooks';
|
||||
|
||||
const Dusken = () => {
|
||||
const user = useAppSelector((store) => store.user);
|
||||
const userToken = useAppSelector((store) => store.userToken);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
// logout authenticated users without token
|
||||
if (user && !userToken) {
|
||||
dispatch(logout());
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<NativeBaseProvider>
|
||||
<Navigation />
|
||||
</NativeBaseProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dusken;
|
||||
+8
-8
@@ -4,14 +4,14 @@ import { NavigationContainer } from '@react-navigation/native';
|
||||
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
||||
import React from 'react';
|
||||
|
||||
import About from './modules/about/About';
|
||||
import EventDetail from './modules/events/EventDetail';
|
||||
import EventList from './modules/events/EventList';
|
||||
import Login from './modules/login/Login';
|
||||
import Membership from './modules/membership/Membership';
|
||||
import UserRegister from './modules/register/UserRegister';
|
||||
import theme from './theme';
|
||||
import type { EventStackParamList, HomeTabParamList, UserStackParamList } from './types/navigation';
|
||||
import About from 'src/modules/about/About';
|
||||
import EventDetail from 'src/modules/events/EventDetail';
|
||||
import EventList from 'src/modules/events/EventList';
|
||||
import Login from 'src/modules/login/Login';
|
||||
import Membership from 'src/modules/membership/Membership';
|
||||
import UserRegister from 'src/modules/register/UserRegister';
|
||||
import theme from 'src/theme';
|
||||
import type { EventStackParamList, HomeTabParamList, UserStackParamList } from 'src/types/navigation';
|
||||
|
||||
// This looks ugly, even though it's a recommended style from the branding profile.
|
||||
// TODO: Change this
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Input, Text, WarningOutlineIcon, FormControl, IInputProps } from 'native-base';
|
||||
import React from 'react';
|
||||
import { Controller, UseControllerProps } from 'react-hook-form';
|
||||
import { ReturnKeyTypeOptions } from 'react-native';
|
||||
import { ReturnKeyTypeOptions, Text, StyleSheet } from 'react-native';
|
||||
import { TextField, TextFieldProps } from 'react-native-ui-lib';
|
||||
|
||||
import theme from 'src/theme';
|
||||
|
||||
type FormInputProps = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -12,7 +14,7 @@ type FormInputProps = {
|
||||
label: string;
|
||||
rules?: UseControllerProps['rules'];
|
||||
returnKeyType?: ReturnKeyTypeOptions;
|
||||
} & IInputProps;
|
||||
} & TextFieldProps;
|
||||
|
||||
const FormInput = ({
|
||||
errors,
|
||||
@@ -31,26 +33,41 @@ const FormInput = ({
|
||||
control={control}
|
||||
rules={rules}
|
||||
render={({ field: { onChange, onBlur, value, ref } }) => (
|
||||
<FormControl isInvalid={Boolean(fieldError)}>
|
||||
<FormControl.Label>{label}</FormControl.Label>
|
||||
<Input
|
||||
<>
|
||||
<TextField
|
||||
ref={ref}
|
||||
label={label}
|
||||
returnKeyType={returnKeyType}
|
||||
onChangeText={onChange}
|
||||
onBlur={onBlur}
|
||||
fieldStyle={[styles.field, fieldError ? styles.fieldWithError : undefined]}
|
||||
value={value}
|
||||
{...rest}
|
||||
/>
|
||||
{fieldError && (
|
||||
<FormControl.ErrorMessage leftIcon={<WarningOutlineIcon size="xs" />}>
|
||||
<Text>{fieldError.message?.toString()}</Text>
|
||||
</FormControl.ErrorMessage>
|
||||
)}
|
||||
</FormControl>
|
||||
{fieldError && <Text style={styles.errorMessage}>{fieldError.message?.toString()}</Text>}
|
||||
</>
|
||||
)}
|
||||
name={name}
|
||||
defaultValue=""
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
field: {
|
||||
borderColor: theme.colors.gray1,
|
||||
borderWidth: 1,
|
||||
borderRadius: 4,
|
||||
padding: 4,
|
||||
marginVertical: 4,
|
||||
},
|
||||
fieldWithError: {
|
||||
borderColor: theme.colors.danger,
|
||||
},
|
||||
errorMessage: {
|
||||
color: theme.colors.danger,
|
||||
marginBottom: 4,
|
||||
},
|
||||
});
|
||||
|
||||
export default FormInput;
|
||||
@@ -6,7 +6,7 @@ import theme from 'src/theme';
|
||||
const PrivacyButton = () => (
|
||||
<TouchableOpacity
|
||||
style={styles.button}
|
||||
onPress={() => Linking.openURL(process.env.EXPO_PUBLIC_DUSKEN_PRIVACY_POLICY_URL)}
|
||||
onPress={() => Linking.openURL(process.env.EXPO_PUBLIC_DUSKEN_PRIVACY_POLICY_URL || '')}
|
||||
>
|
||||
<Text style={styles.linkText}>Personvern</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Linking } from 'react-native';
|
||||
import type { TypedUseSelectorHook } from 'react-redux';
|
||||
import { useDispatch, useSelector, useStore } from 'react-redux';
|
||||
|
||||
import { logout } from './actions';
|
||||
import type { AppDispatch, AppStore, RootState } from './reduxStore';
|
||||
|
||||
export function usePrevious<T>(value: T): T | undefined {
|
||||
@@ -55,3 +56,16 @@ export const useDeepLinking = () => {
|
||||
export const useAppDispatch: () => AppDispatch = useDispatch;
|
||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
||||
export const useAppStore: () => AppStore = useStore;
|
||||
|
||||
/** Logout authenticated users without token */
|
||||
export function useLogoutUsersWithNoToken() {
|
||||
const user = useAppSelector((store) => store.user);
|
||||
const userToken = useAppSelector((store) => store.userToken);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
if (user && !userToken) {
|
||||
dispatch(logout());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -72,7 +72,7 @@ const openMap = () => {
|
||||
/** FIXME: Values here are pretty hardcoded. Should fetch config from a JSON-file or API */
|
||||
const About = () => (
|
||||
<ScrollView>
|
||||
<View style={styles.card}>
|
||||
<View style={theme.card}>
|
||||
<Text style={[styles.header, { marginTop: 8 }]}>Det Norske Studentersamfund</Text>
|
||||
<TouchableOpacity onPress={() => Linking.openURL('https://www.studentersamfundet.no/')}>
|
||||
<Image style={styles.logo} source={logo} />
|
||||
@@ -167,7 +167,6 @@ const styles = StyleSheet.create({
|
||||
paddingBottom: 10,
|
||||
lineHeight: 25,
|
||||
},
|
||||
card: theme.card,
|
||||
featuredImage: {
|
||||
height: 230,
|
||||
width: null,
|
||||
|
||||
@@ -166,11 +166,11 @@ const EventDetail = ({
|
||||
return (
|
||||
<ScrollView>
|
||||
{event.thumbnail.medium_large && <Image source={{ uri: event.thumbnail.medium_large }} style={styles.image} />}
|
||||
<View style={styles.card}>
|
||||
<View style={theme.card}>
|
||||
{/* Date and title */}
|
||||
<View style={styles.dateAndTitle}>
|
||||
<View style={styles.dateContainer}>
|
||||
<View style={[styles.card, styles.dateCard]}>
|
||||
<View style={[theme.card, styles.dateCard]}>
|
||||
<Text style={styles.day}>{format(event.start_time, FORMAT_DAY)}</Text>
|
||||
<Text style={styles.month}>{format(event.start_time, FORMAT_MONTH)}</Text>
|
||||
<Year startTime={event.start_time} />
|
||||
@@ -229,7 +229,6 @@ const styles = StyleSheet.create({
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.neufBlack,
|
||||
},
|
||||
card: theme.card,
|
||||
image: {
|
||||
height: 260,
|
||||
width: null,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SimpleLineIcons as Icon } from '@expo/vector-icons';
|
||||
import moment from 'moment';
|
||||
import { Card, Text, Spinner, ArrowForwardIcon, HStack } from 'native-base';
|
||||
import React from 'react';
|
||||
import { StyleSheet, View, Platform, SectionList, TouchableOpacity } from 'react-native';
|
||||
import { StyleSheet, View, Platform, SectionList, TouchableOpacity, ActivityIndicator, Text } from 'react-native';
|
||||
import { Button } from 'react-native-ui-lib';
|
||||
import 'moment/locale/nb';
|
||||
|
||||
@@ -33,7 +33,7 @@ const EventList = () => {
|
||||
}}
|
||||
style={styles.listItem}
|
||||
>
|
||||
<HStack>
|
||||
<View style={styles.row}>
|
||||
<View style={{ flex: 5 }}>
|
||||
<Text style={styles.listItemTitle} numberOfLines={1}>
|
||||
{item.title.decoded}
|
||||
@@ -41,9 +41,9 @@ const EventList = () => {
|
||||
<Text style={styles.listItemTime}>{formatTime(item.start_time)}</Text>
|
||||
</View>
|
||||
<View style={styles.arrow}>
|
||||
<ArrowForwardIcon size={4} color={theme.colors.neufBlack} marginTop={3} />
|
||||
<Icon name="arrow-right" style={{ color: theme.colors.neufBlack, marginTop: 3 }} />
|
||||
</View>
|
||||
</HStack>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
||||
@@ -52,23 +52,21 @@ const EventList = () => {
|
||||
const Footer = () =>
|
||||
!loading && (
|
||||
<View style={{ paddingVertical: 20 }}>
|
||||
<Spinner color={theme.colors.neufOrange} />
|
||||
<ActivityIndicator color={theme.colors.neufOrange} />
|
||||
</View>
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<View>
|
||||
<Card style={{ margin: 4 }}>
|
||||
<View>
|
||||
<Text style={styles.loadingText}>Kunne ikke hente programmet...</Text>
|
||||
</View>
|
||||
<View>
|
||||
<Button onPress={handleRefresh}>
|
||||
<Text>Prøv igjen</Text>
|
||||
</Button>
|
||||
</View>
|
||||
</Card>
|
||||
<View style={theme.card}>
|
||||
<View>
|
||||
<Text style={styles.loadingText}>Kunne ikke hente programmet...</Text>
|
||||
</View>
|
||||
<View>
|
||||
<Button onPress={handleRefresh}>
|
||||
<Text>Prøv igjen</Text>
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -85,7 +83,7 @@ const EventList = () => {
|
||||
onEndReachedThreshold={10}
|
||||
initialNumToRender={10}
|
||||
ListFooterComponent={Footer}
|
||||
style={[styles.card, styles.list]}
|
||||
style={[theme.card, styles.list]}
|
||||
ListHeaderComponent={Header}
|
||||
/>
|
||||
);
|
||||
@@ -125,7 +123,6 @@ const styles = StyleSheet.create({
|
||||
textAlign: 'center',
|
||||
paddingBottom: 8,
|
||||
},
|
||||
card: theme.card,
|
||||
list: {
|
||||
padding: 0,
|
||||
marginTop: 0,
|
||||
@@ -139,4 +136,5 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
alignItems: 'flex-end',
|
||||
},
|
||||
row: { flex: 1, flexDirection: 'row' },
|
||||
});
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ScrollView } from 'native-base';
|
||||
import React from 'react';
|
||||
import { ActivityIndicator, Linking, StyleSheet, View, Text } from 'react-native';
|
||||
import { ActivityIndicator, Linking, StyleSheet, View, Text, ScrollView } from 'react-native';
|
||||
import { Button } from 'react-native-ui-lib';
|
||||
|
||||
import FormErrors from 'src/components/FormErrors';
|
||||
@@ -14,13 +13,14 @@ const Login = () => {
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
<View style={styles.card}>
|
||||
<View style={theme.card}>
|
||||
<FormErrors errors={errors} />
|
||||
<FormInput
|
||||
name="email"
|
||||
label="E-post / Brukernavn"
|
||||
keyboardType="email-address"
|
||||
control={control}
|
||||
errors={errors}
|
||||
keyboardType="email-address"
|
||||
autoFocus
|
||||
autoCapitalize="none"
|
||||
autoCorrect={false}
|
||||
@@ -67,5 +67,4 @@ const styles = StyleSheet.create({
|
||||
marginTop: 32,
|
||||
marginBottom: 16,
|
||||
},
|
||||
card: theme.card,
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { View, StyleSheet, ActivityIndicator } from 'react-native';
|
||||
import { View, ActivityIndicator } from 'react-native';
|
||||
|
||||
import PrivacyButton from 'src/components/PrivacyButton';
|
||||
import theme from 'src/theme';
|
||||
@@ -14,7 +14,7 @@ const Membership = () => {
|
||||
|
||||
if (isFetchingUserData) {
|
||||
return (
|
||||
<View style={styles.card}>
|
||||
<View style={theme.card}>
|
||||
<ActivityIndicator color={theme.colors.neufOrange} />
|
||||
</View>
|
||||
);
|
||||
@@ -37,40 +37,3 @@ const Membership = () => {
|
||||
);
|
||||
};
|
||||
export default Membership;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: theme.card,
|
||||
paragraph: {
|
||||
textAlign: 'center',
|
||||
fontSize: 16,
|
||||
color: theme.colors.neufBlack,
|
||||
paddingBottom: 10,
|
||||
lineHeight: 25,
|
||||
},
|
||||
button: {
|
||||
marginTop: 6,
|
||||
marginBottom: 16,
|
||||
},
|
||||
em: {
|
||||
fontSize: 16,
|
||||
color: theme.colors.neufBlack,
|
||||
fontStyle: 'italic',
|
||||
textAlign: 'center',
|
||||
marginVertical: 10,
|
||||
},
|
||||
headerTitle: {
|
||||
textAlign: 'center',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 20,
|
||||
color: theme.colors.neufBlack,
|
||||
paddingVertical: 10,
|
||||
lineHeight: 25,
|
||||
},
|
||||
header: {
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
color: theme.colors.neufBlack,
|
||||
lineHeight: 25,
|
||||
paddingLeft: 2,
|
||||
},
|
||||
});
|
||||
@@ -10,7 +10,7 @@ interface MembershipInfoProps {
|
||||
}
|
||||
const MembershipInfo = ({ onRegister, onLogin }: MembershipInfoProps) => (
|
||||
<ScrollView>
|
||||
<View style={styles.card}>
|
||||
<View style={theme.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
|
||||
@@ -38,7 +38,6 @@ const MembershipInfo = ({ onRegister, onLogin }: MembershipInfoProps) => (
|
||||
export default MembershipInfo;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: theme.card,
|
||||
paragraph: {
|
||||
textAlign: 'center',
|
||||
fontSize: 16,
|
||||
|
||||
@@ -148,9 +148,11 @@ interface ProofProps {
|
||||
const Proof = ({ onLogoutPress, fetchUser, isFetchingUserData }: ProofProps) => {
|
||||
const CONFETTI_TIMEOUT = 15000;
|
||||
const confettiRef = useRef<Confetti>(null);
|
||||
const { paymentLoading, isLoadingMembershipType, onPurchasePress, membershipPrice, toast, setToast, user } = useProof({
|
||||
fetchUser,
|
||||
});
|
||||
const { paymentLoading, isLoadingMembershipType, onPurchasePress, membershipPrice, toast, setToast, user } = useProof(
|
||||
{
|
||||
fetchUser,
|
||||
},
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const currentRef = confettiRef.current;
|
||||
@@ -183,7 +185,7 @@ const Proof = ({ onLogoutPress, fetchUser, isFetchingUserData }: ProofProps) =>
|
||||
<View>
|
||||
<UserConfetti user={user} confettiRef={confettiRef} />
|
||||
<ScrollView refreshControl={<RefreshControl refreshing={isFetchingUserData} onRefresh={fetchUser} />}>
|
||||
<View style={[styles.card, { paddingVertical: 16 }]}>
|
||||
<View style={[theme.card, { paddingVertical: 16 }]}>
|
||||
<MembershipName user={user} />
|
||||
<Logo user={user} />
|
||||
<MembershipStatus user={user} onMembershipPress={onMembershipPress} />
|
||||
@@ -218,7 +220,6 @@ const Proof = ({ onLogoutPress, fetchUser, isFetchingUserData }: ProofProps) =>
|
||||
export default Proof;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: theme.card,
|
||||
membershipStatus: {
|
||||
marginVertical: 16,
|
||||
},
|
||||
|
||||
@@ -26,12 +26,10 @@ const UserRegister = () => {
|
||||
setToast,
|
||||
} = useUserRegister();
|
||||
const isDisabled = isRegisteringUser || !isValid;
|
||||
// TODO: validate on type
|
||||
// TODO: highlight errorfields
|
||||
|
||||
return (
|
||||
<>
|
||||
<View style={styles.card}>
|
||||
<View style={theme.card}>
|
||||
<ScrollView>
|
||||
<FormInput
|
||||
name="first_name"
|
||||
@@ -122,5 +120,4 @@ const styles = StyleSheet.create({
|
||||
errorBox: {
|
||||
marginVertical: 10,
|
||||
},
|
||||
card: theme.card,
|
||||
});
|
||||
+1
-1
@@ -47,7 +47,7 @@ function formatErrors(errs: Record<string, string | string[]>) {
|
||||
|
||||
// FIXME: refactor using redux slices
|
||||
// eslint-disable-next-line @typescript-eslint/default-param-last, @typescript-eslint/no-explicit-any
|
||||
export default function duskenApp(state = initialState, action: any) {
|
||||
export default function appReducers(state = initialState, action: any) {
|
||||
switch (action.type) {
|
||||
/* Login */
|
||||
case LOGIN_REQUEST:
|
||||
|
||||
@@ -13,6 +13,7 @@ const colors = {
|
||||
neufBlue: '#0A0F6D',
|
||||
neufWhite: '#FFFFFF',
|
||||
neufBlack: '#161516',
|
||||
gray1: '#dddddd',
|
||||
};
|
||||
|
||||
const card = {
|
||||
|
||||
Reference in new issue
Block a user