Browse Source

Merge branch 'main' of gitlab.work-jetup.site:Vitalik/truthordare into main

main
Vitalik 10 months ago
parent
commit
bcaeadfc68
  1. 4
      android/app/_BUCK
  2. 2
      android/app/build.gradle
  3. 92
      android/app/google-services.json
  4. 2
      android/app/src/debug/java/com/truth/ReactNativeFlipper.java
  5. 2
      android/app/src/main/AndroidManifest.xml
  6. BIN
      android/app/src/main/assets/fonts/fontello.ttf
  7. 2
      android/app/src/main/java/com/truthoraction.jetup/MainActivity.java
  8. 2
      android/app/src/main/java/com/truthoraction.jetup/MainApplication.java
  9. 6
      ios/GoogleService-Info.plist
  10. 8
      ios/Podfile.lock
  11. 4
      ios/Truth.xcodeproj/project.pbxproj
  12. 11
      package-lock.json
  13. 1
      package.json
  14. BIN
      src/assets/resources/fonts/fontello.ttf
  15. 6
      src/config/fontello.json
  16. 1
      src/i18n/interfaces/common.interface.ts
  17. 1
      src/i18n/interfaces/settings.types.interface.ts
  18. 1
      src/i18n/locales/en/common.translation.ts
  19. 1
      src/i18n/locales/en/settings.translation.ts
  20. 11
      src/i18n/locales/ua/common.translation.ts
  21. 66
      src/module/common/questions-dares-list/questions-dares-list.ts
  22. 2
      src/module/common/typing/enums/products.enum.ts
  23. 2
      src/module/common/typing/enums/storage-key.enum.ts
  24. 42
      src/module/game/animations/use-animation-truth-or-dare.hook.ts
  25. 2
      src/module/game/components/index.ts
  26. 2
      src/module/game/components/player-field.component.tsx
  27. 53
      src/module/game/components/truth-or-dare-view.tsx
  28. 104
      src/module/game/components/truth-or-dare.tsx
  29. 1
      src/module/game/config/index.ts
  30. 12
      src/module/game/config/voice.config.ts
  31. 54
      src/module/game/hooks/get-current-truth-dares.hook.tsx
  32. 1
      src/module/game/hooks/index.ts
  33. 2
      src/module/game/hooks/use-set-steps-by-package.tsx
  34. 42
      src/module/game/hooks/use-voice.hook.tsx
  35. 4
      src/module/game/screens/game.screen.tsx
  36. 24
      src/module/game/screens/players.screen.tsx
  37. 24
      src/module/game/screens/truth-or-dare.screen.tsx
  38. 9
      src/module/packages/animation/use-animation-list.hook.ts
  39. 17
      src/module/packages/atoms/packages-page-separator.atom.tsx
  40. 2
      src/module/packages/config/packages-list.config.tsx
  41. 1
      src/module/root/navigations-groups/user.group.tsx
  42. 9
      src/module/root/screens/loading-screen.tsx
  43. 1
      src/module/settings/atoms/index.ts
  44. 2
      src/module/settings/atoms/switch-notifications.atom.tsx
  45. 33
      src/module/settings/atoms/switch-voiceover.atom.tsx
  46. 11
      src/module/settings/config/settings.config.tsx
  47. 2
      src/module/settings/screens/settings.screen.tsx
  48. 10
      src/store/slices/custom-package.slice.ts
  49. 29
      src/store/slices/game-items.slice.ts
  50. 21
      src/store/slices/players.slice.ts

4
android/app/_BUCK

