diff --git a/App.tsx b/App.tsx index eae3889..3fe0dac 100644 --- a/App.tsx +++ b/App.tsx @@ -4,6 +4,11 @@ import {Navigation} from './src/module/root'; import './src/i18n/index'; import {Provider} from 'react-redux'; import { store } from './src/store/store'; + +if(__DEV__) { + import('./ReactotronConfig').then(() => console.log('Reactotron Configured')) +} + const App = () => { useEffect(() => { SplashScreen.hide(); diff --git a/ReactotronConfig.js b/ReactotronConfig.js new file mode 100644 index 0000000..554d916 --- /dev/null +++ b/ReactotronConfig.js @@ -0,0 +1,8 @@ +import AsyncStorage from '@react-native-async-storage/async-storage' +import Reactotron from 'reactotron-react-native' + +Reactotron + .setAsyncStorageHandler(AsyncStorage) // AsyncStorage would either come from `react-native` or `@react-native-community/async-storage` depending on where you get it from + .configure() // controls connection & communication settings + .useReactNative() // add all built-in react native plugins + .connect() // let's connect! \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2ec631e..9d28e29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "jest": "^26.6.3", "metro-react-native-babel-preset": "^0.66.2", "react-test-renderer": "17.0.2", + "reactotron-react-native": "^5.0.3", "typescript": "^4.4.4" } }, @@ -12145,6 +12146,12 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, + "node_modules/mitt": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.1.3.tgz", + "integrity": "sha512-mUDCnVNsAi+eD6qA0HkRkwYczbLHJ49z17BGe2PYRhZL4wpZUFZGJHU7/5tmvohoma+Hdn0Vh/oJTiPEmgSruA==", + "dev": true + }, "node_modules/mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -13443,6 +13450,17 @@ "nullthrows": "^1.1.1" } }, + "node_modules/react-native-flipper": { + "version": "0.164.0", + "resolved": "https://registry.npmjs.org/react-native-flipper/-/react-native-flipper-0.164.0.tgz", + "integrity": "sha512-iJhIe3rqx6okuzBp4AJsTa2b8VRAOGzoLRFx/4HGbaGvu8AurZjz8TTQkhJsRma8dsHN2b6KKZPvGGW3wdWzvA==", + "dev": true, + "optional": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-native": ">0.62.0" + } + }, "node_modules/react-native-gesture-handler": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.5.0.tgz", @@ -14038,6 +14056,47 @@ "react": "17.0.2" } }, + "node_modules/reactotron-core-client": { + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/reactotron-core-client/-/reactotron-core-client-2.8.10.tgz", + "integrity": "sha512-SYRO4OCutJzfWMnaULUGVyETZnMDCU5ECNflXyM3Z5Gnfxp/wV6d7jYonhfxHdpU/aGb4Eg15C22myOCXSu6HQ==", + "dev": true + }, + "node_modules/reactotron-react-native": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/reactotron-react-native/-/reactotron-react-native-5.0.3.tgz", + "integrity": "sha512-uUQ074uw3I9X/pc7FBgrrwrFzfwXDKlxzuekNjzspZz9Y0qVLX1cAm9GTC0ZPsZRvY5wDPY/Il7XfV1YeVSDxA==", + "dev": true, + "dependencies": { + "mitt": "1.1.3", + "query-string": "6.10.1", + "reactotron-core-client": "2.8.10", + "rn-host-detect": "1.2.0" + }, + "optionalDependencies": { + "react-native-flipper": "^0.164.0" + }, + "peerDependencies": { + "react-native": ">=0.40.0" + } + }, + "node_modules/reactotron-react-native/node_modules/query-string": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.10.1.tgz", + "integrity": "sha512-SHTUV6gDlgMXg/AQUuLpTiBtW/etZ9JT6k6RCtCyqADquApLX0Aq5oK/s5UeTUAWBG50IExjIr587GqfXRfM4A==", + "dev": true, + "dependencies": { + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -14470,6 +14529,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rn-host-detect": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/rn-host-detect/-/rn-host-detect-1.2.0.tgz", + "integrity": "sha512-btNg5kzHcjZZ7t7mvvV/4wNJ9e3MPgrWivkRgWURzXL0JJ0pwWlU4zrbmdlz3HHzHOxhBhHB4D+/dbMFfu4/4A==", + "dev": true + }, "node_modules/rsvp": { "version": "4.8.5", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", @@ -26011,6 +26076,12 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, + "mitt": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.1.3.tgz", + "integrity": "sha512-mUDCnVNsAi+eD6qA0HkRkwYczbLHJ49z17BGe2PYRhZL4wpZUFZGJHU7/5tmvohoma+Hdn0Vh/oJTiPEmgSruA==", + "dev": true + }, "mixin-deep": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", @@ -27167,6 +27238,14 @@ "nullthrows": "^1.1.1" } }, + "react-native-flipper": { + "version": "0.164.0", + "resolved": "https://registry.npmjs.org/react-native-flipper/-/react-native-flipper-0.164.0.tgz", + "integrity": "sha512-iJhIe3rqx6okuzBp4AJsTa2b8VRAOGzoLRFx/4HGbaGvu8AurZjz8TTQkhJsRma8dsHN2b6KKZPvGGW3wdWzvA==", + "dev": true, + "optional": true, + "requires": {} + }, "react-native-gesture-handler": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.5.0.tgz", @@ -27404,6 +27483,38 @@ "scheduler": "^0.20.2" } }, + "reactotron-core-client": { + "version": "2.8.10", + "resolved": "https://registry.npmjs.org/reactotron-core-client/-/reactotron-core-client-2.8.10.tgz", + "integrity": "sha512-SYRO4OCutJzfWMnaULUGVyETZnMDCU5ECNflXyM3Z5Gnfxp/wV6d7jYonhfxHdpU/aGb4Eg15C22myOCXSu6HQ==", + "dev": true + }, + "reactotron-react-native": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/reactotron-react-native/-/reactotron-react-native-5.0.3.tgz", + "integrity": "sha512-uUQ074uw3I9X/pc7FBgrrwrFzfwXDKlxzuekNjzspZz9Y0qVLX1cAm9GTC0ZPsZRvY5wDPY/Il7XfV1YeVSDxA==", + "dev": true, + "requires": { + "mitt": "1.1.3", + "query-string": "6.10.1", + "react-native-flipper": "^0.164.0", + "reactotron-core-client": "2.8.10", + "rn-host-detect": "1.2.0" + }, + "dependencies": { + "query-string": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-6.10.1.tgz", + "integrity": "sha512-SHTUV6gDlgMXg/AQUuLpTiBtW/etZ9JT6k6RCtCyqADquApLX0Aq5oK/s5UeTUAWBG50IExjIr587GqfXRfM4A==", + "dev": true, + "requires": { + "decode-uri-component": "^0.2.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + } + } + } + }, "read-pkg": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", @@ -27738,6 +27849,12 @@ "glob": "^7.1.3" } }, + "rn-host-detect": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/rn-host-detect/-/rn-host-detect-1.2.0.tgz", + "integrity": "sha512-btNg5kzHcjZZ7t7mvvV/4wNJ9e3MPgrWivkRgWURzXL0JJ0pwWlU4zrbmdlz3HHzHOxhBhHB4D+/dbMFfu4/4A==", + "dev": true + }, "rsvp": { "version": "4.8.5", "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", diff --git a/package.json b/package.json index 3f3f26c..d060e4d 100644 --- a/package.json +++ b/package.json @@ -55,6 +55,7 @@ "jest": "^26.6.3", "metro-react-native-babel-preset": "^0.66.2", "react-test-renderer": "17.0.2", + "reactotron-react-native": "^5.0.3", "typescript": "^4.4.4" }, "resolutions": { diff --git a/src/module/package/components/packages-item.component.tsx b/src/module/package/components/packages-item.component.tsx index 2e90b4d..9f9c0a1 100644 --- a/src/module/package/components/packages-item.component.tsx +++ b/src/module/package/components/packages-item.component.tsx @@ -4,6 +4,7 @@ import {StyleSheet, TouchableOpacity, View, Text} from 'react-native'; import { useAppDispatch } from '../../../store/hooks'; import { nextStep, resetSteps } from '../../services/current-step/current-step-slice'; import { shuffleDares } from '../../services/dares/dares-slice'; +import { shuffleItems } from '../../services/game-items/game-items-slice'; import { shuffleTruths } from '../../services/truths/truth-slice'; import {Icon, RouteKey} from '../../shared'; @@ -29,11 +30,9 @@ export const PackagesItem: FC = ({ const dispatch = useAppDispatch(); const play = () => { - console.log(title); navigation.navigate(RouteKey.Package, {title}); dispatch(resetSteps()); - dispatch(shuffleTruths()); - dispatch(shuffleDares()); + dispatch(shuffleItems()); } return ( diff --git a/src/module/root/navigations-groups/on-boardings-group.tsx b/src/module/root/navigations-groups/on-boardings-group.tsx index a87f026..6ec025b 100644 --- a/src/module/root/navigations-groups/on-boardings-group.tsx +++ b/src/module/root/navigations-groups/on-boardings-group.tsx @@ -9,36 +9,55 @@ import {Questions} from '../screens/questions'; import {PrivacyPolicy} from '../../privacy-policy/screens/privacy-policy'; import {UseOfTerms} from '../../terms-of-use/screens/use-of-terms'; import firestore from '@react-native-firebase/firestore'; -import AsyncStorage from '@react-native-async-storage/async-storage'; +import { useAppDispatch, useAppSelector } from '../../../store/hooks'; +import { fetchPostsAsync, selectPosts } from '../../services/game-items/game-items-slice'; // console.log(usersCollection); const Stack = createNativeStackNavigator(); export const OnboardingGroup: FC = () => { - // const usersCollection = firestore() - // .collection('GameItems') - // .get() - // .then(querySnapshot => { - // querySnapshot.forEach(snapshot => { - // let data = snapshot.data(); - // data.GameItems.map(item => console.log(item)); - // }); - // }); -const getall = async () => { - const keys = await AsyncStorage.getAllKeys(); - const result = await AsyncStorage.multiGet(keys); - console.log(result); -} + const submitPost = async () => { + firestore() + .collection('GameItems') + .add({ + id: 6, + isDare: true, + en: 'Dare 3', + ua: 'Дія 3', + hi: 'हिम्मत 3' + }) + .then(() => {}) + .catch(error => console.log('Error in firebase')); + }; + // submitPost(); -getall(); + const fetchPosts = async () => { + try { + firestore() + .collection('GameItems') + .get() + .then((querySnapshot) => { + querySnapshot.forEach(doc => { + const item = doc.data(); + console.log(item); + }) + }).catch((error) => { + console.log('Error in fetch: ', error); + }) + } catch(error) { + console.log('Error in fetch: ', error); + } + } + + // fetchPosts(); return ( + initialRouteName={RouteKey.Loading}> diff --git a/src/module/root/screens/loading-screen.tsx b/src/module/root/screens/loading-screen.tsx index 985958c..c9cebe7 100644 --- a/src/module/root/screens/loading-screen.tsx +++ b/src/module/root/screens/loading-screen.tsx @@ -11,11 +11,15 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; import {useNavigation, CommonActions} from '@react-navigation/native'; import {StorageKey, storageService} from '../../services/async-storage.service'; import { useTranslation } from 'react-i18next'; +import { useAppDispatch } from '../../../store/hooks'; +import { fetchPostsAsync } from '../../services/game-items/game-items-slice'; export const LoadingScreen = () => { const {i18n} = useTranslation(); const navigate = useNavigation(); + const dispatch = useAppDispatch(); + const getLanguage = async () => { const response = await AsyncStorage.getItem(StorageKey.Language); return response; @@ -29,6 +33,7 @@ export const LoadingScreen = () => { const init = async () => { let language = await getLanguage(); const isOnBoard = await getOnboardEnd(); + dispatch(fetchPostsAsync()); if (language) { i18n.changeLanguage(language); diff --git a/src/module/root/screens/questions.tsx b/src/module/root/screens/questions.tsx index ceb275b..08f85ef 100644 --- a/src/module/root/screens/questions.tsx +++ b/src/module/root/screens/questions.tsx @@ -25,6 +25,7 @@ import { } from '../../services/current-step/current-step-slice'; import {shuffleTruths} from '../../services/truths/truth-slice'; import {shuffleDares} from '../../services/dares/dares-slice'; +import { shuffleItems } from '../../services/game-items/game-items-slice'; interface IProps extends IRouteParams {} export const Questions: React.FC = ({navigation, route}) => { @@ -44,8 +45,7 @@ export const Questions: React.FC = ({navigation, route}) => { }; const refreshList = () => { - dispatch(shuffleTruths()); - dispatch(shuffleDares()); + dispatch(shuffleItems()) dispatch(resetSteps()); }; diff --git a/src/module/services/game-items/game-items-slice.ts b/src/module/services/game-items/game-items-slice.ts new file mode 100644 index 0000000..aa53308 --- /dev/null +++ b/src/module/services/game-items/game-items-slice.ts @@ -0,0 +1,65 @@ +import {createAsyncThunk, createSlice} from '@reduxjs/toolkit'; +import {GameItem} from '../../shared/interfaces/game-item'; +import firestore from '@react-native-firebase/firestore'; +import {RootState} from '../../../store/store'; +import { shuffle } from '../shuffle/shuffle'; + +export interface PostsState { + posts: GameItem[]; + shuffled: GameItem[]; + loaded: boolean; + hasError: boolean; +} + +const initialState: PostsState = { + posts: [], + shuffled: [], + loaded: false, + hasError: false, +}; + +export const fetchPostsAsync = createAsyncThunk( + 'posts/fetchPosts', + async () => { + const data = [] as GameItem[]; + let querySnapshot = await firestore().collection('GameItems').get(); + querySnapshot.forEach(doc => { + const item = doc.data(); + data.push(item as GameItem); + }); + return data; + }, +); + +export const postsSlice = createSlice({ + name: 'posts', + initialState, + reducers: { + shuffleItems: state => { + state.shuffled = shuffle(state.posts); + }, + }, + extraReducers(builder) { + builder + .addCase(fetchPostsAsync.fulfilled, (state, action) => { + state.posts = action.payload; + state.hasError = false; + state.loaded = true; + }) + .addCase(fetchPostsAsync.pending, state => { + state.loaded = false; + }) + .addCase(fetchPostsAsync.rejected, state => { + state.hasError = true; + }); + }, +}); + +export const {shuffleItems} = postsSlice.actions; + +export const selectPosts = (state: RootState) => state.gameItems.posts; +export const selectHasError = (state: RootState) => state.gameItems.hasError; +export const selectLoaded = (state: RootState) => state.gameItems.loaded; +export const selectShuffled = (state: RootState) => state.gameItems.shuffled; + +export default postsSlice.reducer; diff --git a/src/module/shared/components/question-block/question-block.tsx b/src/module/shared/components/question-block/question-block.tsx index 88c267c..a6daf42 100644 --- a/src/module/shared/components/question-block/question-block.tsx +++ b/src/module/shared/components/question-block/question-block.tsx @@ -13,6 +13,9 @@ import { shuffleTruths, } from '../../../services/truths/truth-slice'; import {getShuffledDares, shuffleDares} from '../../../services/dares/dares-slice'; +import { selectPosts, selectShuffled, shuffleItems } from '../../../services/game-items/game-items-slice'; +import { useTranslation } from 'react-i18next'; +import { GameItem } from '../../interfaces/game-item'; interface IProps { isQuestions: boolean; @@ -21,21 +24,30 @@ interface IProps { export const QuestionBlock: React.FC = ({isQuestions}) => { const navigation = useNavigation(); const dispatch = useAppDispatch(); + const {i18n} = useTranslation(); + const lang = i18n.language; const currentStep = useAppSelector(getStep); + const shuffledTruths = useAppSelector(getShuffledTruths); const shuffledDares = useAppSelector(getShuffledDares); - - console.log(currentStep); + + const gameItems = useAppSelector(selectShuffled); + + const dares = gameItems.filter((dare) => dare.isDare); + const questions = gameItems.filter((question) => !question.isDare); + + console.log(dares); + useEffect(() => { - if (currentStep >= shuffledTruths.length) { + if (currentStep >= questions.length) { dispatch(resetSteps()); - dispatch(shuffleTruths()); + dispatch(shuffleItems()); } - if (currentStep >= shuffledDares.length) { + if (currentStep >= dares.length) { dispatch(resetSteps()); - dispatch(shuffleDares()); + dispatch(shuffleItems()); } }, [currentStep]); @@ -45,12 +57,12 @@ export const QuestionBlock: React.FC = ({isQuestions}) => { - {currentStep < shuffledTruths.length && + {currentStep < questions.length && isQuestions && - shuffledTruths[currentStep].question} - {currentStep < shuffledDares.length && + questions[currentStep][lang as keyof GameItem]} + {currentStep < dares.length && !isQuestions && - shuffledDares[currentStep].dare} + dares[currentStep][lang as keyof GameItem]} ); diff --git a/src/module/shared/interfaces/game-item.ts b/src/module/shared/interfaces/game-item.ts new file mode 100644 index 0000000..1664bff --- /dev/null +++ b/src/module/shared/interfaces/game-item.ts @@ -0,0 +1,7 @@ +export interface GameItem { + id: number; + isDare: boolean; + en: string; + ua: string; + hi: string; +} \ No newline at end of file diff --git a/src/store/store.ts b/src/store/store.ts index f5cb835..e77b07a 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -1,6 +1,7 @@ -import { configureStore } from '@reduxjs/toolkit'; +import { Action, configureStore, ThunkAction } from '@reduxjs/toolkit'; import currentStepSlice from '../module/services/current-step/current-step-slice'; import daresSlice from '../module/services/dares/dares-slice'; +import postsSlice from '../module/services/game-items/game-items-slice'; import truthsSlice from '../module/services/truths/truth-slice'; export const store = configureStore({ @@ -8,8 +9,16 @@ export const store = configureStore({ currentStep: currentStepSlice, truth: truthsSlice, dares: daresSlice, + gameItems: postsSlice, }, }); export type AppDispatch = typeof store.dispatch; -export type RootState = ReturnType; \ No newline at end of file +export type RootState = ReturnType; + +export type AppThunk = ThunkAction< + ReturnType, + RootState, + unknown, + Action +>; \ No newline at end of file