Browse Source

FEATURE | Add players

pull/5/head
Vlad Narizhnyi 11 months ago
parent
commit
759bdf73e0
  1. 1
      src/i18n/interfaces/page-titles.interface.ts
  2. 6
      src/i18n/locales/en/custom-pack.translation.ts
  3. 1
      src/i18n/locales/en/page-title.translation.ts
  4. 12
      src/i18n/locales/ua/custom-pack.translation.ts
  5. 1
      src/i18n/locales/ua/page-title.translation.ts
  6. 1
      src/module/common/components/form/form-controll-wrap.component.tsx
  7. 13
      src/module/common/components/form/form-text-controll.component.tsx
  8. 41
      src/module/common/components/header/header.component.tsx
  9. 5
      src/module/common/typing/enums/guest-route-keys.enum.ts
  10. 10
      src/module/common/typing/enums/index.ts
  11. 4
      src/module/common/typing/enums/nav-group.enum.ts
  12. 5
      src/module/common/typing/enums/storage-key.enum.ts
  13. 6
      src/module/common/typing/enums/user-route-keys.enum.ts
  14. 6
      src/module/custom-package/screens/custom-package-editor.screen.tsx
  15. 19
      src/module/custom-package/screens/custom-package-play.screen.tsx
  16. 35
      src/module/game/animations/use-animation-button.ts
  17. 63
      src/module/game/animations/use-animation-truth-or-dare.hook.ts
  18. 1
      src/module/game/components/index.ts
  19. 49
      src/module/game/components/player-field.component.tsx
  20. 20
      src/module/game/components/player-name.component.tsx
  21. 37
      src/module/game/components/truth-or-dare-view.tsx
  22. 10
      src/module/game/helper/get-current-truth-dares.helper.ts
  23. 59
      src/module/game/screens/game.screen.tsx
  24. 1
      src/module/game/screens/index.ts
  25. 124
      src/module/game/screens/players.screen.tsx
  26. 28
      src/module/game/screens/truth-or-dare.screen.tsx
  27. 4
      src/module/packages/animation/use-animation-list.hook.ts
  28. 12
      src/module/packages/atoms/animated-diamond-icon.atom.tsx
  29. 4
      src/module/packages/atoms/create-custom-package.atom.tsx
  30. 4
      src/module/packages/components/packages-item.component.tsx
  31. 13
      src/module/packages/screens/packages-list.screen.tsx
  32. 10
      src/module/root/atoms/on-boarding-button.component.tsx
  33. 14
      src/module/root/index.tsx
  34. 1
      src/module/root/navigations-groups/index.tsx
  35. 34
      src/module/root/navigations-groups/quest.group.tsx
  36. 74
      src/module/root/navigations-groups/user.group.tsx
  37. 2
      src/module/root/screens/index.tsx
  38. 4
      src/module/root/screens/language-select.screen.tsx
  39. 32
      src/module/root/screens/loading-screen.tsx
  40. 19
      src/module/root/screens/on-boarding.screen.tsx
  41. 4
      src/module/settings/screens/purchases.screen.tsx
  42. 13
      src/module/settings/screens/settings.screen.tsx
  43. 2
      src/store/slices/current-step.slice.ts
  44. 242
      src/store/slices/game-items.slice.ts
  45. 8
      src/store/slices/index.ts
  46. 27
      src/store/slices/navigation.slice.ts
  47. 70
      src/store/slices/players.slice.ts
  48. 65
      src/store/slices/posts.slice.ts
  49. 14
      src/store/store.ts

1
src/i18n/interfaces/page-titles.interface.ts