@ -35,12 +35,12 @@ android_library(
android_build_config( android_build_config(
name = "build_config", name = "build_config",
package = "com.truth", package = "com.truthoraction.jetup",
) )
android_resource( android_resource(
name = "res", name = "res",
package = "com.truth", package = "com.truthoraction.jetup",
res = "src/main/res", res = "src/main/res",
) )

2
android/app/build.gradle

@ -29,7 +29,7 @@ android {
compileSdkVersion rootProject.ext.compileSdkVersion compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig { defaultConfig {
applicationId "com.truth" applicationId "com.truthoraction.jetup"
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1 versionCode 1

92
android/app/google-services.json

@ -1,47 +1,47 @@
{ {
"project_info": { "project_info": {
"project_number": "552568521005", "project_number": "552568521005",
"firebase_url": "https://truthordare-6493e-default-rtdb.europe-west1.firebasedatabase.app", "firebase_url": "https://truthordare-6493e-default-rtdb.europe-west1.firebasedatabase.app",
"project_id": "truth-or-dare-fcc54", "project_id": "truth-or-dare-fcc54",
"storage_bucket": "truthordare-6493e.appspot.com" "storage_bucket": "truthordare-6493e.appspot.com"
}, },
"client": [ "client": [
{ {
"client_info": { "client_info": {
"mobilesdk_app_id": "1:180425292880:android:8690e5274abb413c7da555", "mobilesdk_app_id": "1:180425292880:android:8690e5274abb413c7da555",
"android_client_info": { "android_client_info": {
"package_name": "com.truth" "package_name": "com.truthoraction.jetup"
} }
}, },
"oauth_client": [ "oauth_client": [
{ {
"client_id": "180425292880-1pfj1drhgqc9j19jit1ahfu7jouodjej.apps.googleusercontent.com", "client_id": "180425292880-1pfj1drhgqc9j19jit1ahfu7jouodjej.apps.googleusercontent.com",
"client_type": 3 "client_type": 3
} }
], ],
"api_key": [ "api_key": [
{ {
"current_key": "AIzaSyBXoidUFjbE84RZ8PqIP0IGAsTYU28PEO8" "current_key": "AIzaSyBXoidUFjbE84RZ8PqIP0IGAsTYU28PEO8"
} }
], ],
"services": { "services": {
"appinvite_service": { "appinvite_service": {
"other_platform_oauth_client": [ "other_platform_oauth_client": [
{ {
"client_id": "180425292880-1pfj1drhgqc9j19jit1ahfu7jouodjej.apps.googleusercontent.com", "client_id": "180425292880-1pfj1drhgqc9j19jit1ahfu7jouodjej.apps.googleusercontent.com",
"client_type": 3 "client_type": 3
}, },
{ {
"client_id": "180425292880-bfueoq3p0oq0b0lf2qviebio2q338eu8.apps.googleusercontent.com", "client_id": "180425292880-bfueoq3p0oq0b0lf2qviebio2q338eu8.apps.googleusercontent.com",
"client_type": 2, "client_type": 2,
"ios_info": { "ios_info": {
"bundle_id": "org.reactjs.native.example.Truth" "bundle_id": "org.reactjs.native.example.Truth"
} }
} }
] ]
} }
} }
} }
], ],
"configuration_version": "1" "configuration_version": "1"
} }

2
android/app/src/debug/java/com/truth/ReactNativeFlipper.java

@ -4,7 +4,7 @@
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root * <p>This source code is licensed under the MIT license found in the LICENSE file in the root
* directory of this source tree. * directory of this source tree.
*/ */
package com.truth; package com.truthoraction.jetup;
import android.content.Context; import android.content.Context;
import com.facebook.flipper.android.AndroidFlipperClient; import com.facebook.flipper.android.AndroidFlipperClient;

2
android/app/src/main/AndroidManifest.xml

@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.truth"> package="com.truthoraction.jetup">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />

BIN
android/app/src/main/assets/fonts/fontello.ttf

Binary file not shown.

2
android/app/src/main/java/com/truth/MainActivity.java → android/app/src/main/java/com/truthoraction.jetup/MainActivity.java

@ -1,4 +1,4 @@
package com.truth; package com.truthoraction.jetup;
import android.os.Bundle; import android.os.Bundle;
import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivity;

2
android/app/src/main/java/com/truth/MainApplication.java → android/app/src/main/java/com/truthoraction.jetup/MainApplication.java

@ -1,4 +1,4 @@
package com.truth; package com.truthoraction.jetup;
import android.app.Application; import android.app.Application;
import com.facebook.react.PackageList; import com.facebook.react.PackageList;

6
ios/GoogleService-Info.plist

@ -9,13 +9,15 @@
<key>PLIST_VERSION</key> <key>PLIST_VERSION</key>
<string>1</string> <string>1</string>
<key>BUNDLE_ID</key> <key>BUNDLE_ID</key>
<string>com.truth</string> <string>com.truthoraction.jetup</string>
<key>PROJECT_ID</key> <key>PROJECT_ID</key>
<string>truth-or-dare-fcc54</string> <string>truth-or-dare-fcc54</string>
<key>STORAGE_BUCKET</key> <key>STORAGE_BUCKET</key>
<string>truth-or-dare-fcc54.appspot.com</string> <string>truth-or-dare-fcc54.appspot.com</string>
<key>IS_ADS_ENABLED</key> <key>IS_ADS_ENABLED</key>
<false></false> <false></false>
<key>ITSAppUsesNonExemptEncryption</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key> <key>IS_ANALYTICS_ENABLED</key>
<false></false> <false></false>
<key>IS_APPINVITE_ENABLED</key> <key>IS_APPINVITE_ENABLED</key>
@ -25,6 +27,6 @@
<key>IS_SIGNIN_ENABLED</key> <key>IS_SIGNIN_ENABLED</key>
<true></true> <true></true>
<key>GOOGLE_APP_ID</key> <key>GOOGLE_APP_ID</key>
<string>1:552568521005:ios:66c16d5229fe90142353a4</string> <string>1:552568521005:ios:05030eb25d2be7bf2353a4</string>
</dict> </dict>
</plist> </plist>

8
ios/Podfile.lock

@ -1286,6 +1286,8 @@ PODS:
- RNVectorIcons (9.2.0): - RNVectorIcons (9.2.0):
- React-Core - React-Core
- SocketRocket (0.6.1) - SocketRocket (0.6.1)
- TextToSpeech (4.1.0):
- React
- Yoga (1.14.0) - Yoga (1.14.0)
- YogaKit (1.18.1): - YogaKit (1.18.1):
- Yoga (~> 1.14) - Yoga (~> 1.14)
@ -1368,6 +1370,7 @@ DEPENDENCIES:
- RNScreens (from `../node_modules/react-native-screens`) - RNScreens (from `../node_modules/react-native-screens`)
- RNSVG (from `../node_modules/react-native-svg`) - RNSVG (from `../node_modules/react-native-svg`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`) - RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- TextToSpeech (from `../node_modules/react-native-tts`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS: SPEC REPOS:
@ -1503,6 +1506,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-svg" :path: "../node_modules/react-native-svg"
RNVectorIcons: RNVectorIcons:
:path: "../node_modules/react-native-vector-icons" :path: "../node_modules/react-native-vector-icons"
TextToSpeech:
:path: "../node_modules/react-native-tts"
Yoga: Yoga:
:path: "../node_modules/react-native/ReactCommon/yoga" :path: "../node_modules/react-native/ReactCommon/yoga"
@ -1585,9 +1590,10 @@ SPEC CHECKSUMS:
RNSVG: d7d7bc8229af3842c9cfc3a723c815a52cdd1105 RNSVG: d7d7bc8229af3842c9cfc3a723c815a52cdd1105
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8 RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17
TextToSpeech: b3aa777ff5585705f179c0a2436bfd0926d1716e
Yoga: 4c3aa327e4a6a23eeacd71f61c81df1bcdf677d5 Yoga: 4c3aa327e4a6a23eeacd71f61c81df1bcdf677d5
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
PODFILE CHECKSUM: 0c63220a19ca9fd3d2ce02dc5eecdb8c820bf5ab PODFILE CHECKSUM: 0c63220a19ca9fd3d2ce02dc5eecdb8c820bf5ab
COCOAPODS: 1.11.3 COCOAPODS: 1.12.1

4
ios/Truth.xcodeproj/project.pbxproj

@ -549,7 +549,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 3; CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 7LY53JU2YB; DEVELOPMENT_TEAM = 7LY53JU2YB;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
@ -618,7 +618,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 3; CURRENT_PROJECT_VERSION = 5;
DEVELOPMENT_TEAM = 7LY53JU2YB; DEVELOPMENT_TEAM = 7LY53JU2YB;
HEADER_SEARCH_PATHS = ( HEADER_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",

11
package-lock.json generated

@ -36,6 +36,7 @@
"react-native-splash-screen": "^3.3.0", "react-native-splash-screen": "^3.3.0",
"react-native-svg": "^12.5.1", "react-native-svg": "^12.5.1",
"react-native-svg-transformer": "^1.0.0", "react-native-svg-transformer": "^1.0.0",
"react-native-tts": "^4.1.0",
"react-native-vector-icons": "^9.2.0", "react-native-vector-icons": "^9.2.0",
"react-redux": "^9.0.4", "react-redux": "^9.0.4",
"validate.js": "^0.13.1" "validate.js": "^0.13.1"
@ -13472,6 +13473,11 @@
"react-native-svg": ">=12.0.0" "react-native-svg": ">=12.0.0"
} }
}, },
"node_modules/react-native-tts": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/react-native-tts/-/react-native-tts-4.1.0.tgz",
"integrity": "sha512-tvf3lQ6u9MqztUie37qoPw5YQPqi0ql1lPhDsBBs/RRA6A/H1J9X9H/qb1A0Hx0ZpjavrEdtVSqQQ2JDZvZCTQ=="
},
"node_modules/react-native-vector-icons": { "node_modules/react-native-vector-icons": {
"version": "9.2.0", "version": "9.2.0",
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-9.2.0.tgz", "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-9.2.0.tgz",
@ -25421,6 +25427,11 @@
"path-dirname": "^1.0.2" "path-dirname": "^1.0.2"
} }
}, },
"react-native-tts": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/react-native-tts/-/react-native-tts-4.1.0.tgz",
"integrity": "sha512-tvf3lQ6u9MqztUie37qoPw5YQPqi0ql1lPhDsBBs/RRA6A/H1J9X9H/qb1A0Hx0ZpjavrEdtVSqQQ2JDZvZCTQ=="
},
"react-native-vector-icons": { "react-native-vector-icons": {
"version": "9.2.0", "version": "9.2.0",
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-9.2.0.tgz", "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-9.2.0.tgz",

1
package.json

@ -44,6 +44,7 @@
"react-native-splash-screen": "^3.3.0", "react-native-splash-screen": "^3.3.0",
"react-native-svg": "^12.5.1", "react-native-svg": "^12.5.1",
"react-native-svg-transformer": "^1.0.0", "react-native-svg-transformer": "^1.0.0",
"react-native-tts": "^4.1.0",
"react-native-vector-icons": "^9.2.0", "react-native-vector-icons": "^9.2.0",
"react-redux": "^9.0.4", "react-redux": "^9.0.4",
"validate.js": "^0.13.1" "validate.js": "^0.13.1"

BIN
src/assets/resources/fonts/fontello.ttf

Binary file not shown.

6
src/config/fontello.json

@ -293,6 +293,12 @@
"width": 1000 "width": 1000
}, },
"search": ["clear"] "search": ["clear"]
},
{
"uid": "572c9ded6a688698dc275b30ff30fefa",
"css": "music",
"code": 59406,
"src": "linecons"
} }
] ]
} }

1
src/i18n/interfaces/common.interface.ts

@ -14,4 +14,5 @@ export interface Common {
limitDesc: string limitDesc: string
buyAfterBtn: string buyAfterBtn: string
no: string no: string
placeholderPlayer: string
} }

1
src/i18n/interfaces/settings.types.interface.ts

@ -8,5 +8,6 @@ export namespace SettingLocale {
share: string share: string
policy: string policy: string
label: string label: string
voiceover: string
} }
} }

1
src/i18n/locales/en/common.translation.ts

@ -20,4 +20,5 @@ export const common: Common = {
'You have reached the limit of questions for this set. You need the full version to continue, but you can use “Under18” and “Light” packages. They are always open for you.', 'You have reached the limit of questions for this set. You need the full version to continue, but you can use “Under18” and “Light” packages. They are always open for you.',
buyAfterBtn: 'Buy', buyAfterBtn: 'Buy',
no: 'Back', no: 'Back',
placeholderPlayer: 'Name'
} }

1
src/i18n/locales/en/settings.translation.ts

@ -9,4 +9,5 @@ export const settingTranslation: SettingLocale.Core = {
share: 'Share app', share: 'Share app',
policy: 'Privacy policy', policy: 'Privacy policy',
label: 'What can we do to help?', label: 'What can we do to help?',
voiceover: 'Voiceover',
} }

11
src/i18n/locales/ua/common.translation.ts

@ -7,14 +7,17 @@ const Validation = {
export const common: Common = { export const common: Common = {
validate: Validation, validate: Validation,
shareMessage: 'Поділіться цим додатком зі своїми друзями', shareMessage: 'Поділись цим додатком зі своїми друзями',
notFillPlayerTitle: 'Ой', notFillPlayerTitle: 'Ой',
notFillPlayerMessage: 'Якесь поле не заповнене. Перевір будь ласка!', notFillPlayerMessage:
'Здається не всі імена заповнені. Перевір будь ласка!',
helpAlertTitle: 'Добре!', helpAlertTitle: 'Добре!',
helpAlertDesc: 'Ми розглянемо питання і якнайшвидше дамо відповідь.', helpAlertDesc: 'Ми розглянемо питання і якнайшвидше дамо відповідь.',
writeUsBtn: 'Відправити', writeUsBtn: 'Відправити',
limitTitle: 'Ліміт вичерпано', limitTitle: 'Ліміт вичерпано',
limitDesc: 'Ліміт питань для пакету "Crazy" вичерпано. Щоб продовжити, потрібна повна версія. Але ти можеш використовувати пакети «До 18» та «Легкий». Вони завжди відкриті для тебе.', limitDesc:
'Ліміт питань для пакету "Crazy" вичерпано. Щоб продовжити, потрібна повна версія. Але ти можеш використовувати пакети «До 18» та «Легкий». Вони завжди відкриті для тебе.',
buyAfterBtn: 'До покупок', buyAfterBtn: 'До покупок',
no: 'Назад' no: 'Назад',
placeholderPlayer: "Ім'я",
} }

66
src/module/common/questions-dares-list/questions-dares-list.ts

@ -2,8 +2,8 @@ export const under18 = [
{ {
id: 1, id: 1,
isDare: false, isDare: false,
en: 'Tell about your first fight?', en: 'Tell about your first fight.',
ua: 'Розкажи про свою першу бійку?', ua: 'Розкажи про свою першу бійку.',
}, },
{ {
id: 2, id: 2,
@ -50,14 +50,14 @@ export const under18 = [
{ {
id: 9, id: 9,
isDare: false, isDare: false,
en: 'Name a song that you are uncomfortable listening to in front of others', en: 'Name a song that you are uncomfortable listening to in front of others.',
ua: 'Назви пісню, яку тобі некомфортно слухати при інших', ua: 'Назви пісню, яку тобі некомфортно слухати при інших.',
}, },
{ {
id: 10, id: 10,
isDare: true, isDare: true,
en: "Write 'Where's my beer' to your president on the social network ", en: "Write 'Where's my beer' to your president on the social network.",
ua: 'Напиши "Де моє пиво" своєму президентові в соціальній мережі', ua: 'Напиши "Де моє пиво" своєму президентові в соціальній мережі.',
}, },
{ {
id: 11, id: 11,
@ -68,8 +68,8 @@ export const under18 = [
{ {
id: 12, id: 12,
isDare: true, isDare: true,
en: 'Call someone on video, be silent for 10 seconds and knock out', en: 'Call someone on video, be silent for 10 seconds and knock out.',
ua: 'Подзвони комусь по відео мовчи 10 секунд і вибий', ua: 'Подзвони комусь по відео мовчи 10 секунд і вибий.',
}, },
{ {
id: 13, id: 13,
@ -104,8 +104,8 @@ export const under18 = [
{ {
id: 18, id: 18,
isDare: true, isDare: true,
en: 'Send a heartfelt message to the fifth person in your contact list, expressing gratitude for something', en: 'Send a heartfelt message to the fifth person in your contact list, expressing gratitude for something.',
ua: "Відправ сердечне повідомлення п'ятій людині у своєму списку контактів, висловлюючи вдячність за щось", ua: "Відправ сердечне повідомлення п'ятій людині у своєму списку контактів, висловлюючи вдячність за щось.",
}, },
{ {
id: 19, id: 19,
@ -141,7 +141,7 @@ export const under18 = [
id: 24, id: 24,
isDare: true, isDare: true,
en: 'Write an message with some hint to the person you like.', en: 'Write an message with some hint to the person you like.',
ua: 'Напишіть повідомлення з натяком людині, яка вам подобається.', ua: 'Напиши повідомлення з натяком людині, яка тобі подобається.',
}, },
{ {
id: 25, id: 25,
@ -257,8 +257,8 @@ export const light = [
{ {
id: 2, id: 2,
isDare: true, isDare: true,
en: 'Одягни на вуха носки і сиди так 3 раунда.', ua: 'Одягни на вуха носки і сиди так 3 раунда.',
ua: 'Put socks on your ears and sit like that for 3 rounds.', en: 'Put socks on your ears and sit like that for 3 rounds.',
}, },
{ {
id: 3, id: 3,
@ -275,8 +275,8 @@ export const light = [
{ {
id: 5, id: 5,
isDare: false, isDare: false,
en: 'Найекстремальніше, що ти робив(ла), щоб комусь сподобатись?', ua: 'Найекстремальніше, що ти робив(ла), щоб комусь сподобатись?',
ua: "What's the most extreme thing you've done to please someone?", en: "What's the most extreme thing you've done to please someone?",
}, },
{ {
id: 6, id: 6,
@ -287,8 +287,8 @@ export const light = [
{ {
id: 7, id: 7,
isDare: false, isDare: false,
en: 'If you could be invisible for a day, what would you do?', en: 'You have invisibility for one day. What would you do?',
ua: 'Якщо б ви могли бути невидимими на один день, Що б ти зробив(ла), якб?', ua: 'У тебе невидимість на один день. Що б ти зробив(ла)?',
}, },
{ {
id: 8, id: 8,
@ -312,7 +312,7 @@ export const light = [
id: 11, id: 11,
isDare: false, isDare: false,
en: 'What’s the most unusual place you’ve ever visited?', en: 'What’s the most unusual place you’ve ever visited?',
ua: 'Яке найстрашніше місце, в якому ти був(ла)', ua: 'Яке найстрашніше місце, в якому ти був(ла)?',
}, },
{ {
id: 12, id: 12,
@ -347,8 +347,8 @@ export const light = [
{ {
id: 17, id: 17,
isDare: false, isDare: false,
en: 'What do you often look at on the Internet?', en: 'Tell the first impression when you saw the person on the left of you.',
ua: 'Про що ти часто дивишся в інтернеті?', ua: 'Розкажи перше враження, коли ти побачив(ла) людину ліворуч від тебе.',
}, },
{ {
id: 18, id: 18,
@ -371,8 +371,8 @@ export const light = [
{ {
id: 21, id: 21,
isDare: false, isDare: false,
en: 'Tell the funniest story about how you threw up.', en: 'Tell me about some trashy situation that you saw with your own eyes.',
ua: 'Розкажи про якусь ситуацію, яку ти бачив на власні очі.', ua: 'Розкажи про якусь трешову ситуацію, яку ти бачив на власні очі.',
}, },
{ {
id: 22, id: 22,
@ -407,8 +407,8 @@ export const light = [
{ {
id: 27, id: 27,
isDare: false, isDare: false,
en: 'Have you ever followed someone?', en: 'Have you ever followed someone live?',
ua: 'Ти колись слідкував за кимось?', ua: 'Ти колись слідкував за кимось вживу?',
}, },
{ {
id: 28, id: 28,
@ -420,7 +420,7 @@ export const light = [
id: 27, id: 27,
isDare: false, isDare: false,
en: 'Tell about the channel that you immediately open when something new appears there.', en: 'Tell about the channel that you immediately open when something new appears there.',
ua: 'Розкажи про канал, який ти одразу відкриваєш, коли там з являється щось нове.', ua: "Розкажи про канал, який ти одразу відкриваєш, коли там з'являється щось нове.",
}, },
{ {
id: 28, id: 28,
@ -431,8 +431,8 @@ export const light = [
{ {
id: 29, id: 29,
isDare: false, isDare: false,
en: 'Tell about the channel that you immediately open when something new appears there.', en: 'What act do you feel ashamed of?',
ua: 'Розкажи про канал, який ти одразу відкриваєш, коли там з являється щось нове.', ua: 'За який вчинок ти відчуваєш сором?',
}, },
{ {
id: 30, id: 30,
@ -455,14 +455,14 @@ export const light = [
{ {
id: 33, id: 33,
isDare: false, isDare: false,
en: 'Have you ever been dumped by a person you liked?', en: 'What act do you feel proud of?',
ua: 'Тебе колись кидала людина, яка тобі подобалась?', ua: 'За який вчинок ти відчуваєш гордість?',
}, },
{ {
id: 34, id: 34,
isDare: true, isDare: true,
en: 'Sit with an angry and sad face until the next round.', en: 'Sit with an angry and sad face until the next round.',
ua: 'Сидіть зі злою та сумною міною до наступного раунда.', ua: 'Сиди зі злою та сумною міною до наступного раунда.',
}, },
{ {
id: 35, id: 35,
@ -572,8 +572,8 @@ export const crazy = [
{ {
id: 12, id: 12,
isDare: false, isDare: false,
en: 'Є людина, яку ти ненавидиш, але вона про це не знає?', ua: 'Є людина, яку ти ненавидиш, але вона про це не знає?',
ua: "Is there a person you hate, but they don't know it?", en: "Is there a person you hate, but they don't know it?",
}, },
{ {
id: 13, id: 13,
@ -603,7 +603,7 @@ export const crazy = [
id: 17, id: 17,
isDare: true, isDare: true,
en: 'Act like a certain animal until your next turn. Others have to guess the animal.', en: 'Act like a certain animal until your next turn. Others have to guess the animal.',
ua: 'Ведіть себе як собака до свого наступного ходу.', ua: 'Веди себе як собака до свого наступного ходу.',
}, },
{ {
id: 18, id: 18,

2
src/module/common/typing/enums/products.enum.ts

@ -1,3 +1,3 @@
export enum ProductsEnum { export enum ProductsEnum {
Crazy = 'Crz', Crazy = 'crz',
} }

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

@ -5,7 +5,9 @@ export enum StorageKey {
Products = 'Products', Products = 'Products',
CustomPackage = 'CustomPackage', CustomPackage = 'CustomPackage',
ShuffleCustomPackage = 'ShuffleCustomPackage', ShuffleCustomPackage = 'ShuffleCustomPackage',
ShufflePackage = 'ShufflePackage',
Players = 'Players', Players = 'Players',
LimitForCrazy = 'limit', LimitForCrazy = 'limit',
SavedSteps = 'SavedSteps', SavedSteps = 'SavedSteps',
Voiceover = 'Voiceover',
} }

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

@ -1,10 +1,42 @@
import { useRef } from 'react' import { useRef } from 'react'
import { Animated } from 'react-native' import { useTranslation } from 'react-i18next'
import { Animated, Platform } from 'react-native'
import { Language } from '~module/common'
export const useAnimationTruthOrDare = () => { export const useAnimationTruthOrDare = (wordsArr: string[]) => {
const animContainer = useRef(new Animated.Value(400)).current const animContainer = useRef(new Animated.Value(400)).current
const { i18n } = useTranslation()
const startAnimation = () => { const animValues: Animated.Value[] = []
wordsArr.forEach((_, index) => (animValues[index] = new Animated.Value(0)))
const startAnimTextByVoice = () => {
const animations = wordsArr.map((word, index) => {
const wordWithComma = word.includes(',')
const isEnLanguage = i18n.language === Language.EN
const duration = isEnLanguage ? 50 : 75
const durationWithComma = Platform.select({
android: 75,
default: isEnLanguage ? 175 : 100,
})
return Animated.timing(animValues[index], {
toValue: 1,
duration:
word.length *
(wordWithComma ? durationWithComma : duration),
useNativeDriver: true,
})
})
Animated.sequence(animations).start()
}
const startAnimationCard = () => {
Animated.timing(animContainer, { Animated.timing(animContainer, {
toValue: 0, toValue: 0,
duration: 400, duration: 400,
@ -21,7 +53,9 @@ export const useAnimationTruthOrDare = () => {
} }
return { return {
startAnimation, startAnimationCard,
startAnimTextByVoice,
animValues,
animStyle: animStyleContainer, animStyle: animStyleContainer,
} }
} }

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

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

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

@ -24,7 +24,7 @@ export const PlayerField: FC<IProps> = ({ value, onChange, onDelete }) => {
onChange={onChange} onChange={onChange}
renderPostfix={renderClear} renderPostfix={renderClear}
inputProps={{ inputProps={{
placeholder: t('customPack.placeholder'), placeholder: t('common.placeholderPlayer'),
placeholderTextColor: colors.darkPurple, placeholderTextColor: colors.darkPurple,
}} }}
inputStyle={styles.input} inputStyle={styles.input}

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

@ -1,53 +0,0 @@
import React, { useEffect } from 'react'
import { Font, Icon, colors } from '../../common'
import { Animated, StyleSheet } from 'react-native'
import { useAnimationTruthOrDare } from '../animations'
interface IProps {
item: string
}
export const TruthOrDareView: React.FC<IProps> = ({ item }) => {
const { animStyle, startAnimation } = useAnimationTruthOrDare()
useEffect(() => {
startAnimation()
}, [])
return (
<Animated.View style={[styles.container, animStyle]}>
<Icon
name="magic-star"
size={24}
color={colors.turquoise}
style={styles.starIcon}
/>
<Animated.Text style={[styles.text]}>{item}</Animated.Text>
</Animated.View>
)
}
const styles = StyleSheet.create({
container: {
paddingHorizontal: 16,
paddingVertical: 32,
backgroundColor: colors.darkPurple,
borderRadius: 20,
alignItems: 'center',
shadowColor: '#190f42',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 1,
shadowRadius: 4,
elevation: 5,
marginTop: 88,
},
starIcon: {
marginBottom: 14,
},
text: {
color: colors.purple,
fontSize: 22,
lineHeight: 32,
textAlign: 'center',
fontFamily: Font.Roboto400,
},
})

104
src/module/game/components/truth-or-dare.tsx

@ -0,0 +1,104 @@
import React, { useEffect, useState } from 'react'
import { Font, Icon, StorageKey, colors, storageService } from '../../common'
import { Animated, StyleSheet, View } from 'react-native'
import { useAnimationTruthOrDare } from '../animations'
import Tts from 'react-native-tts'
interface IProps {
item: string
}
export const TruthOrDare: React.FC<IProps> = ({ item }) => {
const wordsArr = item.trim().split(' ')
const [voiceover, setVoiceover] = useState(true)
const { animStyle, animValues, startAnimationCard, startAnimTextByVoice } =
useAnimationTruthOrDare(wordsArr)
useEffect(() => {
startAnimationCard()
checkAndStartVoiceover()
}, [item])
const checkAndStartVoiceover = async () => {
const isVoicer = await storageService.get(StorageKey.Voiceover)
setVoiceover(isVoicer)
if (isVoicer) {
Tts.stop()
Tts.speak(item)
Tts.addEventListener('tts-start', startAnimTextByVoice)
}
}
return (
<Animated.View style={[styles.container, animStyle]}>
<Icon
name="magic-star"
size={24}
color={colors.turquoise}
style={styles.starIcon}
/>
<View style={styles.textWrap}>
{wordsArr?.map((word, index) => {
return (
<Animated.Text
key={`${word} + ${index * word.length}`}
style={[
styles.text,
{
opacity: voiceover ? animValues[index] : 1,
transform: [
{
translateX: voiceover
? Animated.multiply(
animValues[index],
new Animated.Value(-5),
)
: 0,
},
],
},
]}>
{word + ' '}
</Animated.Text>
)
})}
</View>
</Animated.View>
)
}
const styles = StyleSheet.create({
container: {
paddingLeft: 21,
paddingRight: 11,
paddingVertical: 32,
backgroundColor: colors.darkPurple,
borderRadius: 20,
alignItems: 'center',
shadowColor: '#190f42',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 1,
shadowRadius: 4,
elevation: 5,
marginTop: 88,
},
textWrap: {
flexDirection: 'row',
flexWrap: 'wrap',
justifyContent: 'center',
},
starIcon: {
marginBottom: 14,
},
text: {
color: colors.purple,
fontSize: 22,
lineHeight: 32,
textAlign: 'center',
fontFamily: Font.Roboto400,
},
})

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

@ -1 +1,2 @@
export * from './package-name.config'; export * from './package-name.config';
export * from './voice.config';

12
src/module/game/config/voice.config.ts

@ -0,0 +1,12 @@
import { Language } from '~module/common'
export const voiceConfig: any = {
[Language.UA]: {
language: 'uk-UA',
voice: 'com.apple.voice.compact.uk-UA.Lesya',
},
[Language.EN]: {
language: 'en-US',
voice: 'com.apple.ttsbundle.Samantha-compact',
},
}

54
src/module/game/hooks/get-current-truth-dares.hook.tsx

@ -1,4 +1,4 @@
import { useEffect } from 'react' import { useEffect, useMemo } from 'react'
import { import {
resetStepsByTruthOrDare, resetStepsByTruthOrDare,
selectCustomPackage, selectCustomPackage,
@ -19,7 +19,6 @@ import {
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import { RootState } from '~store/store' import { RootState } from '~store/store'
import firestore from '@react-native-firebase/firestore'
import _ from 'lodash' import _ from 'lodash'
interface UseTruthOrDareProps { interface UseTruthOrDareProps {
@ -52,7 +51,7 @@ export const useGetCurrentTruthOrDare = ({
} }
const getGameItemsByCustomPackage = () => { const getGameItemsByCustomPackage = () => {
if (_.isEmpty(customPackageShuffle.questions)) if (_.isEmpty(customPackageShuffle[customType]))
return customPackage[customType] return customPackage[customType]
return customPackageShuffle[customType] return customPackageShuffle[customType]
@ -65,22 +64,28 @@ export const useGetCurrentTruthOrDare = ({
return packageTruthsOrDares?.[currentStep]?.[i18n.language as Language] return packageTruthsOrDares?.[currentStep]?.[i18n.language as Language]
} }
const customTruthsOrDares = getGameItemsByCustomPackage() const customTruthsOrDares = useMemo(getGameItemsByCustomPackage, [
const packageTruthsOrDares = getGameItemsByPackage() customPackageShuffle,
const currentItem = getCurrentItem() customType,
])
console.log('currentItem', currentItem) const packageTruthsOrDares = useMemo(getGameItemsByPackage, [gameItems])
console.log('customTruthsOrDares', customTruthsOrDares) const currentItem = getCurrentItem()
const shuffleAndSavePackage = async () => { const shuffleAndSavePackage = async () => {
const shufflePackages = _.shuffle(packageTruthsOrDares) const savedShuffle = await storageService.get(StorageKey.ShufflePackage)
await firestore()
.collection('content') const shuffleItems = _.shuffle(savedShuffle[packageType])
.doc(packageType)
.update(shufflePackages) const newShuffle = {
...savedShuffle,
[packageType]: shuffleItems,
}
await storageService.set(StorageKey.ShufflePackage, newShuffle)
dispatch(resetStepsByTruthOrDare(choiceType)) dispatch(resetStepsByTruthOrDare(choiceType))
dispatch(shufflePackage(packageType)) dispatch(shufflePackage({ packageType, shuffleItems }))
} }
const shuffleAndSaveCustomPackage = async () => { const shuffleAndSaveCustomPackage = async () => {
@ -88,18 +93,33 @@ export const useGetCurrentTruthOrDare = ({
const shuffleCustom = _.shuffle(customTruthsOrDares) const shuffleCustom = _.shuffle(customTruthsOrDares)
dispatch(shuffleCustomPackage({ customType, shuffleCustom }))
dispatch(resetStepsByTruthOrDare(choiceType))
const savedShuffleCustom = await storageService.get( const savedShuffleCustom = await storageService.get(
StorageKey.ShuffleCustomPackage, StorageKey.ShuffleCustomPackage,
) )
if (!savedShuffleCustom?.[customType]) {
const newShuffled = {
...customPackage,
[customType]: shuffleCustom,
}
dispatch(shuffleCustomPackage(newShuffled))
return await storageService.set(
StorageKey.ShuffleCustomPackage,
newShuffled,
)
}
const newShuffled = { const newShuffled = {
...savedShuffleCustom, ...savedShuffleCustom,
[customType]: shuffleCustom, [customType]: shuffleCustom,
} }
await storageService.set(StorageKey.ShuffleCustomPackage, newShuffled) await storageService.set(StorageKey.ShuffleCustomPackage, newShuffled)
dispatch(shuffleCustomPackage(newShuffled))
dispatch(resetStepsByTruthOrDare(choiceType))
} }
const checkIsNeedShuffle = () => { const checkIsNeedShuffle = () => {

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

@ -1,2 +1,3 @@
export * from './get-current-truth-dares.hook' export * from './get-current-truth-dares.hook'
export * from './use-set-steps-by-package'; export * from './use-set-steps-by-package';
export * from './use-voice.hook';

2
src/module/game/hooks/use-set-steps-by-package.tsx

@ -11,8 +11,6 @@ export const useSetStepsByPackage = (packageType: PackageType) => {
const stepsByPackage = lastSteps?.[packageType] const stepsByPackage = lastSteps?.[packageType]
console.log('stepsByPackage', stepsByPackage)
if (!stepsByPackage) return dispatch(resetSteps()) if (!stepsByPackage) return dispatch(resetSteps())
dispatch(setStep(stepsByPackage)) dispatch(setStep(stepsByPackage))

42
src/module/game/hooks/use-voice.hook.tsx

@ -0,0 +1,42 @@
import { useEffect, useState } from 'react'
import Tts from 'react-native-tts'
import { voiceConfig } from '../config'
import { useTranslation } from 'react-i18next'
export const useVoice = () => {
const { i18n } = useTranslation()
const [ttsStatus, setTtsStatus] = useState('initiliazing')
useEffect(() => {
Tts.addEventListener('tts-finish', _event => setTtsStatus('finished'))
Tts.addEventListener('tts-cancel', _event => setTtsStatus('cancelled'))
Tts.getInitStatus().then(initTts)
return () => {
if (ttsStatus === 'finished') {
Tts.removeEventListener('tts-start', _event =>
setTtsStatus('started'),
)
}
Tts.removeEventListener('tts-finish', _event =>
setTtsStatus('finished'),
)
Tts.removeEventListener('tts-cancel', _event =>
setTtsStatus('cancelled'),
)
}
}, [])
const initTts = async () => {
try {
await Tts.setDefaultLanguage(voiceConfig[i18n.language].language)
await Tts.setDefaultVoice(voiceConfig[i18n.language].voice)
await Tts.setDefaultRate(0.5)
} catch (err) {
//Samsung S9 has always this error:
//"Language is not supported"
console.log(`setDefaultLanguage error `, err)
}
}
}

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

@ -17,7 +17,7 @@ import { useIsFocused, useRoute } from '@react-navigation/native'
import { useAnimationButton } from '../animations' import { useAnimationButton } from '../animations'
import { PlayerName } from '../components/player-name.component' import { PlayerName } from '../components/player-name.component'
import { packageNameConfig } from '../config' import { packageNameConfig } from '../config'
import { useSetStepsByPackage } from '../hooks' import { useSetStepsByPackage, useVoice } from '../hooks'
interface IRouteParams { interface IRouteParams {
packageType?: PackageType packageType?: PackageType
@ -33,6 +33,8 @@ export const GameScreen: FC = () => {
useSetStepsByPackage(packageType) useSetStepsByPackage(packageType)
useVoice()
const randomGame = () => { const randomGame = () => {
const isTruthRandom = Math.random() < 0.5 const isTruthRandom = Math.random() < 0.5
const customRandom = isTruthRandom const customRandom = isTruthRandom

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

@ -37,7 +37,7 @@ export const PlayersScreen: FC = () => {
const onDeletePlayer = (index: number) => { const onDeletePlayer = (index: number) => {
const updatedPlayers = [...players] const updatedPlayers = [...players]
if (updatedPlayers.length === 1) return if (updatedPlayers.length <= 2) return
updatedPlayers.splice(index, 1) updatedPlayers.splice(index, 1)
dispatch(setPlayers(updatedPlayers)) dispatch(setPlayers(updatedPlayers))
@ -66,16 +66,13 @@ export const PlayersScreen: FC = () => {
<ScreenLayout <ScreenLayout
needScroll needScroll
scrollStyle={{ scrollStyle={{
flex: 1,
paddingBottom: 40, paddingBottom: 40,
flexGrow: 1,
}} }}
headerComponent={ headerComponent={
<Header <Header title={t('pageTitles.players')} leftIcon="" />
title={t('pageTitles.players')}
onPressLeft={onSavePlayers}
/>
}> }>
<View style={{ rowGap: 16 }}> <View style={{ rowGap: 16, flex: 1 }}>
{players.map((player, index) => ( {players.map((player, index) => (
<PlayerField <PlayerField
key={index} key={index}
@ -84,14 +81,13 @@ export const PlayersScreen: FC = () => {
onDelete={() => onDeletePlayer(index)} onDelete={() => onDeletePlayer(index)}
/> />
))} ))}
<ButtonWithIcon
styleBtn={styles.plusBtn}
iconName="add-plus"
onPress={onAddField}
/>
</View> </View>
<ButtonWithIcon
styleBtn={styles.plusBtn}
iconName="add-plus"
onPress={onAddField}
/>
<ButtonPrimary <ButtonPrimary
iconName="play" iconName="play"
onPress={onSavePlayers} onPress={onSavePlayers}
@ -109,7 +105,7 @@ const styles = StyleSheet.create({
marginBottom: 122, marginBottom: 122,
}, },
plusBtn: { plusBtn: {
marginTop: 24, marginBottom: 24,
borderRadius: 50, borderRadius: 50,
width: 50, width: 50,
height: 50, height: 50,

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

@ -20,13 +20,14 @@ import {
onNextPlayer, onNextPlayer,
selectStep, selectStep,
} from '../../../store/slices' } from '../../../store/slices'
import { TruthOrDareView } from '../components' import { TruthOrDare } from '../components'
import { useAnimationIconsButton } from '../animations' import { useAnimationIconsButton } from '../animations'
import { useGetCurrentTruthOrDare } from '../hooks' import { useGetCurrentTruthOrDare } from '../hooks'
import { PlayerName } from '../components/player-name.component' import { PlayerName } from '../components/player-name.component'
import { packageNameConfig } from '../config' import { packageNameConfig } from '../config'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import Tts from 'react-native-tts'
interface IRouteParams { interface IRouteParams {
packageType?: PackageType packageType?: PackageType
@ -55,6 +56,7 @@ export const TruthOrDareScreen: React.FC = () => {
dispatch(onNextPlayer()) dispatch(onNextPlayer())
dispatch(nextStep(choiceType)) dispatch(nextStep(choiceType))
saveLastStep() saveLastStep()
Tts.stop()
} }
const onAddGameItemToCustomPackage = () => { const onAddGameItemToCustomPackage = () => {
@ -67,6 +69,7 @@ export const TruthOrDareScreen: React.FC = () => {
} }
const onPressAddPlus = () => { const onPressAddPlus = () => {
Tts.stop()
const subtitleByChoice = const subtitleByChoice =
choiceType === ChoiceType.Truth choiceType === ChoiceType.Truth
? t('customPack.addCustomTruth') ? t('customPack.addCustomTruth')
@ -157,7 +160,7 @@ export const TruthOrDareScreen: React.FC = () => {
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<PlayerName /> <PlayerName />
<TruthOrDareView item={currentItem} /> {currentItem ? <TruthOrDare item={currentItem} /> : null}
<View style={styles.buttons}> <View style={styles.buttons}>
<ButtonWithIcon <ButtonWithIcon
@ -175,14 +178,15 @@ export const TruthOrDareScreen: React.FC = () => {
animation={animScale.func} animation={animScale.func}
animStyle={animScale.style} animStyle={animScale.style}
/> />
{packageType !== PackageType.Custom && (
<ButtonWithIcon <ButtonWithIcon
styleBtn={{ width: 101 }} styleBtn={{ width: 101 }}
iconName="add-plus" iconName="add-plus"
onPress={onPressAddPlus} onPress={onPressAddPlus}
animation={animScale.func} animation={animScale.func}
animStyle={animScale.style} animStyle={animScale.style}
/> />
)}
</View> </View>
</View> </View>
</ScreenLayout> </ScreenLayout>

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

@ -1,10 +1,7 @@
import React, { useEffect, useRef } from 'react' import React, { useEffect, useRef } from 'react'
import { Animated } from 'react-native' import { Animated } from 'react-native'
import { useIsFocused } from '@react-navigation/native'
export const useAnimationList = (delay: number) => { export const useAnimationList = (delay: number) => {
const isFocus = useIsFocused()
const animTransformX = useRef(new Animated.Value(0)).current const animTransformX = useRef(new Animated.Value(0)).current
const animTransformY = useRef(new Animated.Value(0)).current const animTransformY = useRef(new Animated.Value(0)).current
@ -33,12 +30,8 @@ export const useAnimationList = (delay: number) => {
), ),
]) ])
if (!isFocus) {
return animation.reset()
}
animation.start() animation.start()
}, [isFocus]) }, [])
const animationStyleItem = { const animationStyleItem = {
opacity: animTransformX, opacity: animTransformX,

17
src/module/packages/atoms/packages-page-separator.atom.tsx

@ -8,20 +8,17 @@ export const PackagesPageSeparator = () => {
useEffect(() => { useEffect(() => {
Animated.spring(fadeInAnim, { Animated.spring(fadeInAnim, {
toValue: 1, toValue: 1,
useNativeDriver: true, useNativeDriver: false,
delay: 1100, stiffness: 50,
delay: 1400,
}).start() }).start()
}, []) }, [])
const animStyle = { const animStyle = {
transform: [ width: fadeInAnim.interpolate({
{ inputRange: [0, 1],
translateY: fadeInAnim.interpolate({ outputRange: ['0%', '100%'],
inputRange: [0, 1], }),
outputRange: [-900, 0],
}),
},
],
} }
return ( return (

2
src/module/packages/config/packages-list.config.tsx

@ -27,7 +27,7 @@ export const packageListConfig = [
/> />
), ),
description: { description: {
ua: 'Challenge your friends or your significant other and see how far they can go!', ua: 'Веселі та несподівані завдання гарантовано розсмішать та відкриють нові факти з життя учасників!',
en: 'Fun and unexpected tasks are guaranteed to make you laugh and discover new facts from the life of the participants.', en: 'Fun and unexpected tasks are guaranteed to make you laugh and discover new facts from the life of the participants.',
}, },
}, },

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

@ -35,6 +35,7 @@ export const UserNavigationGroup: FC = () => {
<UserStack.Screen <UserStack.Screen
name={UserRouteKey.TruthOrDare} name={UserRouteKey.TruthOrDare}
component={TruthOrDareScreen} component={TruthOrDareScreen}
options={{ animation: 'fade' }}
/> />
<UserStack.Screen <UserStack.Screen

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

@ -31,6 +31,14 @@ export const LoadingScreen: FC = () => {
return await storageService.get(StorageKey.FinishOnBoarding) return await storageService.get(StorageKey.FinishOnBoarding)
} }
const setVoiceover = async () => {
const isVoiceover = await storageService.get(StorageKey.Voiceover)
if (isVoiceover === null) {
await storageService.set(StorageKey.Voiceover, true)
}
}
const init = async () => { const init = async () => {
const language = await getLanguage() const language = await getLanguage()
const isOnBoard = await getOnboardEnd() const isOnBoard = await getOnboardEnd()
@ -56,6 +64,7 @@ export const LoadingScreen: FC = () => {
useEffect(() => { useEffect(() => {
init() init()
setVoiceover()
}, []) }, [])
return ( return (

1
src/module/settings/atoms/index.ts

@ -1,3 +1,4 @@
export * from './selected-language-in-settings.atom' export * from './selected-language-in-settings.atom'
export * from './switch-notifications.atom' export * from './switch-notifications.atom'
export * from './purchases.atom' export * from './purchases.atom'
export * from './switch-voiceover.atom';

2
src/module/settings/atoms/switch-notifications.atom.tsx

@ -6,7 +6,7 @@ export const SwitchNotificationsAtom = () => {
const [isEnabled, setIsEnabled] = useState(true) const [isEnabled, setIsEnabled] = useState(true)
const toggleSwitch = () => { const toggleSwitch = () => {
setIsEnabled(previousState => !previousState) setIsEnabled(!isEnabled)
} }
return ( return (

33
src/module/settings/atoms/switch-voiceover.atom.tsx

@ -0,0 +1,33 @@
import React, { useEffect, useState } from 'react'
import { Switch } from 'react-native'
import { StorageKey, colors, storageService } from '../../common'
export const SwitchVoiceoverAtom = () => {
const [voiceover, setVoiceover] = useState(true)
const toggleSwitch = async () => {
setVoiceover(!voiceover)
await storageService.set(StorageKey.Voiceover, !voiceover)
}
const getIsVoiceover = async () => {
const isVoiceover = await storageService.get(StorageKey.Voiceover)
setVoiceover(isVoiceover)
}
useEffect(() => {
getIsVoiceover()
}, [])
return (
<Switch
trackColor={{ true: colors.purple }}
thumbColor={voiceover ? colors.turquoise : colors.darkPurple}
ios_backgroundColor={colors.purple}
onValueChange={toggleSwitch}
style={{ width: 51 }}
value={voiceover}
/>
)
}

11
src/module/settings/config/settings.config.tsx

@ -1,6 +1,10 @@
import React from 'react' import React from 'react'
import { SettingLocale } from '../../../i18n/interfaces/settings.types.interface' import { SettingLocale } from '../../../i18n/interfaces/settings.types.interface'
import { SelectedLanguage, SwitchNotificationsAtom } from '../atoms' import {
SelectedLanguage,
SwitchNotificationsAtom,
SwitchVoiceoverAtom,
} from '../atoms'
const translatePath = (itemKey: keyof SettingLocale.Core) => const translatePath = (itemKey: keyof SettingLocale.Core) =>
`settingTranslation.${itemKey}` `settingTranslation.${itemKey}`
@ -20,6 +24,11 @@ export const settingsConfig = [
image: 'notification', image: 'notification',
component: () => <SwitchNotificationsAtom />, component: () => <SwitchNotificationsAtom />,
}, },
{
title: translatePath('voiceover'),
image: 'music',
component: () => <SwitchVoiceoverAtom />,
},
{ {
title: translatePath('write'), title: translatePath('write'),
image: 'message', image: 'message',

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

@ -67,6 +67,8 @@ export const SettingsScreen: FC = () => {
break break
case 'notification': case 'notification':
break break
case 'voice':
break
case 'message': case 'message':
nav.navigate(UserRouteKey.WriteToUs) nav.navigate(UserRouteKey.WriteToUs)
break break

10
src/store/slices/custom-package.slice.ts

@ -49,15 +49,9 @@ export const customPackageSlice = createSlice({
reducers: { reducers: {
shuffleCustomPackage: ( shuffleCustomPackage: (
state, state,
action: PayloadAction<{ action: PayloadAction<ICustomPackage>,
shuffleCustom: string[]
customType: CustomType
}>,
) => { ) => {
const shuffleItems = action.payload.shuffleCustom state.shuffleCustom = action.payload
const customType = action.payload.customType
state.shuffleCustom[customType] = shuffleItems
}, },
addCustomItem: ( addCustomItem: (
state, state,

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

@ -1,6 +1,11 @@
import { PayloadAction, createAsyncThunk, createSlice } from '@reduxjs/toolkit' import { PayloadAction, createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import firestore from '@react-native-firebase/firestore' import firestore from '@react-native-firebase/firestore'
import { GameItem, PackageType } from '../../module/common' import {
GameItem,
PackageType,
StorageKey,
storageService,
} from '../../module/common'
import { RootState } from '../store' import { RootState } from '../store'
import _ from 'lodash' import _ from 'lodash'
@ -34,7 +39,14 @@ export const getGameItemsFromFirestore = createAsyncThunk(
return acc return acc
}, {}) }, {})
return allPackages const saved = await storageService.get(StorageKey.ShufflePackage)
if (!saved) {
await storageService.set(StorageKey.ShufflePackage, allPackages)
return allPackages
}
return saved
}, },
) )
@ -42,15 +54,22 @@ export const gameItemsSlice = createSlice({
name: 'gameItems', name: 'gameItems',
initialState, initialState,
reducers: { reducers: {
shufflePackage: (state, action: PayloadAction<PackageType>) => { shufflePackage: (
const packageType = action.payload state,
state[packageType] = _.shuffle(state[packageType]) action: PayloadAction<{
packageType: PackageType
shuffleItems: GameItem[]
}>,
) => {
const packageType = action.payload.packageType
state[packageType] = action.payload.shuffleItems
}, },
}, },
extraReducers(builder) { extraReducers(builder) {
builder builder
.addCase(getGameItemsFromFirestore.fulfilled, (state, action) => { .addCase(getGameItemsFromFirestore.fulfilled, (state, action) => {
const allPackages = action.payload const allPackages = action.payload
state.under18 = allPackages[PackageType.Under18] state.under18 = allPackages[PackageType.Under18]
state.light = allPackages[PackageType.Light] state.light = allPackages[PackageType.Light]
state.crazy = allPackages[PackageType.Crazy] state.crazy = allPackages[PackageType.Crazy]

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

@ -7,22 +7,18 @@ export interface PlayersState {
players: string[] players: string[]
playerIndex: number playerIndex: number
currentPlayer: string currentPlayer: string
loaded: boolean
hasError: boolean
} }
const initialState: PlayersState = { const initialState: PlayersState = {
players: [], players: [],
playerIndex: 0, playerIndex: 0,
currentPlayer: '', currentPlayer: '',
loaded: false,
hasError: false,
} }
export const getPlayersAsync = createAsyncThunk('get-players', async () => { export const getPlayersAsync = createAsyncThunk('get-players', async () => {
const savedPlayers = await storageService.get(StorageKey.Players) const savedPlayers = await storageService.get(StorageKey.Players)
return savedPlayers ? savedPlayers : [''] return savedPlayers ? savedPlayers : ['', '']
}) })
export const playersSlice = createSlice({ export const playersSlice = createSlice({
@ -47,17 +43,10 @@ export const playersSlice = createSlice({
}, },
}, },
extraReducers(builder) { extraReducers(builder) {
builder builder.addCase(getPlayersAsync.fulfilled, (state, action) => {
.addCase(getPlayersAsync.fulfilled, (state, action) => { state.players = action.payload
state.players = action.payload state.currentPlayer = state.players[0]
state.currentPlayer = state.players[0] })
})
.addCase(getPlayersAsync.pending, state => {
state.loaded = false
})
.addCase(getPlayersAsync.rejected, state => {
state.hasError = true
})
}, },
}) })

Loading…
Cancel
Save