@coorpacademy/app-review 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@coorpacademy/app-review",
3
+ "version": "0.0.3",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "engines": {
7
+ "node": ">=12.22.12"
8
+ },
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "author": "CoorpAcademy",
13
+ "files": [
14
+ "src",
15
+ "readme.md"
16
+ ],
17
+ "dependencies": {
18
+ "react-native": "^0.68.2",
19
+ "react-redux": "^8.0.1",
20
+ "redux": "^4.2.0"
21
+ },
22
+ "devDependencies": {
23
+ "@storybook/react-native": "^5.3.25",
24
+ "redux-devtools-extension": "^2.13.9"
25
+ },
26
+ "gitHead": "c28143fe08c0e290c3584806812e8158ef6f65f7"
27
+ }
package/readme.md ADDED
@@ -0,0 +1 @@
1
+ # App Review
@@ -0,0 +1 @@
1
+ export * from './navigation';
@@ -0,0 +1,22 @@
1
+ import {ActionCreator} from 'redux';
2
+
3
+ export const NAVIGATE_TO = '@@navigation/NAVIGATE_TO';
4
+ export const NAVIGATE_BACK = '@@navigation/NAVIGATE_BACK';
5
+
6
+ export type $NAVIGATE_TO = {
7
+ type: '@@navigation/NAVIGATE_TO';
8
+ payload: string;
9
+ };
10
+
11
+ export type $NAVIGATE_BACK = {
12
+ type: '@@navigation/NAVIGATE_BACK';
13
+ };
14
+
15
+ export const navigateTo: ActionCreator<$NAVIGATE_TO> = (newPath: string) => ({
16
+ type: NAVIGATE_TO,
17
+ payload: newPath
18
+ });
19
+
20
+ export const navigateBack: ActionCreator<$NAVIGATE_BACK> = () => ({
21
+ type: NAVIGATE_BACK
22
+ });
@@ -0,0 +1,16 @@
1
+ import * as React from 'react';
2
+ import {storiesOf} from '@storybook/react-native';
3
+ import RevisionHome from './revision-home';
4
+
5
+ const emptySkills: [] = [];
6
+ const skillsToRevise: {title: string; info: string}[] = [
7
+ {title: 'Negotiation and powers of persuasion', info: '75 questions to revise'},
8
+ {title: 'Negotiation and powers of persuasion', info: '75 questions to revise'},
9
+ {title: 'Negotiation and powers of persuasion', info: '75 questions to revise'},
10
+ {title: 'Negotiation and powers of persuasion', info: '75 questions to revise'},
11
+ {title: 'Creativity and innovation', info: '75 questions to revise'}
12
+ ];
13
+
14
+ storiesOf('RevisionHome', module)
15
+ .add('No skills', () => <RevisionHome skillsToRevise={emptySkills} />)
16
+ .add('Few skills', () => <RevisionHome skillsToRevise={skillsToRevise} />);
@@ -0,0 +1,125 @@
1
+ import * as React from 'react';
2
+ import {View, FlatList, Pressable, StyleSheet, Text, ImageBackground} from 'react-native';
3
+ import {NovaCompositionNavigationArrowRight as ArrowRight} from '@coorpacademy/nova-icons';
4
+
5
+ // import theme from '../../../modules/theme';
6
+ // import translations from '../../../translations';
7
+ // import {HEADER_HEIGHT} from '../../../app-shared/components/header-v2';
8
+ // import noSkillsImage from '../../../assets/images/revision-no-skills.png';
9
+ // import Touchable from '../../../app-shared/components/touchable';
10
+
11
+ type _Skill_ = {title?: string; info?: string; isExtraSpace?: boolean};
12
+ interface Props {
13
+ skillsToRevise: {title?: string; info?: string}[];
14
+ }
15
+
16
+ const styles = StyleSheet.create({
17
+ container: {
18
+ flex: 1,
19
+ padding: 20
20
+ // paddingTop: HEADER_HEIGHT + 20,
21
+ // backgroundColor: theme.colors.white
22
+ },
23
+ title: {
24
+ // fontSize: theme.fontSize.xlarge,
25
+ // fontWeight: theme.fontWeight.bold,
26
+ lineHeight: 28
27
+ // color: theme.colors.text.primary,
28
+ // marginBottom: theme.spacing.base,
29
+ // marginTop: theme.spacing.small
30
+ },
31
+ subtitle: {
32
+ // fontSize: theme.fontSize.xlarge,
33
+ // color: theme.colors.text.primary,
34
+ // marginBottom: theme.spacing.tiny
35
+ },
36
+ text: {
37
+ fontSize: 16,
38
+ lineHeight: 22,
39
+ // color: theme.colors.text.primary,
40
+ opacity: 0.7
41
+ // marginBottom: theme.spacing.base
42
+ },
43
+ noSkillsImage: {
44
+ flex: 1,
45
+ alignSelf: 'center',
46
+ width: 320,
47
+ height: 300
48
+ },
49
+ skills: {},
50
+ skill: {
51
+ display: 'flex',
52
+ flexDirection: 'row',
53
+ overflow: 'hidden',
54
+ alignItems: 'center',
55
+ height: 118,
56
+ borderRadius: 8
57
+ // backgroundColor: theme.colors.gray.lighter,
58
+ // marginTop: theme.spacing.micro,
59
+ // marginBottom: theme.spacing.micro,
60
+ // padding: theme.spacing.base
61
+ },
62
+ skillTexts: {
63
+ flex: 1,
64
+ flexDirection: 'column'
65
+ },
66
+ skillTitle: {
67
+ fontSize: 18,
68
+ fontWeight: '600',
69
+ lineHeight: 22
70
+ // color: theme.colors.text.primary,
71
+ // marginBottom: theme.spacing.tiny
72
+ },
73
+ skillInfo: {
74
+ fontSize: 12,
75
+ lineHeight: 16
76
+ // color: theme.colors.gray.medium
77
+ },
78
+ extraSpace: {
79
+ height: 100
80
+ }
81
+ });
82
+
83
+ const onSelectSkill = (title: string) => () => {
84
+ console.log('pressed on', {title});
85
+ };
86
+
87
+ const ExtraSpace = () => <View style={styles.extraSpace}></View>;
88
+
89
+ const Skill = ({title, info}: _Skill_) => (
90
+ <Pressable style={styles.skill} onPress={onSelectSkill(title)}>
91
+ <View style={styles.skillTexts}>
92
+ <Text style={styles.skillTitle}>{title}</Text>
93
+ <Text style={styles.skillInfo}>{info}</Text>
94
+ </View>
95
+ <ArrowRight color="#ededed" height={20} width={20} />
96
+ </Pressable>
97
+ );
98
+
99
+ const renderSkill = ({item: {title, info, isExtraSpace = false}}: {item: _Skill_}) =>
100
+ isExtraSpace ? <ExtraSpace /> : <Skill title={title} info={info} />;
101
+
102
+ const Skills = (props: {skills: _Skill_[]}) => (
103
+ <FlatList contentContainerStyle={styles.skills} data={props.skills} renderItem={renderSkill} />
104
+ );
105
+
106
+ const NoSkills = () => (
107
+ <>
108
+ {/* <Text style={styles.subtitle}>{translations.revision.home.noSkills}</Text>
109
+ <Text style={styles.text}>{translations.revision.home.noSkillsDetails}</Text>
110
+ <ImageBackground source={noSkillsImage} style={styles.noSkillsImage} /> */}
111
+ </>
112
+ );
113
+
114
+ const RevisionHome = (props: Props) => (
115
+ <View style={styles.container}>
116
+ {/* <Text style={styles.title}>{translations.revision.home.title}</Text> */}
117
+ {!props.skillsToRevise || props.skillsToRevise.length === 0 ? (
118
+ <NoSkills />
119
+ ) : (
120
+ <Skills skills={[...props.skillsToRevise, {isExtraSpace: true}]} />
121
+ )}
122
+ </View>
123
+ );
124
+
125
+ export default RevisionHome;
@@ -0,0 +1,84 @@
1
+ import * as React from 'react';
2
+ import {View, StyleSheet, Text} from 'react-native';
3
+
4
+ import {NovaLineSelectionCursorsCursorArrowTarget as TargetIcon} from '@coorpacademy/nova-icons';
5
+
6
+ // import theme from '../../../modules/theme';
7
+ // import translations from '../../../translations';
8
+ // import Button from '../../../app-shared/components/button';
9
+ // import {HEADER_HEIGHT} from '../../../app-shared/components/header-v2';
10
+
11
+ const styles = StyleSheet.create({
12
+ container: {
13
+ justifyContent: 'space-between',
14
+ flex: 1,
15
+ padding: 20
16
+ // paddingTop: HEADER_HEIGHT + 20
17
+ },
18
+ title: {
19
+ height: 72,
20
+ width: 268,
21
+ // fontSize: theme.fontSize.xxlarge,
22
+ // fontWeight: theme.fontWeight.bold,
23
+ lineHeight: 36
24
+ // color: theme.colors.text.primary,
25
+ // marginBottom: theme.spacing.small
26
+ },
27
+ text: {
28
+ // fontSize: theme.fontSize.large,
29
+ // color: theme.colors.text.primary,
30
+ opacity: 0.7
31
+ // marginBottom: theme.spacing.small
32
+ },
33
+ tip: {
34
+ flexDirection: 'row',
35
+ overflow: 'hidden',
36
+ alignItems: 'center',
37
+ height: 54,
38
+ borderRadius: 8
39
+ // backgroundColor: theme.colors.gray.extra,
40
+ // marginTop: theme.spacing.micro,
41
+ // marginBottom: theme.spacing.micro
42
+ },
43
+ tipText: {
44
+ // fontSize: theme.fontSize.large,
45
+ // color: theme.colors.text.primary
46
+ },
47
+ icon: {
48
+ margin: 10
49
+ },
50
+ button: {
51
+ height: 52,
52
+ borderRadius: 7
53
+ // marginBottom: theme.spacing.large,
54
+ // marginTop: theme.spacing.large
55
+ }
56
+ });
57
+
58
+ const onButtonPress = () => {};
59
+
60
+ const Tip = ({Icon, text}) => (
61
+ <View style={styles.tip}>
62
+ {/* <Icon height={16} width={16} style={styles.icon} color={theme.colors.text.primary} /> */}
63
+ <Text style={styles.tipText}>{text}</Text>
64
+ </View>
65
+ );
66
+
67
+ const RevisionOnboarding = () => (
68
+ <View style={styles.container}>
69
+ {/* <Text style={styles.title}>{translations.revision.onboarding.title}</Text>
70
+ <Text style={styles.text}>{translations.revision.onboarding.intro1}</Text>
71
+ <Text style={styles.text}>{translations.revision.onboarding.intro2}</Text>
72
+
73
+ <Tip Icon={TargetIcon} text={translations.revision.onboarding.tip1} />
74
+ <Tip Icon={TargetIcon} text={translations.revision.onboarding.tip2} />
75
+ <Tip Icon={TargetIcon} text={translations.revision.onboarding.tip3} />
76
+ <Tip Icon={TargetIcon} text={translations.revision.onboarding.tip4} /> */}
77
+
78
+ {/* <Button style={styles.button} onPress={onButtonPress} testID={`button-quit-revision-onboarding`}>
79
+ {translations.revision.onboarding.validate}
80
+ </Button> */}
81
+ </View>
82
+ );
83
+
84
+ export default RevisionOnboarding;
@@ -0,0 +1,176 @@
1
+ import * as React from 'react';
2
+ import {View, StyleSheet, Text, useWindowDimensions} from 'react-native';
3
+
4
+ // import theme from '../../../modules/theme';
5
+ // import translations from '../../../translations';
6
+ // import {BrandThemeContext} from '../../../app-shared/components/brand-theme-provider';
7
+ // import Button from '../../../app-shared/components/button';
8
+ // import {HEADER_HEIGHT} from '../../../app-shared/components/header-v2';
9
+
10
+ export type _Choice_ = {
11
+ text?: string;
12
+ selected: boolean;
13
+ };
14
+
15
+ export type _Slide_ = {
16
+ category: string;
17
+ question: string;
18
+ instruction: string;
19
+ type: 'multiSelection' | 'singleSelection' | 'trueOrFalse';
20
+ choices?: _Choice_[];
21
+ };
22
+
23
+ interface Props {
24
+ slide: _Slide_;
25
+ num: number;
26
+ }
27
+
28
+ const quizzerStyle = StyleSheet.create({
29
+ container: {
30
+ flex: 1,
31
+ padding: 20,
32
+ // paddingTop: HEADER_HEIGHT + 20, @todo with props
33
+ // backgroundColor: theme.colors.white, @todo with props
34
+ justifyContent: 'space-between',
35
+ alignItems: 'center'
36
+ }
37
+ });
38
+
39
+ const SLIDE_HEIGHT = () => useWindowDimensions().height * 0.75;
40
+
41
+ const creatSlideStyle = (num: number) =>
42
+ StyleSheet.create({
43
+ slide: {
44
+ position: 'absolute',
45
+ // backgroundColor: theme.colors.white, @todo with props
46
+ top: useWindowDimensions().height / 2 - SLIDE_HEIGHT() / 2 - num * 4,
47
+ flex: 1,
48
+ height: SLIDE_HEIGHT(),
49
+ width: useWindowDimensions().width - 40 - num * 8,
50
+ justifyContent: 'space-between',
51
+ alignItems: 'center',
52
+ padding: 25,
53
+ shadowColor: '#000',
54
+ shadowOffset: {width: 0, height: -1},
55
+ shadowOpacity: 0.05,
56
+ shadowRadius: 16,
57
+ elevation: 10 - num * 1,
58
+ borderRadius: 16
59
+ },
60
+ category: {
61
+ fontSize: 12,
62
+ lineHeight: 16,
63
+ // color: theme.colors.text.primary, @todo with props
64
+ // marginBottom: theme.spacing.tiny, @todo with props
65
+ // marginTop: theme.spacing.small, @todo with props
66
+ textAlign: 'center'
67
+ },
68
+ question: {
69
+ fontSize: 16,
70
+ lineHeight: 22,
71
+ fontWeight: '700',
72
+ // color: theme.colors.text.primary, @todo with props
73
+ textAlign: 'center'
74
+ },
75
+ instruction: {
76
+ fontSize: 12,
77
+ lineHeight: 16,
78
+ // color: theme.colors.gray.medium, @todo with props
79
+ // marginBottom: theme.spacing.base, @todo with props
80
+ // marginTop: theme.spacing.small, @todo with props
81
+ textAlign: 'center'
82
+ },
83
+ button: {
84
+ width: 287,
85
+ height: 52,
86
+ borderRadius: 7
87
+ // marginTop: theme.spacing.base @todo with props
88
+ }
89
+ });
90
+
91
+ const createOptionStyle = (selected: boolean) => {
92
+ // const brandTheme = React.useContext(BrandThemeContext); @todo with props
93
+
94
+ return StyleSheet.create({
95
+ box: {
96
+ width: '100%',
97
+ // https://stackoverflow.com/a/66561995/959219
98
+ shadowColor: '#000',
99
+ shadowOffset: {width: 0, height: -1},
100
+ shadowOpacity: 0.2,
101
+ shadowRadius: 16,
102
+ elevation: 8,
103
+ // backgroundColor: selected ? brandTheme.colors.primary : theme.colors.white, @todo with props
104
+ padding: 12,
105
+ marginTop: 4,
106
+ marginBottom: 4,
107
+ borderRadius: 8
108
+ },
109
+ text: {
110
+ fontSize: 15,
111
+ fontWeight: 'bold',
112
+ lineHeight: 24,
113
+ // color: selected ? theme.colors.white : theme.colors.text.primary, @todo with props
114
+ textAlign: 'center'
115
+ }
116
+ });
117
+ };
118
+
119
+ const choicesStyle = StyleSheet.create({
120
+ container: {
121
+ flex: 1,
122
+ width: '100%',
123
+ justifyContent: 'center',
124
+ alignItems: 'center'
125
+ }
126
+ });
127
+
128
+ const onButtonPress = () => {};
129
+
130
+ const Choices = ({choices}: any) => (
131
+ <View style={choicesStyle.container}>
132
+ {choices.map(({text, selected = false}: _Choice_) => {
133
+ const optionStyle = createOptionStyle(selected);
134
+ return (
135
+ <View style={optionStyle.box}>
136
+ <Text style={optionStyle.text}>
137
+ {text} {selected}
138
+ </Text>
139
+ </View>
140
+ );
141
+ })}
142
+ </View>
143
+ );
144
+
145
+ const Slide = ({slide, num}: Props) => {
146
+ const slideStyle = creatSlideStyle(num);
147
+
148
+ return (
149
+ <View style={slideStyle.slide}>
150
+ <Text style={slideStyle.category}>
151
+ {slide.category} {num}
152
+ </Text>
153
+ <Text style={slideStyle.question}>{slide.question}</Text>
154
+ <Text style={slideStyle.instruction}>{slide.instruction}</Text>
155
+
156
+ <Choices choices={slide.choices} />
157
+
158
+ {/* <Button style={slideStyle.button} onPress={onButtonPress} testID={`button-quizzer-validate`}>
159
+ {translations.validate}
160
+ </Button> */}
161
+ </View>
162
+ );
163
+ };
164
+
165
+ const RevisionQuizzer = ({slide}: Props) => {
166
+ const slides = [slide, slide, slide, slide, slide];
167
+ return (
168
+ <View style={quizzerStyle.container}>
169
+ {slides.reverse().map((slide, index) => (
170
+ <Slide slide={slide} num={slides.length - index} />
171
+ ))}
172
+ </View>
173
+ );
174
+ };
175
+
176
+ export default RevisionQuizzer;
@@ -0,0 +1,24 @@
1
+ import {applyMiddleware, compose, createStore} from 'redux';
2
+ import thunk from 'redux-thunk';
3
+ import {composeWithDevTools} from 'redux-devtools-extension';
4
+
5
+ import * as actionCreators from './actions';
6
+ import rootReducer from './reducers';
7
+
8
+ const preloadedState = undefined;
9
+
10
+ export default function configureStore(services) {
11
+ const _compose = process.env.NODE_ENV !== 'production' ? composeWithDevTools : compose;
12
+
13
+ const composeEnhancers = _compose({
14
+ actionCreators,
15
+ name: 'app-review',
16
+ trace: true,
17
+ traceLimit: 25
18
+ });
19
+
20
+ const composedEnhancer = composeEnhancers(applyMiddleware(thunk));
21
+ const store = createStore(rootReducer, preloadedState, composedEnhancer);
22
+
23
+ return store;
24
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,103 @@
1
+ import React, {useEffect, useState} from 'react';
2
+ import {Text, StyleSheet, Pressable, View, BackHandler} from 'react-native';
3
+ import {connect, Provider} from 'react-redux';
4
+
5
+ import configureStore from './configure-store';
6
+ import {navigateTo, navigateBack} from './actions/navigation';
7
+ import {getCurrentViewName} from './reducers/navigation';
8
+
9
+ // -----------------------------------------------------------------------------
10
+
11
+ const styles = StyleSheet.create({
12
+ rootView: {
13
+ flex: 1,
14
+ alignItems: 'center',
15
+ justifyContent: 'center'
16
+ }
17
+ });
18
+
19
+ // -----------------------------------------------------------------------------
20
+
21
+ const NavText = ({viewName, navigateTo}) => {
22
+ console.log({viewName});
23
+ switch (viewName) {
24
+ case 'home':
25
+ return (
26
+ <Pressable
27
+ onPress={() => {
28
+ navigateTo('onboarding');
29
+ }}
30
+ >
31
+ <Text>view home</Text>
32
+ </Pressable>
33
+ );
34
+ case 'onboarding':
35
+ return (
36
+ <Pressable onPress={() => navigateTo('quizzer')}>
37
+ <Text>view onboarding</Text>
38
+ </Pressable>
39
+ );
40
+ case 'quizzer':
41
+ return (
42
+ <Pressable onPress={() => navigateTo('home')}>
43
+ <Text>view quizzer</Text>
44
+ </Pressable>
45
+ );
46
+ default:
47
+ return <Text>view (none selected)</Text>;
48
+ }
49
+ };
50
+
51
+ // -----------------------------------------------------------------------------
52
+
53
+ const RootView = (props) => {
54
+ const backAction = () => {
55
+ props.navigateBack();
56
+ return true;
57
+ };
58
+
59
+ useEffect(() => {
60
+ // https://reactnative.dev/docs/backhandler
61
+ const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
62
+
63
+ return () => backHandler.remove();
64
+ }, []);
65
+
66
+ return (
67
+ <View style={styles.rootView}>
68
+ <NavText viewName={props.viewName} navigateTo={props.navigateTo} />
69
+ </View>
70
+ );
71
+ };
72
+
73
+ // -----------------------------------------------------------------------------
74
+
75
+ const mapStateToProps = (state, ownProps) => {
76
+ return {...ownProps, viewName: getCurrentViewName(state)};
77
+ };
78
+
79
+ const mapDispatchToProps = {navigateTo, navigateBack};
80
+ const App = connect(mapStateToProps, mapDispatchToProps)(RootView);
81
+
82
+ // -----------------------------------------------------------------------------
83
+
84
+ const AppRevision = ({services}) => {
85
+ const [store, setStore] = useState(null);
86
+
87
+ useEffect(() => {
88
+ const store = configureStore(services);
89
+ setStore(store);
90
+ }, []);
91
+
92
+ if (!store) return null;
93
+
94
+ return (
95
+ <Provider store={store}>
96
+ <App />
97
+ </Provider>
98
+ );
99
+ };
100
+
101
+ // -----------------------------------------------------------------------------
102
+
103
+ export default AppRevision;
@@ -0,0 +1,8 @@
1
+ import {combineReducers} from 'redux';
2
+ import navigation from './navigation';
3
+
4
+ const rootReducer = combineReducers({
5
+ navigation
6
+ });
7
+
8
+ export default rootReducer;
@@ -0,0 +1,24 @@
1
+ import {NAVIGATE_BACK, NAVIGATE_TO} from '../actions/navigation';
2
+ import type {$NAVIGATE_TO, $NAVIGATE_BACK} from '../actions/navigation';
3
+
4
+ export type State = Array<string>;
5
+
6
+ export const initialState: State = ['home'];
7
+
8
+ const reducer = (state: State = initialState, action: $NAVIGATE_TO | $NAVIGATE_BACK): State => {
9
+ switch (action.type) {
10
+ case NAVIGATE_TO: {
11
+ return [...state, action.payload];
12
+ }
13
+ case NAVIGATE_BACK: {
14
+ const newState = [...state];
15
+ newState.pop();
16
+ return newState;
17
+ }
18
+ default:
19
+ return state;
20
+ }
21
+ };
22
+
23
+ export const getCurrentViewName = appState => appState.navigation[appState.navigation.length - 1];
24
+ export default reducer;