@ -2,4 +2,5 @@ export interface PageTitles { @@ -2,4 +2,5 @@ export interface PageTitles {
settings: string,
privacy: string,
terms: string,
players: string
}

6
src/i18n/locales/en/custom-pack.translation.ts

@ -7,9 +7,9 @@ export const customPack: CustomPack = { @@ -7,9 +7,9 @@ export const customPack: CustomPack = {
'Create your own custom pack with questions and task. It all depends on your imagination!',
editor: 'Editor',
placeholder: 'Write here...',
addTruth: 'Add a truth',
addTruth: 'Add a question',
addDare: 'Add a dare',
viewTruths: 'View truths',
viewTruths: 'View questions',
viewDares: 'View dares',
alertCreateTitle: 'Gratefully!',
alertCreateDesc: 'You can play your custom package now!',
@ -19,7 +19,7 @@ export const customPack: CustomPack = { @@ -19,7 +19,7 @@ export const customPack: CustomPack = {
alertSaveYes: 'Save',
alertEmptyTitle: 'Oops!',
alertEmptyTruthDesc:
'Your truths list is empty. You need have at least 1 truth',
'Your questions list is empty. You need have at least 1 question',
alertEmptyDaresDesc:
'Your dares list is empty. You need have at least 1 dare',
editorBtn: 'Tasks and questions editor',

1
src/i18n/locales/en/page-title.translation.ts

@ -4,4 +4,5 @@ export const pageTitles = { @@ -4,4 +4,5 @@ export const pageTitles = {
privacy: 'Privacy Policy',
terms: 'Terms and conditions',
writeToUs: 'Write to us',
players: 'Players'
}

12
src/i18n/locales/ua/custom-pack.translation.ts

@ -4,24 +4,24 @@ export const customPack: CustomPack = { @@ -4,24 +4,24 @@ export const customPack: CustomPack = {
label: 'Власний пакет',
title: 'Створити власний пакет',
description:
'Створіть свій власний пакет з правд та дій. Все залежить від вашої уяви!',
'Створіть свій власний пакет з питань та дій. Все залежить від твоєї уяви!',
editor: 'Редактор',
placeholder: 'Пишіть тут...',
placeholder: 'Пиши тут...',
addTruth: 'Додати питання',
addDare: 'Додати дію',
viewTruths: 'Питання',
viewDares: 'Дії',
alertCreateTitle: 'Чудово! 🎉',
alertCreateDesc: 'Зіграйте прямо зараз!',
alertSaveTitle: 'Ви маєте незбережені зміни',
alertCreateDesc: 'Зіграй прямо зараз!',
alertSaveTitle: 'У тебе незбережені зміни',
alertSaveDesc: 'Зберегти зміни?',
alertSaveNo: 'Ні',
alertSaveYes: 'Так',
alertEmptyTitle: 'Ой! 👀',
alertEmptyTruthDesc:
'Ваш список правд порожній. Вам потрібно мати хоча б 1 правду',
'Твій список питань порожній. Потрібно мати хоча б 1 питання',
alertEmptyDaresDesc:
'Ваш список викликів порожній. Вам потрібно мати хоча б 1 виклик',
'Твій список дій порожній. Потрібно мати хоча б 1 дії',
editorBtn: 'Редактор правд та дій',
play: 'Грати',
}

1
src/i18n/locales/ua/page-title.translation.ts

@ -4,4 +4,5 @@ export const pageTitles = { @@ -4,4 +4,5 @@ export const pageTitles = {
privacy: 'Політика \n конфіденційності',
terms: 'Правила та умови',
writeToUs: 'Напишіть нам',
players: 'Гравці',
}

1
src/module/common/components/form/form-controll-wrap.component.tsx

@ -44,7 +44,6 @@ const styles = StyleSheet.create({ @@ -44,7 +44,6 @@ const styles = StyleSheet.create({
labelWrap: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 5,
},
label: {
color: colors.secondaryText,

13
src/module/common/components/form/form-text-controll.component.tsx

@ -19,7 +19,9 @@ interface FormTextControllProps { @@ -19,7 +19,9 @@ interface FormTextControllProps {
label?: string
error?: string
renderClearPostfix?: () => React.JSX.Element
renderPostfix?: () => React.JSX.Element
renderPrefix?: () => React.JSX.Element
subtext?: string
inputStyle?: ViewStyle
@ -35,7 +37,8 @@ export const FormTextControll: FC<FormTextControllProps> = ({ @@ -35,7 +37,8 @@ export const FormTextControll: FC<FormTextControllProps> = ({
label,
error,
renderClearPostfix,
renderPostfix,
renderPrefix,
inputStyle,
style,
@ -43,6 +46,7 @@ export const FormTextControll: FC<FormTextControllProps> = ({ @@ -43,6 +46,7 @@ export const FormTextControll: FC<FormTextControllProps> = ({
return (
<FormControllWrap label={label} error={error} style={style}>
<View style={styles.inputContainer}>
{renderPrefix && renderPrefix()}
<TextInput
style={[
styles.input,
@ -54,7 +58,7 @@ export const FormTextControll: FC<FormTextControllProps> = ({ @@ -54,7 +58,7 @@ export const FormTextControll: FC<FormTextControllProps> = ({
placeholderTextColor="#A0A3BD"
{...inputProps}
/>
{renderClearPostfix && renderClearPostfix()}
{renderPostfix && renderPostfix()}
</View>
</FormControllWrap>
)
@ -65,7 +69,7 @@ const styles = StyleSheet.create({ @@ -65,7 +69,7 @@ const styles = StyleSheet.create({
borderColor: '#FB5450',
},
inputContainer: {
paddingRight: 1,
flexDirection: 'row',
},
input: {
borderColor: colors.secondaryText,
@ -76,6 +80,7 @@ const styles = StyleSheet.create({ @@ -76,6 +80,7 @@ const styles = StyleSheet.create({
color: colors.textPrimary,
fontFamily: Font.Roboto400,
fontSize: 16,
width: '100%',
},
inputActive: {
borderColor: '#FB5450',

41
src/module/common/components/header/header.component.tsx

@ -32,10 +32,10 @@ export const Header: FC<IProps> = ({ @@ -32,10 +32,10 @@ export const Header: FC<IProps> = ({
return (
<View style={styles.header}>
<View style={styles.button}>
<View style={styles.buttonView}>
{leftIcon && (
<TouchableOpacity
style={styles.button}
style={styles.btn}
onPress={onPressLeft || goBack}>
<Icon
name={leftIcon}
@ -46,19 +46,19 @@ export const Header: FC<IProps> = ({ @@ -46,19 +46,19 @@ export const Header: FC<IProps> = ({
)}
</View>
<View
style={[
styles.titleContainer,
gamer && { backgroundColor: colors.darkPurple },
]}>
<Txt style={styles.title}>{title}</Txt>
<View style={styles.wrap}>
<View
style={[
styles.titleContainer,
gamer && { backgroundColor: colors.darkPurple },
]}>
<Txt style={styles.title}>{title}</Txt>
</View>
</View>
<View style={styles.button}>
<View style={styles.buttonView}>
{rightIcon && (
<TouchableOpacity
style={styles.button}
onPress={onPressRight}>
<TouchableOpacity style={styles.btn} onPress={onPressRight}>
<Icon
name={rightIcon}
size={24}
@ -77,7 +77,6 @@ const styles = StyleSheet.create({ @@ -77,7 +77,6 @@ const styles = StyleSheet.create({
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 24,
},
title: {
color: colors.turquoise,
@ -87,13 +86,25 @@ const styles = StyleSheet.create({ @@ -87,13 +86,25 @@ const styles = StyleSheet.create({
},
titleContainer: {
borderRadius: 60,
minWidth: 130,
padding: 14,
justifyContent: 'center',
alignItems: 'center',
},
button: {
wrap: {
position: 'absolute',
width: '100%',
left: 0,
right: 0,
top: 3,
justifyContent: 'center',
alignItems: 'center',
},
buttonView: {
alignItems: 'center',
justifyContent: 'center',
zIndex: 2,
},
btn: {
paddingHorizontal: 24,
},
})

5
src/module/common/typing/enums/guest-route-keys.enum.ts

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
export enum GuestRouteKey {
Onboarding = 'Onboarding',
LanguageSelect = 'LanguageSelect',
Loading = 'Loading',
}

10
src/module/common/typing/enums/index.ts

@ -1,6 +1,10 @@ @@ -1,6 +1,10 @@
export * from './route-keys.enum'
export * from './guest-route-keys.enum'
export * from './fonts.enum'
export * from './storage-key.enum'
export * from './language.enum'
export * from './products.enum';
export * from './type-custom.enum';
export * from './products.enum'
export * from './type-custom.enum'
export * from './guest-route-keys.enum'
export * from './nav-group.enum';
export * from './user-route-keys.enum';

4
src/module/common/typing/enums/nav-group.enum.ts

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
export enum NavGroup {
User = 'u',
Guest = 'g'
}

5
src/module/common/typing/enums/storage-key.enum.ts

@ -1,7 +1,8 @@ @@ -1,7 +1,8 @@
export enum StorageKey {
OnBoarding = 'ONBOARDING_END',
Language = 'LANG_SELECTED',
FinishOnBoarding = 'FinishOnBoarding',
Language = 'Language',
Purchases = 'Purchases',
Products = 'Products',
CustomPackage = 'CustomPackage',
Players = 'Players',
}

6
src/module/common/typing/enums/route-keys.enum.ts → src/module/common/typing/enums/user-route-keys.enum.ts

@ -1,8 +1,6 @@ @@ -1,8 +1,6 @@
export enum RouteKey {
Onboarding = 'Onboarding',
LanguageSelect = 'LanguageSelect',
export enum UserRouteKey {
Game = 'Game',
Loading = 'Loading',
Players = 'Players',
Packages = 'Packages',
TruthOrDare = 'TruthOrDare',
CustomPackage = 'CustomPackage',

6
src/module/custom-package/screens/custom-package-editor.screen.tsx

@ -13,7 +13,7 @@ import { @@ -13,7 +13,7 @@ import {
TypeCustom,
useAppDispatch,
useNav,
RouteKey,
UserRouteKey,
} from '~module/common'
import { CustomBlock, EmptyItemsAtom } from '../atoms'
import _ from 'lodash'
@ -98,7 +98,7 @@ export const CustomPackageEditorScreen: FC = () => { @@ -98,7 +98,7 @@ export const CustomPackageEditorScreen: FC = () => {
saveCustomPackage()
nav.navigate(RouteKey.CustomPackage)
nav.navigate(UserRouteKey.CustomPackage)
}
const goBack = () => {
@ -159,7 +159,7 @@ export const CustomPackageEditorScreen: FC = () => { @@ -159,7 +159,7 @@ export const CustomPackageEditorScreen: FC = () => {
placeholderTextColor: colors.darkPurple,
}}
inputStyle={styles.input}
renderClearPostfix={renderClear}
renderPostfix={renderClear}
/>
<View style={styles.line} />

19
src/module/custom-package/screens/custom-package-play.screen.tsx

@ -7,7 +7,7 @@ import { @@ -7,7 +7,7 @@ import {
ButtonPrimary,
Header,
ProductsEnum,
RouteKey,
UserRouteKey,
ScreenLayout,
appEvents,
colors,
@ -23,23 +23,23 @@ export const CustomPackagePreviewScreen: FC = () => { @@ -23,23 +23,23 @@ export const CustomPackagePreviewScreen: FC = () => {
const customPackage = useSelector(selectCustomPackage)
const goToSettings = () => {
nav.navigate(RouteKey.Settings)
nav.navigate(UserRouteKey.Settings)
}
const goToCustomEditor = () => {
nav.navigate(RouteKey.CustomEditor)
nav.navigate(UserRouteKey.CustomEditor)
}
const goToGame = () => {
const isPurchased = checkIsPurchasedCustomPack()
if (!isPurchased) return nav.navigate(RouteKey.Purchases)
if (!isPurchased) return nav.navigate(UserRouteKey.Purchases)
const isFullCustomPack = checkIsFullCustomPack()
if (!isFullCustomPack) return
nav.navigate(RouteKey.Game, {
nav.navigate(UserRouteKey.Game, {
packageName: 'My package',
isCustom: true,
})
@ -89,10 +89,7 @@ export const CustomPackagePreviewScreen: FC = () => { @@ -89,10 +89,7 @@ export const CustomPackagePreviewScreen: FC = () => {
onPress={goToCustomEditor}>
{t('customPack.editorBtn')}
</ButtonPrimary>
<ButtonPrimary
style={styles.playBtn}
iconName="play"
onPress={goToGame}>
<ButtonPrimary iconName="play" onPress={goToGame}>
{t('customPack.play')}
</ButtonPrimary>
</View>
@ -109,10 +106,6 @@ const styles = StyleSheet.create({ @@ -109,10 +106,6 @@ const styles = StyleSheet.create({
paddingBottom: 50,
},
playBtn: {
flexDirection: 'row',
columnGap: 8,
},
editorBtn: {
backgroundColor: colors.darkPurple,
},

35
src/module/game/animations/use-animation-button.ts

@ -3,37 +3,44 @@ import { Animated, Easing } from 'react-native' @@ -3,37 +3,44 @@ import { Animated, Easing } from 'react-native'
import { colors } from '~module/common'
export const useAnimationButton = () => {
const buttonConfigs = [
{ delay: 400, ref: useRef(new Animated.Value(0)) },
{ delay: 1200, ref: useRef(new Animated.Value(0)) },
{ delay: 800, ref: useRef(new Animated.Value(0)) },
const animationsConfig = [
{ delay: 400, value: useRef(new Animated.Value(0)).current },
{ delay: 1200, value: useRef(new Animated.Value(0)).current },
{ delay: 800, value: useRef(new Animated.Value(0)).current },
]
const startAnimationsBorder = buttonConfigs.map(
({ delay, ref }) =>
const startAnimationsBorder = animationsConfig.map(
({ delay, value }) =>
() =>
Animated.timing(ref.current, {
Animated.timing(value, {
toValue: 1,
duration: 600,
delay,
easing: Easing.linear,
useNativeDriver: true,
}).start(() => ref.current.setValue(0)),
}).start(() => value.setValue(0)),
)
const interpolatedColors = buttonConfigs.map(({ ref }) =>
ref.current.interpolate({
const interpolatedColors = animationsConfig.map(({ value }) =>
value.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [colors.darkPurple, colors.blue, colors.darkPurple],
}),
)
const animBorderStyles = interpolatedColors.map(color => ({
borderColor: color,
const interpolatedScale = animationsConfig.map(({ value }) =>
value.interpolate({
inputRange: [0, 0.5, 1],
outputRange: [1, 1.03, 1],
}),
)
const animStyle = interpolatedColors.map((it, index) => ({
borderColor: interpolatedColors[index],
transform: [{ scale: interpolatedScale[index] }],
}))
return {
animBorderStyles,
animStyle,
startAnimationsBorder,
}
}

63
src/module/game/animations/use-animation-truth-or-dare.hook.ts

@ -2,73 +2,26 @@ import { useRef } from 'react' @@ -2,73 +2,26 @@ import { useRef } from 'react'
import { Animated } from 'react-native'
export const useAnimationTruthOrDare = () => {
const animValue = useRef(new Animated.Value(0)).current
const animText = useRef(new Animated.Value(1)).current
const animContainer = useRef(new Animated.Value(400)).current
const startAnimation = () => {
Animated.sequence([
Animated.timing(animText, {
toValue: 0,
duration: 200,
useNativeDriver: true,
}),
Animated.timing(animText, {
toValue: 1,
delay: 400,
useNativeDriver: true,
}),
]).start()
Animated.sequence([
Animated.timing(animValue, {
toValue: 1,
useNativeDriver: true,
}),
Animated.timing(animValue, {
toValue: 0,
useNativeDriver: true,
}),
]).start()
Animated.timing(animContainer, {
toValue: 0,
duration: 400,
useNativeDriver: true,
}).start()
}
const animStyleContainer = {
transform: [
{
translateY: animValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 20],
}),
},
{
rotateX: animValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '180deg'],
}),
},
],
}
const animStyleText = {
transform: [
{
scale: animText.interpolate({
inputRange: [0, 0.5, 0.75, 1],
outputRange: [0, 0.1, 0.5, 1],
}),
},
{
translateY: animValue.interpolate({
inputRange: [0, 1],
outputRange: [0, 250],
}),
translateX: animContainer,
},
],
}
return {
startAnimation,
animStyle: {
text: animStyleText,
container: animStyleContainer,
},
animStyle: animStyleContainer,
}
}

1
src/module/game/components/index.ts

@ -1 +1,2 @@ @@ -1 +1,2 @@
export * from './truth-or-dare-view'
export * from './player-field.component';

49
src/module/game/components/player-field.component.tsx

@ -0,0 +1,49 @@ @@ -0,0 +1,49 @@
import React, { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { StyleSheet, TouchableOpacity } from 'react-native'
import { FormTextControll, Icon, colors } from '~module/common'
interface IProps {
value: string
onChange: (val: string) => void
onDelete: () => void
}
export const PlayerField: FC<IProps> = ({ value, onChange, onDelete }) => {
const { t } = useTranslation()
const renderClear = () => (
<TouchableOpacity style={styles.clearIcon} onPress={onDelete}>
<Icon name={'clear'} size={24} color={colors.textPrimary} />
</TouchableOpacity>
)
return (
<FormTextControll
value={value}
onChange={onChange}
renderPostfix={renderClear}
inputProps={{
placeholder: t('customPack.placeholder'),
placeholderTextColor: colors.darkPurple,
}}
inputStyle={styles.input}
/>
)
}
const styles = StyleSheet.create({
input: {
borderRadius: 60,
backgroundColor: colors.lightPurple,
borderWidth: 0,
fontSize: 18,
color: colors.purple,
},
clearIcon: {
position: 'absolute',
top: 3,
right: 10,
padding: 10,
},
})

20
src/module/game/components/player-name.component.tsx

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
import React, { FC } from 'react'
import { StyleSheet } from 'react-native'
import { Font, Txt } from '~module/common'
interface IProps {
playerName: string
}
export const PlayerName: FC<IProps> = ({ playerName }) => {
return <Txt style={styles.playerName}>{playerName}</Txt>
}
const styles = StyleSheet.create({
playerName: {
fontFamily: Font.Roboto700,
textAlign: 'center',
fontSize: 24,
lineHeight: 34,
},
})

37
src/module/game/components/truth-or-dare-view.tsx

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import React, { useEffect, useMemo } from 'react'
import React, { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import {
Font,
@ -10,7 +10,7 @@ import { @@ -10,7 +10,7 @@ import {
useAppSelector,
} from '../../common'
import {
getStep,
selectStep,
resetSteps,
shuffleCustom,
shuffleItems,
@ -26,40 +26,32 @@ interface IProps { @@ -26,40 +26,32 @@ interface IProps {
export const TruthOrDareView: React.FC<IProps> = ({ items, isCustom }) => {
const dispatch = useAppDispatch()
const { i18n } = useTranslation()
const currentStep = useAppSelector(getStep)
const memoItems = useMemo(() => items, [items])
const currentStep = useAppSelector(selectStep)
const { startAnimation, animStyle } = useAnimationTruthOrDare()
const content = isCustom
? items?.[currentStep]
: items?.[currentStep]?.[i18n.language as Language]
useEffect(() => {
if (currentStep == memoItems.length) {
if (currentStep == items.length) {
dispatch(shuffleItems())
dispatch(resetSteps())
isCustom && dispatch(shuffleCustom())
}
startAnimation()
}, [currentStep])
if (currentStep === 0) {
startAnimation()
}
const content = isCustom
? memoItems?.[currentStep]
: memoItems?.[currentStep]?.[i18n.language as Language]
}, [currentStep, items])
return (
<Animated.View style={[styles.container, animStyle.container]}>
<Animated.View style={[styles.container, animStyle]}>
<Icon
name="magic-star"
size={24}
color={colors.turquoise}
style={styles.starIcon}
/>
<Animated.Text style={[styles.text, animStyle.text]}>
{content}
</Animated.Text>
<Animated.Text style={[styles.text]}>{content}</Animated.Text>
</Animated.View>
)
}
@ -71,7 +63,12 @@ const styles = StyleSheet.create({ @@ -71,7 +63,12 @@ const styles = StyleSheet.create({
backgroundColor: colors.darkPurple,
borderRadius: 20,
alignItems: 'center',
marginTop: '50%',
shadowColor: '#190f42',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 1,
shadowRadius: 4,
elevation: 5,
marginTop: '45%',
},
starIcon: {
marginBottom: 14,

10
src/module/game/helper/get-current-truth-dares.helper.ts

@ -10,14 +10,14 @@ export const getCurrentTruthOrDare = ({ @@ -10,14 +10,14 @@ export const getCurrentTruthOrDare = ({
isTruth,
customType,
}: UseTruthOrDareProps) => {
const gameItems = useAppSelector(selectShuffled)
const customPackage = useAppSelector(selectShuffledCustom)
const dares = gameItems.filter(dare => dare.isDare)
const questions = gameItems.filter(question => !question.isDare)
const shuffledGameItems = useAppSelector(selectShuffled)
const shuffledCustomPackage = useAppSelector(selectShuffledCustom)
const dares = shuffledGameItems.filter(dare => dare.isDare)
const questions = shuffledGameItems.filter(question => !question.isDare)
const packageTruthOrDare = isTruth ? questions : dares
const truthOrDareItems = customType
? customPackage[customType]
? shuffledCustomPackage[customType]
: packageTruthOrDare
return truthOrDareItems

59
src/module/game/screens/game.screen.tsx

@ -5,22 +5,43 @@ import { @@ -5,22 +5,43 @@ import {
colors,
Font,
Header,
RouteKey,
UserRouteKey,
ScreenLayout,
TypeCustom,
useNav,
useAppDispatch,
} from '../../common'
import { useTranslation } from 'react-i18next'
import { useIsFocused, useRoute } from '@react-navigation/native'
import { useAnimationButton } from '../animations'
import {
shuffleItems,
shuffleCustom,
resetSteps,
selectCurrentPlayer,
} from '~store/slices'
import { PlayerName } from '../components/player-name.component'
import { useSelector } from 'react-redux'
export const GameScreen: FC = () => {
const { t } = useTranslation()
const nav = useNav()
const dispatch = useAppDispatch()
const { params }: any = useRoute()
const { packageName, isCustom } = params
const isFocused = useIsFocused()
const { animBorderStyles, startAnimationsBorder } = useAnimationButton()
const { animStyle, startAnimationsBorder } = useAnimationButton()
const playerName = useSelector(selectCurrentPlayer)
useEffect(() => {
refreshList()
}, [])
const refreshList = () => {
!isCustom && dispatch(shuffleItems())
isCustom && dispatch(shuffleCustom())
dispatch(resetSteps())
}
const randomGame = () => {
const isTruthRandom = Math.random() < 0.5
@ -28,57 +49,61 @@ export const GameScreen: FC = () => { @@ -28,57 +49,61 @@ export const GameScreen: FC = () => {
? TypeCustom.Questions
: TypeCustom.Dares
nav.navigate(RouteKey.TruthOrDare, {
nav.navigate(UserRouteKey.TruthOrDare, {
isTruth: isTruthRandom,
packageName,
customType: isCustom ? customRandom : null,
playerName: playerName,
})
}
const onChooseTruth = () => {
nav.navigate(RouteKey.TruthOrDare, {
nav.navigate(UserRouteKey.TruthOrDare, {
isTruth: true,
packageName,
customType: isCustom && TypeCustom.Questions,
playerName: playerName,
})
}
const onChooseDare = () => {
nav.navigate(RouteKey.TruthOrDare, {
nav.navigate(UserRouteKey.TruthOrDare, {
packageName,
customType: isCustom && TypeCustom.Dares,
playerName: playerName,
})
}
return (
<ScreenLayout headerComponent={<Header title={packageName} gamer />}>
<PlayerName playerName={playerName} />
<View style={styles.wrapButtons}>
<ButtonPrimary
width={188}
width={200}
style={styles.gameButton}
styleTxt={styles.styleTxtBtn}
animationStyle={animBorderStyles[0]}
animationStyle={animStyle[0]}
animation={startAnimationsBorder[0]}
isFocused={isFocused}
onPress={onChooseTruth}>
{t('buttonsTranslation.truth')}
</ButtonPrimary>
<ButtonPrimary
width={188}
width={200}
style={styles.randomButton}
styleTxt={{ fontFamily: Font.Roboto400 }}
animationStyle={animBorderStyles[1]}
animationStyle={animStyle[1]}
animation={startAnimationsBorder[1]}
isFocused={isFocused}
onPress={randomGame}>
{t('buttonsTranslation.random')}
</ButtonPrimary>
<ButtonPrimary
width={188}
width={200}
style={styles.gameButton}
styleTxt={styles.styleTxtBtn}
animation={startAnimationsBorder[2]}
animationStyle={animBorderStyles[2]}
animationStyle={animStyle[2]}
isFocused={isFocused}
onPress={onChooseDare}>
{t('buttonsTranslation.dare')}
@ -97,24 +122,20 @@ const styles = StyleSheet.create({ @@ -97,24 +122,20 @@ const styles = StyleSheet.create({
wrapButtons: {
alignItems: 'center',
justifyContent: 'center',
rowGap: 40,
marginTop: '50%',
rowGap: 20,
marginTop: '45%',
},
gameButton: {
height: 66,
width: 188,
borderRadius: 40,
backgroundColor: colors.primaryColor,
borderWidth: 2,
borderColor: colors.darkPurple,
height: 66,
},
randomButton: {
height: 66,
width: 188,
borderRadius: 40,
backgroundColor: 'transparent',
borderWidth: 2,
borderColor: colors.darkPurple,
height: 66,
},
styleTxtBtn: {
color: colors.red,

1
src/module/game/screens/index.ts

@ -1,2 +1,3 @@ @@ -1,2 +1,3 @@
export * from './game.screen'
export * from './truth-or-dare.screen'
export * from './players.screen';

124
src/module/game/screens/players.screen.tsx

@ -0,0 +1,124 @@ @@ -0,0 +1,124 @@
import React, { FC } from 'react'
import { StyleSheet, View } from 'react-native'
import {
ButtonPrimary,
colors,
Font,
Header,
UserRouteKey,
ScreenLayout,
useNav,
ButtonWithIcon,
appEvents,
StorageKey,
useAppDispatch,
} from '../../common'
import { useTranslation } from 'react-i18next'
import { PlayerField } from '../components'
import AsyncStorage from '@react-native-async-storage/async-storage'
import { selectPlayers, setPlayers } from '~store/slices'
import { useSelector } from 'react-redux'
export const PlayersScreen: FC = () => {
const { t } = useTranslation()
const nav = useNav()
const dispatch = useAppDispatch()
const players = useSelector(selectPlayers)
const onAddField = () => {
dispatch(setPlayers([...players, '']))
}
const onChangePlayer = (index: number, value: string) => {
const updatedPlayers = [...players]
updatedPlayers[index] = value
dispatch(setPlayers(updatedPlayers))
}
const onDeletePlayer = (index: number) => {
const updatedPlayers = [...players]
if (updatedPlayers.length === 1) return
updatedPlayers.splice(index, 1)
dispatch(setPlayers(updatedPlayers))
}
const checkIsFullName = () => {
return players.some(playerName => !playerName)
}
const onSavePlayers = async () => {
const hasEmptyName = checkIsFullName()
if (hasEmptyName) {
return appEvents.emit('alert', {
title: 'Oops',
subtitle:
'It seems that you did not fill in all the players name. Please fill it.',
})
}
await AsyncStorage.setItem(StorageKey.Players, JSON.stringify(players))
nav.navigate(UserRouteKey.Packages)
}
return (
<ScreenLayout
needScroll
scrollStyle={{
flex: 1,
paddingBottom: 40,
}}
headerComponent={
<Header
title={t('pageTitles.players')}
onPressLeft={onSavePlayers}
/>
}>
<View style={{ rowGap: 16 }}>
{players.map((player, index) => (
<PlayerField
key={index}
value={player}
onChange={value => onChangePlayer(index, value)}
onDelete={() => onDeletePlayer(index)}
/>
))}
</View>
<ButtonWithIcon
styleBtn={styles.plusBtn}
iconName="add-plus"
onPress={onAddField}
/>
<ButtonPrimary
iconName="play"
onPress={onSavePlayers}
style={styles.playBtn}>
{t('customPack.play')}
</ButtonPrimary>
</ScreenLayout>
)
}
const styles = StyleSheet.create({
playerName: {
fontFamily: Font.Roboto700,
textAlign: 'center',
marginBottom: 122,
},
plusBtn: {
marginTop: 24,
borderRadius: 50,
width: 50,
height: 50,
backgroundColor: colors.purple,
marginLeft: 'auto',
},
playBtn: {
marginTop: 'auto',
},
})

28
src/module/game/screens/truth-or-dare.screen.tsx

@ -1,50 +1,52 @@ @@ -1,50 +1,52 @@
import React, { useRef } from 'react'
import { Animated, StyleSheet, View } from 'react-native'
import React, { useEffect } from 'react'
import { StyleSheet, View } from 'react-native'
import { useRoute } from '@react-navigation/native'
import {
ButtonWithIcon,
Header,
RouteKey,
UserRouteKey,
ScreenLayout,
TypeCustom,
useAppDispatch,
useAppSelector,
useNav,
} from '../../common'
import {
nextStep,
onNextPlayer,
resetSteps,
selectShuffled,
selectShuffledCustom,
shuffleCustom,
shuffleItems,
} from '../../../store/slices'
import { TruthOrDareView } from '../components'
import { useAnimationIconsButton } from '../animations'
import { getCurrentTruthOrDare } from '../helper'
import { PlayerName } from '../components/player-name.component'
interface IRouteParams {
packageName?: string
isTruth?: boolean
customType?: TypeCustom
playerName?: string
}
export const TruthOrDareScreen: React.FC = () => {
const nav = useNav()
const dispatch = useAppDispatch()
const { params } = useRoute()
const { packageName, isTruth, customType }: IRouteParams = params
const { packageName, isTruth, customType, playerName }: IRouteParams =
params
const truthOrDareItems = getCurrentTruthOrDare({ isTruth, customType })
const { animRotate, animScale } = useAnimationIconsButton()
const goBack = () => {
nav.navigate(RouteKey.Game, { packageName })
const onNext = () => {
nav.navigate(UserRouteKey.Game, { packageName })
dispatch(onNextPlayer())
}
const onNext = () => {
useEffect(() => {
dispatch(nextStep())
}
}, [])
const refreshList = () => {
!customType && dispatch(shuffleItems())
@ -57,11 +59,13 @@ export const TruthOrDareScreen: React.FC = () => { @@ -57,11 +59,13 @@ export const TruthOrDareScreen: React.FC = () => {
headerComponent={
<Header
gamer
onPressLeft={() => goBack()}
onPressLeft={() => onNext()}
title={packageName}
/>
}>
<View style={{ flex: 1 }}>
<PlayerName playerName={playerName} />
<TruthOrDareView
items={truthOrDareItems}
isCustom={customType}

4
src/module/packages/animation/use-animation-list.hook.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import React, { useEffect, useRef } from 'react'
import { Animated, Easing } from 'react-native'
import { Animated } from 'react-native'
import { useIsFocused } from '@react-navigation/native'
export const useAnimationList = (delay: number) => {
@ -22,14 +22,12 @@ export const useAnimationList = (delay: number) => { @@ -22,14 +22,12 @@ export const useAnimationList = (delay: number) => {
duration: 2000,
delay,
useNativeDriver: true,
easing: Easing.linear,
}),
Animated.timing(animTransformY, {
toValue: 0,
duration: 2000,
delay,
useNativeDriver: true,
easing: Easing.linear,
}),
]),
),

12
src/module/packages/atoms/animated-diamond-icon.atom.tsx

@ -11,13 +11,11 @@ export const AnimatedDiamondIcon: FC<IProps> = ({}) => { @@ -11,13 +11,11 @@ export const AnimatedDiamondIcon: FC<IProps> = ({}) => {
useEffect(() => {
const loopAnimation = Animated.loop(
Animated.sequence([
Animated.spring(rotateIcon, {
toValue: 1,
useNativeDriver: true,
stiffness: 5,
}),
]),
Animated.spring(rotateIcon, {
toValue: 1,
useNativeDriver: true,
stiffness: 5,
}),
)
if (!isFocus) {

4
src/module/packages/atoms/create-custom-package.atom.tsx

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import React, { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { Animated, StyleSheet, TouchableOpacity, View } from 'react-native'
import { colors, Font, RouteKey, Txt, useNav } from '../../common'
import { colors, Font, UserRouteKey, Txt, useNav } from '../../common'
import { AnimatedDiamondIcon } from './animated-diamond-icon.atom'
import { useAnimationCustomItem } from '../animation'
@ -14,7 +14,7 @@ export const CustomPackage: FC<IProps> = ({}) => { @@ -14,7 +14,7 @@ export const CustomPackage: FC<IProps> = ({}) => {
const onPressCustomPackage = () => {
startAnimation()
nav.navigate(RouteKey.CustomPackage)
nav.navigate(UserRouteKey.CustomPackage)
}
return (

4
src/module/packages/components/packages-item.component.tsx

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import React, { FC } from 'react'
import { Animated, StyleSheet, View } from 'react-native'
import { ButtonWithIcon, RouteKey, Txt, colors, useNav } from '../../common'
import { ButtonWithIcon, UserRouteKey, Txt, colors, useNav } from '../../common'
import { useAnimationList } from '../animation'
interface IType {
@ -26,7 +26,7 @@ export const PackageItem: FC<IPackage> = ({ @@ -26,7 +26,7 @@ export const PackageItem: FC<IPackage> = ({
const { animationStyleItem } = useAnimationList(delay)
const onPlay = () => {
nav.navigate(RouteKey.Game, { packageName })
nav.navigate(UserRouteKey.Game, { packageName })
}
return (

13
src/module/packages/screens/packages-list.screen.tsx

@ -1,11 +1,5 @@ @@ -1,11 +1,5 @@
import React, { FC } from 'react'
import {
Header,
ProductsEnum,
RouteKey,
ScreenLayout,
useNav,
} from '../../common'
import { Header, UserRouteKey, ScreenLayout, useNav } from '../../common'
import { StyleSheet, View } from 'react-native'
import { useTranslation } from 'react-i18next'
import { packageListConfig } from '../config'
@ -24,9 +18,10 @@ export const PackagesListScreen: FC = () => { @@ -24,9 +18,10 @@ export const PackagesListScreen: FC = () => {
needScroll
headerComponent={
<Header
leftIcon=""
leftIcon="settings"
rightIcon="setting"
onPressRight={() => nav.navigate(RouteKey.Settings)}
onPressLeft={() => nav.navigate(UserRouteKey.Players)}
onPressRight={() => nav.navigate(UserRouteKey.Settings)}
/>
}>
<View style={styles.container}>

10
src/module/root/atoms/on-boarding-button.component.tsx

@ -2,7 +2,9 @@ import AsyncStorage from '@react-native-async-storage/async-storage' @@ -2,7 +2,9 @@ import AsyncStorage from '@react-native-async-storage/async-storage'
import React, { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { StyleSheet, TouchableOpacity, View } from 'react-native'
import { ButtonPrimary, RouteKey, StorageKey, Txt, useNav } from '../../common'
import { ButtonPrimary, StorageKey, Txt, NavGroup } from '../../common'
import { useDispatch } from 'react-redux'
import { setNavGroup } from '~store/slices'
interface IProps {
isLastBlock: boolean
@ -10,12 +12,12 @@ interface IProps { @@ -10,12 +12,12 @@ interface IProps {
}
export const OnBoardingBottom: FC<IProps> = ({ isLastBlock, onPressSkip }) => {
const nav = useNav()
const { t } = useTranslation()
const dispatch = useDispatch()
const onBoardFinish = async () => {
await AsyncStorage.setItem(StorageKey.OnBoarding, 'true')
nav.navigate(RouteKey.Packages)
await AsyncStorage.setItem(StorageKey.FinishOnBoarding, 'true')
dispatch(setNavGroup(NavGroup.User))
}
return (

14
src/module/root/index.tsx

@ -1,16 +1,24 @@ @@ -1,16 +1,24 @@
import React, { FC, useEffect, useMemo } from 'react'
import { AlertConfirmWidget, AlertWidget, gcService } from '../common'
import { AlertConfirmWidget, AlertWidget, NavGroup, gcService } from '../common'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { UserNavigationGroup } from './navigations-groups'
import { GuestNavigationGroup, UserNavigationGroup } from './navigations-groups'
import { useSelector } from 'react-redux'
import { selectNavGroup } from '~store/slices'
export const Root: FC = () => {
const insets = useSafeAreaInsets()
const activeGroup = useSelector(selectNavGroup)
const navGroups = {
[NavGroup.Guest]: <GuestNavigationGroup />,
[NavGroup.User]: <UserNavigationGroup />,
}
useEffect(() => {
gcService.set('insetsTop', insets.top)
}, [insets.top])
const navigation = useMemo(() => <UserNavigationGroup />, [])
const navigation = useMemo(() => navGroups[activeGroup], [activeGroup])
return (
<>

1
src/module/root/navigations-groups/index.tsx

@ -1 +1,2 @@ @@ -1 +1,2 @@
export * from './user.group';
export * from './quest.group';

34
src/module/root/navigations-groups/quest.group.tsx

@ -0,0 +1,34 @@ @@ -0,0 +1,34 @@
import React, { FC } from 'react'
import { createNativeStackNavigator } from '@react-navigation/native-stack'
import { Platform } from 'react-native'
import { GuestRouteKey } from '~module/common'
import { LanguageSelectScreen, OnboardingScreen } from '../screens'
import { LoadingScreen } from '../screens/loading-screen'
const GuestStack = createNativeStackNavigator()
export const GuestNavigationGroup: FC = () => {
return (
<GuestStack.Navigator
screenOptions={{
headerShown: false,
animation: Platform.select({
android: 'slide_from_right',
}),
}}
initialRouteName={GuestRouteKey.Loading}>
<GuestStack.Screen
name={GuestRouteKey.Loading}
component={LoadingScreen}
/>
<GuestStack.Screen
name={GuestRouteKey.LanguageSelect}
component={LanguageSelectScreen}
/>
<GuestStack.Screen
name={GuestRouteKey.Onboarding}
component={OnboardingScreen}
/>
</GuestStack.Navigator>
)
}

74
src/module/root/navigations-groups/user.group.tsx

@ -1,17 +1,11 @@ @@ -1,17 +1,11 @@
import React, { FC } from 'react'
import { createNativeStackNavigator } from '@react-navigation/native-stack'
import { RouteKey } from '../../common'
import { GameScreen, TruthOrDareScreen } from '../../game'
import { GameScreen, PlayersScreen, TruthOrDareScreen } from '../../game'
import { PackagesListScreen } from '../../packages'
import {
LanguageSelectScreen,
OnboardingScreen,
SettingsScreen,
} from '../screens'
import { LoadingScreen } from '../screens/loading-screen'
import {
PrivacyPolicyScreen,
PurchasesScreen,
SettingsScreen,
WriteToUsScreen,
} from '../../settings'
import {
@ -19,66 +13,66 @@ import { @@ -19,66 +13,66 @@ import {
CustomPackagePreviewScreen,
} from '~module/custom-package'
import { Platform } from 'react-native'
import { UserRouteKey } from '~module/common'
const Stack = createNativeStackNavigator()
const SettingsStack = createNativeStackNavigator()
const UserStack = createNativeStackNavigator()
export const UserNavigationGroup: FC = () => {
return (
<Stack.Navigator
<UserStack.Navigator
screenOptions={{
headerShown: false,
animation: Platform.select({
android: 'slide_from_right',
}),
}}
initialRouteName={RouteKey.Loading}>
<Stack.Screen
name={RouteKey.TruthOrDare}
component={TruthOrDareScreen}
/>
<Stack.Screen
name={RouteKey.Packages}
initialRouteName={UserRouteKey.Players}>
<UserStack.Screen
name={UserRouteKey.Packages}
component={PackagesListScreen}
/>
<Stack.Screen name={RouteKey.Game} component={GameScreen} />
<Stack.Screen
name={RouteKey.Onboarding}
component={OnboardingScreen}
<UserStack.Screen
name={UserRouteKey.TruthOrDare}
component={TruthOrDareScreen}
/>
<Stack.Screen name={RouteKey.Loading} component={LoadingScreen} />
<Stack.Screen
name={RouteKey.LanguageSelect}
component={LanguageSelectScreen}
<UserStack.Screen
name={UserRouteKey.Players}
component={PlayersScreen}
options={{ animation: 'fade_from_bottom' }}
/>
<Stack.Screen
name={RouteKey.CustomPackage}
<UserStack.Screen name={UserRouteKey.Game} component={GameScreen} />
<UserStack.Screen
name={UserRouteKey.CustomPackage}
component={CustomPackagePreviewScreen}
options={{ animation: 'fade_from_bottom' }}
/>
<Stack.Screen
name={RouteKey.CustomEditor}
<UserStack.Screen
name={UserRouteKey.CustomEditor}
component={CustomPackageEditorScreen}
/>
<SettingsStack.Screen
name={RouteKey.Settings}
<UserStack.Screen
name={UserRouteKey.Settings}
component={SettingsScreen}
/>
<Stack.Screen
name={RouteKey.PrivacyPolicy}
<UserStack.Screen
name={UserRouteKey.PrivacyPolicy}
component={PrivacyPolicyScreen}
/>
<Stack.Screen
name={RouteKey.Purchases}
<UserStack.Screen
name={UserRouteKey.Purchases}
component={PurchasesScreen}
/>
<Stack.Screen
name={RouteKey.WriteToUs}
<UserStack.Screen
name={UserRouteKey.WriteToUs}
component={WriteToUsScreen}
/>
</Stack.Navigator>
</UserStack.Navigator>
)
}

2
src/module/root/screens/index.tsx

@ -1,3 +1,3 @@ @@ -1,3 +1,3 @@
export * from './language-select.screen'
export * from './on-boarding.screen'
export * from '../../settings/screens/settings.screen'
export * from './loading-screen';

4
src/module/root/screens/language-select.screen.tsx

@ -3,7 +3,6 @@ import React, { FC } from 'react' @@ -3,7 +3,6 @@ import React, { FC } from 'react'
import { useTranslation } from 'react-i18next'
import { StyleSheet, View } from 'react-native'
import {
RouteKey,
colors,
ScreenLayout,
useNav,
@ -13,6 +12,7 @@ import { @@ -13,6 +12,7 @@ import {
EngSvg,
UaSvg,
Font,
GuestRouteKey,
} from '../../common'
import { LanguageItem } from '../components'
@ -33,7 +33,7 @@ export const LanguageSelectScreen: FC = () => { @@ -33,7 +33,7 @@ export const LanguageSelectScreen: FC = () => {
const nav = useNav()
const onSelectLanguage = async (language: Language) => {
nav.navigate(RouteKey.Onboarding)
nav.navigate(GuestRouteKey.Onboarding)
i18n.changeLanguage(language)
await AsyncStorage.setItem(StorageKey.Language, language)
}

32
src/module/root/screens/loading-screen.tsx

@ -2,37 +2,44 @@ import { ActivityIndicator, StyleSheet, View } from 'react-native' @@ -2,37 +2,44 @@ import { ActivityIndicator, StyleSheet, View } from 'react-native'
import React, { FC, useEffect } from 'react'
import {
ScreenLayout,
RouteKey,
useNav,
useAppDispatch,
StorageKey,
NavGroup,
GuestRouteKey,
UserRouteKey,
} from '../../common'
import AsyncStorage from '@react-native-async-storage/async-storage'
import { useTranslation } from 'react-i18next'
import { purchasesService } from '../../settings'
import { fetchPostsAsync, getCustomPackage } from '../../../store/slices'
import {
getGameItemsFromFirestore,
getCustomPackage,
setNavGroup,
getPlayersAsync,
} from '../../../store/slices'
export const LoadingScreen: FC = () => {
const { i18n } = useTranslation()
const nav = useNav()
const dispatch = useAppDispatch()
const getLanguage = async () => {
const response = await AsyncStorage.getItem(StorageKey.Language)
return response
return await AsyncStorage.getItem(StorageKey.Language)
}
const getOnboardEnd = async () => {
const response = await AsyncStorage.getItem(StorageKey.OnBoarding)
return response
return await AsyncStorage.getItem(StorageKey.FinishOnBoarding)
}
const init = async () => {
let language = await getLanguage()
const language = await getLanguage()
const isOnBoard = await getOnboardEnd()
dispatch(fetchPostsAsync())
dispatch(getGameItemsFromFirestore())
dispatch(getCustomPackage())
dispatch(getPlayersAsync())
purchasesService.init()
if (language) {
@ -40,11 +47,11 @@ export const LoadingScreen: FC = () => { @@ -40,11 +47,11 @@ export const LoadingScreen: FC = () => {
}
if (isOnBoard && language) {
nav.navigate(RouteKey.Packages)
dispatch(setNavGroup(NavGroup.User))
} else if (language && !isOnBoard) {
nav.navigate(RouteKey.Onboarding)
nav.navigate(GuestRouteKey.Onboarding)
} else if (!language) {
nav.navigate(RouteKey.LanguageSelect)
nav.navigate(GuestRouteKey.LanguageSelect)
}
}
@ -65,5 +72,6 @@ const styles = StyleSheet.create({ @@ -65,5 +72,6 @@ const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
})

19
src/module/root/screens/on-boarding.screen.tsx

@ -1,20 +1,11 @@ @@ -1,20 +1,11 @@
import React, { FC, useState } from 'react'
import {
Font,
Header,
RouteKey,
ScreenLayout,
Txt,
colors,
useNav,
} from '../../common'
import { Font, Header, ScreenLayout, Txt, colors } from '../../common'
import { StyleSheet, View } from 'react-native'
import { onBoardingConfig } from '../config'
import { DotsAtom, OnBoardingBottom } from '../atoms'
import { useTranslation } from 'react-i18next'
export const OnboardingScreen: FC = () => {
const nav = useNav()
const { t } = useTranslation()
const [currentIndex, setCurrentIndex] = useState(0)
@ -26,14 +17,6 @@ export const OnboardingScreen: FC = () => { @@ -26,14 +17,6 @@ export const OnboardingScreen: FC = () => {
}
}
const goBack = () => {
if (currentIndex < 1) {
nav.navigate(RouteKey.LanguageSelect)
} else {
setCurrentIndex(currentIndex - 1)
}
}
const Picture = onBoardingConfig[currentIndex].image
const isLastBlock = onBoardingConfig.length - 1 === currentIndex

4
src/module/settings/screens/purchases.screen.tsx

@ -14,7 +14,7 @@ import { @@ -14,7 +14,7 @@ import {
Icon,
ModalComponent,
ProductsEnum,
RouteKey,
UserRouteKey,
ScreenLayout,
Txt,
useNav,
@ -77,7 +77,7 @@ export const PurchasesScreen: FC = () => { @@ -77,7 +77,7 @@ export const PurchasesScreen: FC = () => {
isPurchased={it.isPurchased}
onPress={() =>
it.isPurchased
? nav.navigate(RouteKey.Packages)
? nav.navigate(UserRouteKey.Packages)
: purchaseProduct(it.productId)
}
/>

13
src/module/settings/screens/settings.screen.tsx

@ -7,12 +7,14 @@ import { @@ -7,12 +7,14 @@ import {
EngSvg,
Header,
Language,
RouteKey,
UserRouteKey,
ScreenLayout,
StorageKey,
UaSvg,
useNav,
} from '../../common'
import { SheetManager } from 'react-native-actions-sheet'
import AsyncStorage from '@react-native-async-storage/async-storage'
export const SettingsScreen: FC = () => {
const { t, i18n } = useTranslation()
@ -43,8 +45,9 @@ export const SettingsScreen: FC = () => { @@ -43,8 +45,9 @@ export const SettingsScreen: FC = () => {
}
}
const onChangeLanguage = (language: Language) => {
const onChangeLanguage = async (language: Language) => {
SheetManager.hide('bottom-sheet')
await AsyncStorage.setItem(StorageKey.Language, language)
return i18n.changeLanguage(language)
}
@ -57,7 +60,7 @@ export const SettingsScreen: FC = () => { @@ -57,7 +60,7 @@ export const SettingsScreen: FC = () => {
const onPressSettingItem = (key: string) => {
switch (key) {
case 'purchases':
nav.navigate(RouteKey.Purchases)
nav.navigate(UserRouteKey.Purchases)
break
case 'lang':
openBottomSheetForChangeLanguage()
@ -65,10 +68,10 @@ export const SettingsScreen: FC = () => { @@ -65,10 +68,10 @@ export const SettingsScreen: FC = () => {
case 'notification':
break
case 'message':
nav.navigate(RouteKey.WriteToUs)
nav.navigate(UserRouteKey.WriteToUs)
break
case 'privacy-policy':
nav.navigate(RouteKey.PrivacyPolicy)
nav.navigate(UserRouteKey.PrivacyPolicy)
break
case 'rate':
break

2
src/store/slices/current-step-slice.tsx → src/store/slices/current-step.slice.ts

@ -24,6 +24,6 @@ export const CurrentStepSlice = createSlice({ @@ -24,6 +24,6 @@ export const CurrentStepSlice = createSlice({
export const { nextStep, resetSteps } = CurrentStepSlice.actions
export const getStep = (state: RootState) => state.currentStep.step
export const selectStep = (state: RootState) => state.currentStep.step
export default CurrentStepSlice.reducer

242
src/store/slices/game-items.slice.ts

@ -0,0 +1,242 @@ @@ -0,0 +1,242 @@
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import firestore from '@react-native-firebase/firestore'
import { GameItem } from '../../module/common'
import { RootState } from '../store'
import _ from 'lodash'
export interface GameItemsState {
gameItems: GameItem[]
shuffled: GameItem[]
loaded: boolean
hasError: boolean
}
const initialState: GameItemsState = {
gameItems: [],
shuffled: [],
loaded: false,
hasError: false,
}
const arr = [
{
id: 1,
isDare: false,
en: "What is the most outrageous thing you've ever done?",
ua: 'Яке найризикованіше рішення ти коли-небудь приймав?',
},
{
id: 2,
isDare: true,
en: 'Do your best dance moves for the next minute.',
ua: 'Станцюй свій найкрутіший танець протягом 20 секунд під музику.',
},
{
id: 3,
isDare: false,
en: "What's the most embarrassing thing you've Googled?",
ua: "Найсором'язливіше, що ти шукав в Google?",
},
{
id: 4,
isDare: true,
en: "Call a friend and sing 'Happy Birthday' to them, regardless of whether it's their birthday or not.",
ua: "Подзвони другові і заспівай 'З Днем Народження' незалежно від його дати народження.",
},
{
id: 5,
isDare: false,
en: "What's the most unusual food you've ever eaten?",
ua: 'Сама незвичайна їжа, яку ти коли-небудь їв(ла)?',
},
{
id: 6,
isDare: false,
en: 'If you could switch lives with someone for a day, who would it be and why?',
ua: 'Якщо б ти міг помінятися життям з кимось на один день, хто б це був і чому?',
},
{
id: 7,
isDare: true,
en: 'Take a funny selfie and post it on your social media.',
ua: 'Зроби смішне селфі і опублікуй його у своїх соціальних мережах.',
},
{
id: 8,
isDare: false,
en: "What's the most unusual food you've ever eaten?",
ua: 'Сама незвичайна їжа, яку ти коли-небудь їв(ла)?',
},
{
id: 9,
isDare: true,
en: "Send a text to your crush confessing your feelings (even if it's just for fun).",
ua: 'Напиши в особисті 10-му своєму підписнику "В мене погані новини для тебе" і не відповідай 24 години.',
},
{
id: 10,
isDare: false,
en: 'If you could have dinner with any historical figure, who would it be and what would you ask them?',
ua: 'Якщо б ви могли пообідати з будь-якою історичною постаттю, хто б це був і що ви б їх запитали?',
},
{
id: 11,
isDare: true,
en: 'Imitate a celebrity.',
ua: 'Імітуй якусь знаменитість.',
},
{
id: 12,
isDare: false,
en: 'If you had to eat one food for the rest of your life, what would it be?',
ua: 'Якщо б тобі довелося їсти одну їжу до кінця життя, що б це було?',
},
{
id: 14,
isDare: false,
en: "What's the most rebellious thing you did as a teenager?",
ua: 'Яке найбільш хуліганське ти зробив(ла) в дитинстві?',
},
{
id: 15,
isDare: true,
en: 'Create a silly rap about the person to your right.',
ua: 'Заспівай дурний реп про особу справа від тебе.',
},
{
id: 16,
isDare: false,
en: 'What is the weirdest talent you have?',
ua: 'Що ти вмієш робити найдивніше?',
},
{
id: 17,
isDare: true,
en: 'Act like a certain animal until your next turn. Others have to guess the animal.',
ua: 'Ведіть себе як собака до свого наступного ходу',
},
{
id: 18,
isDare: false,
en: 'If you could be invisible for a day, what would you do?',
ua: 'Якщо б ви могли бути невидимими на один день, що б ви зробили?',
},
{
id: 19,
isDare: true,
en: 'Send a strange sound to the third person on your social media friends list.',
ua: 'Відправ дивний звук третій людині, на яку ти підписаний(на)',
},
{
id: 20,
isDare: false,
en: 'What’s the most unusual job you can think of?',
ua: 'Найдурніша робота, яку б ти робив(ла) за хороші гроші?',
},
{
id: 21,
isDare: true,
en: 'Post something embarrassing on your social media for the next 30 minutes.',
ua: 'Добав в сторіз щось сором’язливе на 1 хв.',
},
{
id: 22,
isDare: false,
en: 'What’s the most embarrassing thing you’ve said in the heat of the moment?',
ua: 'Що ти "ляпав(ла)" не те в пориві емоцій?',
},
{
id: 23,
isDare: true,
en: 'Sing a song loudly and out of tune.',
ua: 'Співай що завгодно наступні 10 секунд',
},
{
id: 24,
isDare: false,
en: 'If you could time travel, which era would you visit and why?',
ua: 'Якщо б ти міг подорожувати в часі, яку епоху та місце ти б відвідав(ла) і чому?',
},
{
id: 25,
isDare: true,
en: 'Take a goofy selfie and set it as your profile picture for the next hour.',
ua: 'Зроби невдале селфі і постав на аву на 1 годину',
},
{
id: 26,
isDare: false,
en: 'What’s the most unusual place you’ve ever visited?',
ua: 'Яке найстрашніше місце, в якому ти був(ла)',
},
{
id: 27,
isDare: true,
en: 'Send a funny text to the fifth person in your contact list.',
ua: 'Відправ повідомлення "Я хочу зіграти з тобою в гру" 5-му в своєму списку контактів.',
},
{
id: 28,
isDare: false,
en: "What's the most daring thing you would do for a million dollars?",
ua: 'Ти б ходив ходив(ла) на протязі тижня голим(голою) на роботу за мільйон гривень?',
},
{
id: 29,
isDare: true,
en: 'What’s the most unexpected compliment you’ve ever received?',
ua: 'Який найбільш несподіваний комплімент тобі коли-небудь говорили?',
},
{
id: 30,
isDare: true,
en: 'Post something embarrassing on your social media for the next 30 minutes.',
ua: 'Добав в сторіз щось сором’язливе на 30 хв.',
},
]
export const getGameItemsFromFirestore = createAsyncThunk(
'game-items/get-from-firestore',
async () => {
const query = await firestore().collection('light').get()
const response: any = query.docs.map(doc => doc.data())
const truthsAndDares: GameItem[] = response[0]['data']
return truthsAndDares
},
)
export const postsSlice = createSlice({
name: 'gameItems',
initialState,
reducers: {
shuffleItems: state => {
state.shuffled = _.shuffle(state.gameItems)
},
},
extraReducers(builder) {
builder
.addCase(getGameItemsFromFirestore.fulfilled, (state, action) => {
state.gameItems = action.payload
state.hasError = false
state.loaded = true
})
.addCase(getGameItemsFromFirestore.pending, state => {
state.loaded = false
})
.addCase(getGameItemsFromFirestore.rejected, state => {
state.hasError = true
})
},
})
export const { shuffleItems } = postsSlice.actions
export const selectGameItems = (state: RootState) => state.gameItems.gameItems
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

8
src/store/slices/index.ts

@ -1,3 +1,5 @@ @@ -1,3 +1,5 @@
export * from './current-step-slice'
export * from './posts.slice'
export * from './custom-package.slice';
export * from './current-step.slice'
export * from './game-items.slice'
export * from './custom-package.slice'
export * from './navigation.slice'
export * from './players.slice'

27
src/store/slices/navigation.slice.ts

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
import { PayloadAction, createSlice } from '@reduxjs/toolkit'
import { RootState } from '../store'
import { NavGroup } from '~module/common'
interface NavGroupState {
navGroup: NavGroup
}
const initialState: NavGroupState = {
navGroup: NavGroup.Guest,
}
export const NavGroupSlice = createSlice({
name: 'navGroup',
initialState,
reducers: {
setNavGroup: (state, action: PayloadAction<NavGroup>) => {
state.navGroup = action.payload
},
},
})
export const { setNavGroup } = NavGroupSlice.actions
export const selectNavGroup = (state: RootState) => state.navGroup.navGroup
export default NavGroupSlice.reducer

70
src/store/slices/players.slice.ts

@ -0,0 +1,70 @@ @@ -0,0 +1,70 @@
import { PayloadAction, createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import { StorageKey } from '../../module/common'
import { RootState } from '../store'
import _ from 'lodash'
import AsyncStorage from '@react-native-async-storage/async-storage'
export interface PlayersState {
players: string[]
playerIndex: number
currentPlayer: string
loaded: boolean
hasError: boolean
}
const initialState: PlayersState = {
players: [],
playerIndex: 0,
currentPlayer: '',
loaded: false,
hasError: false,
}
export const getPlayersAsync = createAsyncThunk('get-players', async () => {
const savedPlayers = await AsyncStorage.getItem(StorageKey.Players)
return savedPlayers ? JSON.parse(savedPlayers) : ['']
})
export const playersSlice = createSlice({
name: 'players',
initialState,
reducers: {
setPlayers: (state, action: PayloadAction<string[]>) => {
state.players = action.payload
},
onNextPlayer: state => {
state.playerIndex = state.playerIndex + 1
const player = state.players[state.playerIndex]
if (!player) {
state.currentPlayer = state.players[0]
state.playerIndex = 0
return
}
state.currentPlayer = player
},
},
extraReducers(builder) {
builder
.addCase(getPlayersAsync.fulfilled, (state, action) => {
state.players = action.payload
state.currentPlayer = state.players[0]
})
.addCase(getPlayersAsync.pending, state => {
state.loaded = false
})
.addCase(getPlayersAsync.rejected, state => {
state.hasError = true
})
},
})
export const { setPlayers, onNextPlayer } = playersSlice.actions
export const selectPlayers = (state: RootState) => state.players.players
export const selectCurrentPlayer = (state: RootState) =>
state.players.currentPlayer
export default playersSlice.reducer

65
src/store/slices/posts.slice.ts

@ -1,65 +0,0 @@ @@ -1,65 +0,0 @@
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import firestore from '@react-native-firebase/firestore'
import { GameItem } from '../../module/common'
import { RootState } from '../store'
import _ from 'lodash'
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 querySnapshot = await firestore().collection('GameItems').get()
const data: GameItem[] = querySnapshot.docs.map(
doc => doc.data() 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

14
src/store/store.ts

@ -1,13 +1,17 @@ @@ -1,13 +1,17 @@
import { Action, configureStore, ThunkAction } from '@reduxjs/toolkit'
import currentStepSlice from './slices/current-step-slice'
import postsSlice from './slices/posts.slice'
import { configureStore } from '@reduxjs/toolkit'
import currentStep from './slices/current-step.slice'
import gameItems from './slices/game-items.slice'
import customPackage from './slices/custom-package.slice'
import navGroup from './slices/navigation.slice'
import players from './slices/players.slice'
export const store = configureStore({
reducer: {
currentStep: currentStepSlice,
gameItems: postsSlice,
currentStep,
gameItems,
customPackage,
navGroup,
players,
},
})

Loading…
Cancel
Save