Browse Source

fix | conflict

fixVoiceMsg
Denis Kurmansky 3 years ago
parent
commit
1eca952c98
  1. 8
      android/app/agconnect-services.json
  2. 2
      android/app/build.gradle
  3. 1
      android/app/src/main/AndroidManifest.xml
  4. 3
      android/app/src/main/java/com/taskme/MainActivity.java
  5. 1
      android/app/src/main/java/com/taskme/MainApplication.java
  6. 2
      android/settings.gradle
  7. 59
      ios/Podfile.lock
  8. 8
      ios/taskme.xcodeproj/project.pbxproj
  9. 181
      package-lock.json
  10. 5
      package.json
  11. 1
      src/api/auth/requests.ts
  12. 10
      src/api/tasks-documents/requests.ts
  13. 3
      src/api/tasks/responses.interface.ts
  14. 1
      src/api/tasks/transform.ts
  15. 53
      src/modules/account/hooks/use-account-editor.hook.ts
  16. 6
      src/modules/account/validations/edit-account.validator.ts
  17. 129
      src/modules/chats/components/chat-send-img-modal.compoennt.tsx
  18. 36
      src/modules/chats/configs/chat-card-buttons.config.ts
  19. 54
      src/modules/chats/hooks/use-chat-messages.hook.ts
  20. 26
      src/modules/chats/hooks/use-chats-list.hook.ts
  21. 48
      src/modules/chats/hooks/use-create-text-message.hook.ts
  22. 2
      src/modules/chats/screens/chat.tsx
  23. 2
      src/modules/chats/transforms/chat-messages.transforms.ts
  24. 2
      src/modules/comments/components/comments-footer.tsx
  25. 8
      src/modules/comments/screens/comments.screen.tsx
  26. 13
      src/modules/contacts/hooks/use-fetch-contacts.hook.ts
  27. 2
      src/modules/groups/components/groups-list.component.tsx
  28. 7
      src/modules/root/index.tsx
  29. 46
      src/modules/root/smart-components/info-modal.smart-component.tsx
  30. 1
      src/modules/root/smart-components/modal-picker.smart-component.tsx
  31. 5
      src/modules/tasks/components/task-card.component.tsx
  32. 20
      src/modules/tasks/components/task-details-attachments-collapse.component.tsx
  33. 7
      src/modules/tasks/components/task-details-attachments-list.component.tsx
  34. 184
      src/modules/tasks/config/task-attachments-menu.config.ts
  35. 6
      src/modules/tasks/config/task-card-buttons.config.ts
  36. 3
      src/modules/tasks/config/task-card-icons.config.ts
  37. 8
      src/modules/tasks/consts/add-update-task.consts.ts
  38. 3
      src/modules/tasks/consts/index.ts
  39. 2
      src/modules/tasks/enum/index.ts
  40. 5
      src/modules/tasks/enum/task-attachments.enums.ts
  41. 5
      src/modules/tasks/hooks/index.ts
  42. 42
      src/modules/tasks/hooks/use-fetch-group-tasks.hook.ts
  43. 53
      src/modules/tasks/hooks/use-fetch-my-tasks.hook.ts
  44. 4
      src/modules/tasks/hooks/use-fetch-tasks-by-filter.hook.ts
  45. 50
      src/modules/tasks/hooks/use-fetch-user-tasks.hook.ts
  46. 6
      src/modules/tasks/hooks/use-task-actions.hook.ts
  47. 7
      src/modules/tasks/hooks/use-task-details.hook.ts
  48. 9
      src/modules/tasks/hooks/use-task-filter.hook.ts
  49. 27
      src/modules/tasks/hooks/use-task-list-events.hook.ts
  50. 6
      src/modules/tasks/hooks/use-tasks-print.hook.ts
  51. 2
      src/modules/tasks/interfaces/index.ts
  52. 5
      src/modules/tasks/interfaces/task-attachments.interfaces.ts
  53. 57
      src/modules/tasks/screens/add-update-task.screen.tsx
  54. 32
      src/modules/tasks/screens/filter-tasks-result.screen.tsx
  55. 3
      src/modules/tasks/screens/my-tasks.screen.tsx
  56. 1
      src/modules/tasks/screens/print-tasks.screen.tsx
  57. 12
      src/modules/tasks/screens/task-details.screen.tsx
  58. 1
      src/modules/tasks/smart-components/swipable-task-card.smart-component.tsx
  59. 86
      src/modules/tasks/smart-components/task-attachments-collapse.smart-component.tsx
  60. 1
      src/modules/tasks/smart-components/tasks-list.smart-component.tsx
  61. 1
      src/modules/tasks/transforms/index.ts
  62. 15
      src/modules/tasks/transforms/task-attachments.transforms.ts
  63. 6
      src/modules/tasks/validations/create-task.validations.ts
  64. 3
      src/modules/taxonomies/smart-components/select-taxonomies-modal/index.tsx
  65. 3
      src/modules/users/smart-components/select-user-modal/index.tsx
  66. 5
      src/services/domain/auth.service.ts
  67. 23
      src/services/domain/task-documents.service.ts
  68. 48
      src/services/system/camera-roll.service.ts
  69. 90
      src/services/system/fs.service.ts
  70. 3
      src/services/system/real-time.service.ts
  71. 18
      src/shared/components/plugins/accordion.component.tsx
  72. 21
      src/shared/components/plugins/chat/chat-bar.component.tsx
  73. 14
      src/shared/components/plugins/chat/chat-messages.component.tsx
  74. 2
      src/shared/components/tools/keyboard-spacer.component.tsx
  75. 7
      src/shared/events/index.ts
  76. 26
      src/shared/helpers/fs.helpers.ts
  77. 1
      src/shared/interfaces/media.interfaces.ts
  78. 5
      src/shared/interfaces/tasks.interfaces.ts
  79. 1
      src/store/shared/reducer.ts
  80. 70
      yarn.lock

8
android/app/agconnect-services.json

@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
"project_id":"99536292102144353",
"app_id":"105896531",
"api_key":"DAEDAMe2g0hAuXPDcOWX/VjzI9nScUzYz9mqg91UlGpseX11V5bNprMJPD5dVXxZOvZ5uThxc8vSZlkf3ki4nzMZ46mB/ek7pYdPcA==",
"package_name":"com.taskme2"
"package_name":"com.taskme"
},
"oauth_client":{
"client_id":"105896531",
@ -31,7 +31,7 @@ @@ -31,7 +31,7 @@
},
"app_info":{
"app_id":"105896531",
"package_name":"com.taskme2"
"package_name":"com.taskme"
},
"service":{
"analytics":{
@ -57,12 +57,12 @@ @@ -57,12 +57,12 @@
"configuration_version":"3.0",
"appInfos":[
{
"package_name":"com.taskme2",
"package_name":"com.taskme",
"client":{
"app_id":"105896531"
},
"app_info":{
"package_name":"com.taskme2",
"package_name":"com.taskme",
"app_id":"105896531"
},
"oauth_client":{

2
android/app/build.gradle

@ -32,7 +32,7 @@ android { @@ -32,7 +32,7 @@ android {
}
defaultConfig {
applicationId "com.taskme2"
applicationId "com.taskme"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 8

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

@ -33,6 +33,7 @@ @@ -33,6 +33,7 @@
android:icon="@mipmap/ic_launcher"
android:allowBackup="false"
android:usesCleartextTraffic="true"
android:requestLegacyExternalStorage="true"
android:theme="@style/AppTheme">
<meta-data
android:name="com.onesignal.NotificationServiceExtension"

3
android/app/src/main/java/com/taskme/MainActivity.java

@ -3,8 +3,7 @@ package com.taskme; @@ -3,8 +3,7 @@ package com.taskme;
import android.os.Bundle;
import com.facebook.react.ReactActivity;
// react-native-splash-screen >= 0.3.1
import org.devio.rn.splashscreen.SplashScreen; // here
import org.devio.rn.splashscreen.SplashScreen;
import android.content.Intent;
import android.content.res.Configuration;

1
android/app/src/main/java/com/taskme/MainApplication.java

@ -7,7 +7,6 @@ import com.facebook.react.ReactApplication; @@ -7,7 +7,6 @@ import com.facebook.react.ReactApplication;
import com.punarinta.RNSoundLevel.RNSoundLevel;
import com.rnfs.RNFSPackage;
import org.devio.rn.splashscreen.SplashScreenReactPackage;
import org.devio.rn.splashscreen.SplashScreenReactPackage;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;

2
android/settings.gradle

@ -3,6 +3,8 @@ include ':react-native-sound-level' @@ -3,6 +3,8 @@ include ':react-native-sound-level'
project(':react-native-sound-level').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sound-level/android')
include ':react-native-fs'
project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android')
include ':react-native-fs'
project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android')
include ':react-native-video'
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android-exoplayer')
include ':react-native-splash-screen'

59
ios/Podfile.lock

@ -26,7 +26,16 @@ PODS: @@ -26,7 +26,16 @@ PODS:
- libwebp/mux (1.2.1):
- libwebp/demux
- libwebp/webp (1.2.1)
- OneSignal (3.10.2)
- OneSignal (3.11.1):
- OneSignal/OneSignalCore (= 3.11.1)
- OneSignal/OneSignalExtension (= 3.11.1)
- OneSignal/OneSignalOutcomes (= 3.11.1)
- OneSignal/OneSignalCore (3.11.1)
- OneSignal/OneSignalExtension (3.11.1):
- OneSignal/OneSignalCore
- OneSignal/OneSignalOutcomes
- OneSignal/OneSignalOutcomes (3.11.1):
- OneSignal/OneSignalCore
- OneSignalXCFramework (3.10.1)
- Permission-Camera (3.2.0):
- RNPermissions
@ -225,6 +234,8 @@ PODS: @@ -225,6 +234,8 @@ PODS:
- React-jsi (= 0.64.3)
- React-perflogger (= 0.64.3)
- React-jsinspector (0.64.3)
- react-native-cameraroll (4.1.2):
- React-Core
- react-native-clear-cache (1.0.3):
- React
- react-native-date-picker (3.4.3):
@ -387,6 +398,8 @@ PODS: @@ -387,6 +398,8 @@ PODS:
- RNScreens (3.6.0):
- React-Core
- React-RCTImage
- RNShare (7.4.1):
- React-Core
- RNSoundLevel (1.1.5):
- React
- RNSVG (9.13.6):
@ -424,6 +437,7 @@ DEPENDENCIES: @@ -424,6 +437,7 @@ DEPENDENCIES:
- React-jsi (from `../node_modules/react-native/ReactCommon/jsi`)
- React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`)
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
- "react-native-cameraroll (from `../node_modules/@react-native-community/cameraroll`)"
- react-native-clear-cache (from `../node_modules/oa-react-native-clear-cache`)
- react-native-date-picker (from `../node_modules/react-native-date-picker`)
- react-native-document-picker (from `../node_modules/react-native-document-picker`)
@ -465,6 +479,7 @@ DEPENDENCIES: @@ -465,6 +479,7 @@ DEPENDENCIES:
- RNPermissions (from `../node_modules/react-native-permissions`)
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNScreens (from `../node_modules/react-native-screens`)
- RNShare (from `../node_modules/react-native-share`)
- RNSoundLevel (from `../node_modules/react-native-sound-level`)
- RNSVG (from `../node_modules/react-native-svg`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
@ -518,6 +533,8 @@ EXTERNAL SOURCES: @@ -518,6 +533,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon/jsiexecutor"
React-jsinspector:
:path: "../node_modules/react-native/ReactCommon/jsinspector"
react-native-cameraroll:
:path: "../node_modules/@react-native-community/cameraroll"
react-native-clear-cache:
:path: "../node_modules/oa-react-native-clear-cache"
react-native-date-picker:
@ -600,6 +617,8 @@ EXTERNAL SOURCES: @@ -600,6 +617,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-reanimated"
RNScreens:
:path: "../node_modules/react-native-screens"
RNShare:
:path: "../node_modules/react-native-share"
RNSoundLevel:
:path: "../node_modules/react-native-sound-level"
RNSVG:
@ -618,10 +637,10 @@ SPEC CHECKSUMS: @@ -618,10 +637,10 @@ SPEC CHECKSUMS:
ffmpeg-kit-react-native: bc3c900af9f761b6fe30906ee1bd98000c79f9c9
glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62
libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc
OneSignal: 29b555f5991109dc717f75d4440c565cab92511c
OneSignal: 60902887c93f91e9b5599583e7df5f55b2144755
OneSignalXCFramework: fe027712118f2862ab193b72270487f11450ea63
Permission-Camera: 53efcbb755b0e8bdf253dbb27cc7559ccfce8480
Permission-PhotoLibrary: 7bec836dcdd04a0bfb200c314f1aae06d4476357
Permission-Camera: 9eb618fd601ae4a674b072c3b0d37f109d7b91e5
Permission-PhotoLibrary: 900e7e33012bd5e02e5859cb65d5bb2134259c64
RCT-Folly: ec7a233ccc97cc556cf7237f0db1ff65b986f27c
RCTRequired: d34bf57e17cb6e3b2681f4809b13843c021feb6c
RCTTypeSafety: 8dab4933124ed39bb0c1d88d74d61b1eb950f28f
@ -633,20 +652,21 @@ SPEC CHECKSUMS: @@ -633,20 +652,21 @@ SPEC CHECKSUMS:
React-jsi: a8b09c29521c798f1783348b37b511ba7b3dbeb3
React-jsiexecutor: df6abc9fafbecb8e5b7a5fbc5e6d4bd017d594d5
React-jsinspector: 34e23860273a23695342f58eed3ffd3ba10c31e0
react-native-cameraroll: 2957f2bce63ae896a848fbe0d5352c1bd4d20866
react-native-clear-cache: 28bce59b33cd809e0afe903786787b4409d1c1fb
react-native-date-picker: 201b481c94dcb7678f4712477ad026dd7793305b
react-native-document-picker: 429972f7ece4463aa5bcdd789622b3a674a3c5d1
react-native-document-picker: 772d04a4bc5c35da9abe27b08ac271420ae3f9ef
react-native-html-to-pdf: 4c5c6e26819fe202971061594058877aa9b25265
react-native-image-picker: 5fe0a96bef4935bbdfb02f59b910bf40d5526109
react-native-netinfo: 3d3769f0d65de15c83a9bf1346f8be71de5a24bf
react-native-onesignal: 7643517fe655b94cfda8e697ec16f9b9135648b8
react-native-image-picker: 27c3726557dac6e224a17c564c16cdc7fb952f79
react-native-netinfo: 877946c7b4eb85a639cf1ea31333dadc2be999a1
react-native-onesignal: 9cb94608abf49ab78fb660f4cca0cd08e4da9742
react-native-orientation-locker: 998c0744e26624407dac068c04c605b4af7304a2
react-native-pager-view: 5ab4d0b4b44d89f77310cb3eb8129745f274ce55
react-native-pager-view: f21658a2e12eced35ef998250375e4e4dc9b8487
react-native-print: 1ae7a4ce4bf673b975f2ec9c4e00ad0c9b60e8aa
react-native-restart: 45c8dca02491980f2958595333cbccd6877cb57e
react-native-safe-area-context: 61c8c484a3a9e7d1fda19f7b1794b35bbfd2262a
react-native-restart: 8af4579c94638f38bd9074ec477ebf087de87dc5
react-native-safe-area-context: 8465df05de8106c584b117f0e027e17174d6e02e
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
react-native-video: a4c2635d0802f983594b7057e1bce8f442f0ad28
react-native-video: 77ce22be7abff9e373527ca10106a99dea3ba56c
React-perflogger: cc76a4254d19640f1d8ad1c66fdee800414b805c
React-RCTActionSheet: 7448f049318d8d7e8a9a1ebb742ada721757eea8
React-RCTAnimation: fb9b3fa1a4a9f5e6ab01b3368693ce69860ba76a
@ -661,20 +681,21 @@ SPEC CHECKSUMS: @@ -661,20 +681,21 @@ SPEC CHECKSUMS:
ReactCommon: 8fea6422328e2fc093e25c9fac67adbcf0f04fb4
ReactNativeExceptionHandler: b11ff67c78802b2f62eed0e10e75cb1ef7947c60
rn-fetch-blob: f065bb7ab7fb48dd002629f8bdcb0336602d3cba
RNAudioRecorderPlayer: 001f01049754e978c43af97162452c8563d5794a
RNCAsyncStorage: e8b8d6320a0dd90eb610fb0d0b1ef90596697c69
RNCClipboard: 41d8d918092ae8e676f18adada19104fa3e68495
RNAudioRecorderPlayer: 4482e03d9336c8d8ba41825af227f74f41ba0a01
RNCAsyncStorage: 0f655864a81214d1c5a9bf0faf79d86dc25c383e
RNCClipboard: 5e299c6df8e0c98f3d7416b86ae563d3a9f768a3
RNCPicker: c3a3d481bec16624ed84a3c2a64c92fdc5515960
RNCPushNotificationIOS: 87b8d16d3ede4532745e05b03c42cff33a36cc45
RNDeviceInfo: cc7de0772378f85d8f36ae439df20f05c590a651
RNCPushNotificationIOS: 089da3b657e1e3d464f38195fd2e3069608ef5af
RNDeviceInfo: 0d6865ab0a57d9192bdd4e4f5894340b846c3e53
RNFastImage: 1f2cab428712a4baaf78d6169eaec7f622556dd7
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
RNGestureHandler: a479ebd5ed4221a810967000735517df0d2db211
RNImageCropPicker: 448d3c6e923fde3466b49caf3c2457c2a0ba02dd
RNPermissions: f7ebe52db07c00901127966ca080b4ec6a6ceb0a
RNPermissions: bf844d392fe0ecbfbd2e4ae2b88cc32f2f09b369
RNReanimated: 58e7b950e0172235ff8296dd39ec145f9577e301
RNScreens: eb0dfb2d6b21d2d7f980ad46b14eb306d2f1062e
RNSoundLevel: 3dd5d2f6431e47f806233600936deb636a0d9800
RNShare: 2074af48908436c310847c6c47ebb6129db1dc89
RNSVG: 8ba35cbeb385a52fd960fd28db9d7d18b4c2974f
RNVectorIcons: f67a1abce2ec73e62fe4606e8110e95a832bc859
SDWebImage: a7f831e1a65eb5e285e3fb046a23fcfbf08e696d
@ -684,4 +705,4 @@ SPEC CHECKSUMS: @@ -684,4 +705,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 794df2878aa835db0bc03a96f2f040b5ffad064c
COCOAPODS: 1.11.2
COCOAPODS: 1.11.3

8
ios/taskme.xcodeproj/project.pbxproj

@ -764,7 +764,7 @@ @@ -764,7 +764,7 @@
CODE_SIGN_ENTITLEMENTS = taskme/taskme.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 91;
CURRENT_PROJECT_VERSION = 97;
DEVELOPMENT_TEAM = HQ3J3TDPR2;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
@ -802,7 +802,7 @@ @@ -802,7 +802,7 @@
CODE_SIGN_ENTITLEMENTS = taskme/taskme.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 91;
CURRENT_PROJECT_VERSION = 97;
DEVELOPMENT_TEAM = HQ3J3TDPR2;
ENABLE_BITCODE = NO;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
@ -863,7 +863,7 @@ @@ -863,7 +863,7 @@
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 ";
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@ -928,7 +928,7 @@ @@ -928,7 +928,7 @@
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 ";
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;

181
package-lock.json generated

@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
"@gorhom/bottom-sheet": "^4.1.5",
"@miblanchard/react-native-slider": "^2.1.0",
"@react-native-async-storage/async-storage": "^1.15.8",
"@react-native-community/cameraroll": "^4.1.2",
"@react-native-community/clipboard": "^1.5.1",
"@react-native-community/netinfo": "^6.2.1",
"@react-native-community/push-notification-ios": "^1.10.1",
@ -29,8 +30,11 @@ @@ -29,8 +30,11 @@
"ffmpeg-kit-react-native": "^4.5.2",
"jet-tools": "^1.1.0",
"lodash": "^4.17.21",
"mime": "^3.0.0",
"mime-types": "^2.1.35",
"moment": "^2.29.1",
"oa-react-native-clear-cache": "^1.0.3",
"path": "^0.12.7",
"react": "17.0.1",
"react-native": "^0.64.3",
"react-native-app-badge": "^0.1.5",
@ -72,6 +76,7 @@ @@ -72,6 +76,7 @@
"react-native-shadow": "^1.2.2",
"react-native-shadow-2": "^5.1.2",
"react-native-shadow-view": "^0.0.1",
"react-native-share": "^7.4.1",
"react-native-sound-level": "^1.1.5",
"react-native-splash-screen": "^3.2.0",
"react-native-svg": "9.13",
@ -2797,6 +2802,15 @@ @@ -2797,6 +2802,15 @@
"react-native": ">=0.59"
}
},
"node_modules/@react-native-community/cameraroll": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@react-native-community/cameraroll/-/cameraroll-4.1.2.tgz",
"integrity": "sha512-jkdhMByMKD2CZ/5MPeBieYn8vkCfC4MOTouPpBpps3I8N6HUYJk+1JnDdktVYl2WINnqXpQptDA2YptVyifYAg==",
"peerDependencies": {
"react": "16 || 17",
"react-native": ">=0.60"
}
},
"node_modules/@react-native-community/cli-debugger-ui": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-5.0.1.tgz",
@ -3856,25 +3870,6 @@ @@ -3856,25 +3870,6 @@
"node": ">= 0.6"
}
},
"node_modules/accepts/node_modules/mime-db": {
"version": "1.51.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
"integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/accepts/node_modules/mime-types": {
"version": "2.1.34",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
"integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
"dependencies": {
"mime-db": "1.51.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/acorn": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
@ -10462,30 +10457,30 @@ @@ -10462,30 +10457,30 @@
}
},
"node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
"integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
"node": ">=10.0.0"
}
},
"node_modules/mime-db": {
"version": "1.49.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
"integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==",
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.32",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz",
"integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==",
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
"mime-db": "1.49.0"
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
@ -11383,6 +11378,15 @@ @@ -11383,6 +11378,15 @@
"node": ">=0.10.0"
}
},
"node_modules/path": {
"version": "0.12.7",
"resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
"integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=",
"dependencies": {
"process": "^0.11.1",
"util": "^0.10.3"
}
},
"node_modules/path-dirname": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
@ -11690,6 +11694,14 @@ @@ -11690,6 +11694,14 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@ -12443,6 +12455,11 @@ @@ -12443,6 +12455,11 @@
"react-native-svg": "*"
}
},
"node_modules/react-native-share": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/react-native-share/-/react-native-share-7.4.1.tgz",
"integrity": "sha512-NFYkq9ytOP2kAAa33ZVOfBQLrpcBqwsNHsuDgU9DUcFpAlu2hYsjBy0MgLyJk8+uTzCBa5UCIlFFjZDwqFOKuw=="
},
"node_modules/react-native-sound-level": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/react-native-sound-level/-/react-native-sound-level-1.1.5.tgz",
@ -13551,6 +13568,17 @@ @@ -13551,6 +13568,17 @@
"node": ">= 0.8.0"
}
},
"node_modules/send/node_modules/mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
"bin": {
"mime": "cli.js"
},
"engines": {
"node": ">=4"
}
},
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -15212,6 +15240,14 @@ @@ -15212,6 +15240,14 @@
"resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz",
"integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ=="
},
"node_modules/util": {
"version": "0.10.4",
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
"integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
"dependencies": {
"inherits": "2.0.3"
}
},
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@ -15232,6 +15268,11 @@ @@ -15232,6 +15268,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/util/node_modules/inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
},
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@ -17580,6 +17621,12 @@ @@ -17580,6 +17621,12 @@
"deep-assign": "^3.0.0"
}
},
"@react-native-community/cameraroll": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@react-native-community/cameraroll/-/cameraroll-4.1.2.tgz",
"integrity": "sha512-jkdhMByMKD2CZ/5MPeBieYn8vkCfC4MOTouPpBpps3I8N6HUYJk+1JnDdktVYl2WINnqXpQptDA2YptVyifYAg==",
"requires": {}
},
"@react-native-community/cli-debugger-ui": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-5.0.1.tgz",
@ -18435,21 +18482,6 @@ @@ -18435,21 +18482,6 @@
"requires": {
"mime-types": "~2.1.34",
"negotiator": "0.6.3"
},
"dependencies": {
"mime-db": {
"version": "1.51.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
"integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="
},
"mime-types": {
"version": "2.1.34",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
"integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
"requires": {
"mime-db": "1.51.0"
}
}
}
},
"acorn": {
@ -23565,21 +23597,21 @@ @@ -23565,21 +23597,21 @@
}
},
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
"integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="
},
"mime-db": {
"version": "1.49.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz",
"integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA=="
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
},
"mime-types": {
"version": "2.1.32",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz",
"integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==",
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"requires": {
"mime-db": "1.49.0"
"mime-db": "1.52.0"
}
},
"mimic-fn": {
@ -24258,6 +24290,15 @@ @@ -24258,6 +24290,15 @@
"resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
"integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ="
},
"path": {
"version": "0.12.7",
"resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
"integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=",
"requires": {
"process": "^0.11.1",
"util": "^0.10.3"
}
},
"path-dirname": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
@ -24487,6 +24528,11 @@ @@ -24487,6 +24528,11 @@
}
}
},
"process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
"integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@ -25107,6 +25153,11 @@ @@ -25107,6 +25153,11 @@
"color-alpha": "^1.0.2"
}
},
"react-native-share": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/react-native-share/-/react-native-share-7.4.1.tgz",
"integrity": "sha512-NFYkq9ytOP2kAAa33ZVOfBQLrpcBqwsNHsuDgU9DUcFpAlu2hYsjBy0MgLyJk8+uTzCBa5UCIlFFjZDwqFOKuw=="
},
"react-native-sound-level": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/react-native-sound-level/-/react-native-sound-level-1.1.5.tgz",
@ -25936,6 +25987,11 @@ @@ -25936,6 +25987,11 @@
"statuses": "~1.5.0"
},
"dependencies": {
"mime": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@ -27252,6 +27308,21 @@ @@ -27252,6 +27308,21 @@
"resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz",
"integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ=="
},
"util": {
"version": "0.10.4",
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
"integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
"requires": {
"inherits": "2.0.3"
},
"dependencies": {
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
}
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",

5
package.json

@ -18,6 +18,7 @@ @@ -18,6 +18,7 @@
"@gorhom/bottom-sheet": "^4.1.5",
"@miblanchard/react-native-slider": "^2.1.0",
"@react-native-async-storage/async-storage": "^1.15.8",
"@react-native-community/cameraroll": "^4.1.2",
"@react-native-community/clipboard": "^1.5.1",
"@react-native-community/netinfo": "^6.2.1",
"@react-native-community/push-notification-ios": "^1.10.1",
@ -34,8 +35,11 @@ @@ -34,8 +35,11 @@
"ffmpeg-kit-react-native": "^4.5.2",
"jet-tools": "^1.1.0",
"lodash": "^4.17.21",
"mime": "^3.0.0",
"mime-types": "^2.1.35",
"moment": "^2.29.1",
"oa-react-native-clear-cache": "^1.0.3",
"path": "^0.12.7",
"react": "17.0.1",
"react-native": "^0.64.3",
"react-native-app-badge": "^0.1.5",
@ -77,6 +81,7 @@ @@ -77,6 +81,7 @@
"react-native-shadow": "^1.2.2",
"react-native-shadow-2": "^5.1.2",
"react-native-shadow-view": "^0.0.1",
"react-native-share": "^7.4.1",
"react-native-sound-level": "^1.1.5",
"react-native-splash-screen": "^3.2.0",
"react-native-svg": "9.13",

1
src/api/auth/requests.ts

@ -23,7 +23,6 @@ export const finishLogin = ( @@ -23,7 +23,6 @@ export const finishLogin = (
export const resetSessionReq = (
params: IRefreshTokenPayload,
): ApiResponse<IAuthSuccessResponse> => {
console.log('reset start')
return http.post<IAuthSuccessResponse>('auth/refresh-token', params)
}

10
src/api/tasks-documents/requests.ts

@ -1,13 +1,17 @@ @@ -1,13 +1,17 @@
import http from '../http.service'
import * as Req from './requests.interfaces'
import { prepareFormData } from '@/shared/helpers'
import { ITaskDocument } from '@/shared'
import { ApiResponse } from '../http.types'
import { ITaskDocument } from '@/shared'
export const getDocumentsByTaskIdReq = async (taskId: number): ApiResponse<ITaskDocument[]> => {
return http.get<ITaskDocument[]>(`tasks-documents/${taskId}`, {})
}
export const addTaskDocumentsReq = async (
params: Req.AddTaskDocumentsParams,
): ApiResponse<ITaskDocument[]> => {
return http.put<ITaskDocument[]>('tasks-documents', createFormData(params))
): ApiResponse<void> => {
return http.put<void>('tasks-documents', createFormData(params))
}
export const removeTaskDocumentsReq = async (

3
src/api/tasks/responses.interface.ts

@ -1,9 +1,9 @@ @@ -1,9 +1,9 @@
import {
IPaginationResult,
ITaskDocument,
ITaskExecutor,
TaskEvent,
TaskStatus,
ITaskDocument,
} from '@/shared'
export interface ITaskPreviewResponse {
@ -14,6 +14,7 @@ export interface ITaskPreviewResponse { @@ -14,6 +14,7 @@ export interface ITaskPreviewResponse {
description: string
isFavorite: boolean
hasUnreadComments: boolean
hasDocuments: boolean
events: Record<string, TaskEvent>[]
startDate: string
endDate: string

1
src/api/tasks/transform.ts

@ -105,6 +105,7 @@ export const transformTasksInList = ( @@ -105,6 +105,7 @@ export const transformTasksInList = (
executor: taskExecutor,
isFavorite: item.isFavorite,
hasUnreadComments: item.hasUnreadComments,
hasAttachments: item.hasDocuments,
events: item.events,
doneDate: item.doneDate,
}

53
src/modules/account/hooks/use-account-editor.hook.ts

@ -3,18 +3,19 @@ import { accountService } from '@/services/domain' @@ -3,18 +3,19 @@ import { accountService } from '@/services/domain'
import { appEvents, useForm } from '@/shared'
import { selectAccount } from '@/store/account'
import * as _ from 'lodash'
import moment from 'moment'
import { useCallback, useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import { validateEditAccount } from '../validations'
interface IForm {
firstName: string
lastName: string
firstName?: string
lastName?: string
middleName?: string
position: string
avatarUrl: string
phoneNumber: string
email: string
position?: string
avatarUrl?: string
phoneNumber?: string
email?: string
innerPhoneNumber?: string
personalPhoneNumber?: string
dateOfBirth?: string
@ -26,7 +27,6 @@ export const useAccountEditor = () => { @@ -26,7 +27,6 @@ export const useAccountEditor = () => {
const accountInfo = account.info
const [isLoading, setLoading] = useState(false)
const [isDeleteAvatar, setDeleteAvatar] = useState(false)
const form = useForm<IForm>(
{
firstName: ' ',
@ -56,7 +56,8 @@ export const useAccountEditor = () => { @@ -56,7 +56,8 @@ export const useAccountEditor = () => {
useEffect(init, [account.info])
const preSubmit = () =>
const preSubmit = () => {
existDataField()
form.onSubmit(() => {
appEvents.emit('openConfirmModal', {
title: 'Ви впевненні, що хочете зберегти зміни профілю',
@ -66,11 +67,33 @@ export const useAccountEditor = () => { @@ -66,11 +67,33 @@ export const useAccountEditor = () => {
submit()
}, 300)
},
notAllowBtnAction: () => {},
notAllowBtnAction: () => { },
})
})
}
const { dateOfBirth, firstName, lastName, middleName,
position, phoneNumber, personalPhoneNumber,
innerPhoneNumber, email, avatarUrl, avatar } = form.values
const existDataField = () => {
const prepareInfo: IForm = {}
prepareDataForForm().dateOfBirth === dateOfBirth ? null : prepareInfo.dateOfBirth = dateOfBirth
prepareDataForForm().firstName === firstName ? null : prepareInfo.firstName = firstName
prepareDataForForm().lastName === lastName ? null : prepareInfo.lastName = lastName
prepareDataForForm().middleName === middleName ? null : prepareInfo.middleName = middleName
prepareDataForForm().position === position ? null : prepareInfo.position = position
prepareDataForForm().personalPhoneNumber === personalPhoneNumber ? null : prepareInfo.personalPhoneNumber = personalPhoneNumber
prepareDataForForm().innerPhoneNumber === innerPhoneNumber ? null : prepareInfo.innerPhoneNumber = innerPhoneNumber
prepareDataForForm().email === email ? null : prepareInfo.email = email
prepareInfo.avatarUrl = avatarUrl
prepareInfo.avatar = avatar
prepareInfo.phoneNumber = phoneNumber
return prepareInfo
};
const submit = async () => {
setLoading(true)
try {
await updateAvatar()
@ -79,13 +102,14 @@ export const useAccountEditor = () => { @@ -79,13 +102,14 @@ export const useAccountEditor = () => {
appEvents.emit('openInfoModal', {
title: 'Успіх!',
message: 'Дані оновлені.',
onPressOk: () => {},
onPressOk: () => { },
})
} catch (e: any) {
console.log('errorData', e.response)
appEvents.emit('openInfoModal', {
title: 'Сталась помилка!',
message: 'Спробуйте будь-ласка пізніше.',
onPressOk: () => {},
onPressOk: () => { },
})
} finally {
setLoading(false)
@ -102,8 +126,11 @@ export const useAccountEditor = () => { @@ -102,8 +126,11 @@ export const useAccountEditor = () => {
}
const updateInfo = async () => {
if (_.isEqual(prepareDataForForm(), form.values)) return null
const { data } = await updateAccountReq(form.values)
let updateUserInfo = existDataField()
if (_.isEqual(prepareDataForForm(), form.values)) {
return null
}
const { data } = await updateAccountReq(updateUserInfo)
accountService.setAccount(data)
return data
}

6
src/modules/account/validations/edit-account.validator.ts

@ -2,18 +2,12 @@ import _ from 'lodash' @@ -2,18 +2,12 @@ import _ from 'lodash'
import moment from 'moment'
import validate from 'validate.js'
// Here is a sample implementation using moment.js
validate.extend(validate.validators.datetime, {
// The value is guaranteed not to be null or undefined but otherwise it
// could be anything.
parse: function (value, options) {
console.log('parse', value, +moment.utc(value))
return +moment.utc(value)
},
// Input is a unix timestamp
format: function (value, options) {
var format = options.dateOnly ? 'YYYY-MM-DD' : 'YYYY-MM-DD hh:mm:ss'
console.log('format', value, moment.utc(value).format(format))
return moment.utc(value).format(format)
},
})

129
src/modules/chats/components/chat-send-img-modal.compoennt.tsx

@ -1,85 +1,86 @@ @@ -1,85 +1,86 @@
import {
$size,
BottomModal,
Button,
useEventsListener,
useTheme,
$size,
BottomModal,
Button,
useEventsListener,
useTheme,
} from '@/shared'
import { PartialTheme } from '@/shared/themes/interfaces'
import React, { FC, useRef, useState } from 'react'
import { StyleSheet } from 'react-native'
import React, { FC, useEffect, useRef, useState } from 'react'
import { Image, StyleSheet } from 'react-native'
import RBSheet from 'react-native-raw-bottom-sheet'
import FastImage from 'react-native-fast-image'
import { fsService } from '@/services/system'
interface ChatSendImgModalSettings {
onSend: () => void
onSend: () => void
}
export const ChatSendImgModal: FC = () => {
const { styles } = useTheme(createStyles)
const { styles } = useTheme(createStyles)
const [imgUrl, setImgUrl] = useState('')
const [imgUrl, setImgUrl] = useState('')
const sheetRef = useRef<RBSheet>()
const settingsRef = useRef<ChatSendImgModalSettings>({
onSend: null,
})
const sheetRef = useRef<RBSheet>()
const settingsRef = useRef<ChatSendImgModalSettings>({
onSend: null,
})
useEventsListener(
'chatSendImgModal',
data => {
sheetRef.current.open()
try {
settingsRef.current.onSend = data.onSend
setImgUrl(data.url)
} catch (err) {
console.log('APP-EVENT chatSendImgModal ERR: ', err)
}
},
[sheetRef.current, settingsRef.current],
)
useEventsListener(
'chatSendImgModal',
data => {
sheetRef.current.open()
try {
settingsRef.current.onSend = data.onSend
setImgUrl(data.url)
} catch (err) {
console.log('APP-EVENT chatSendImgModal ERR: ', err)
}
},
[sheetRef.current, settingsRef.current],
)
const onSend = () => {
settingsRef.current.onSend()
sheetRef.current.close()
}
const onSend = () => {
settingsRef.current.onSend()
sheetRef.current.close()
}
const onCancel = () => {
sheetRef.current.close()
}
const onCancel = () => {
sheetRef.current.close()
}
return (
<BottomModal
sheetRef={ref => (sheetRef.current = ref)}
height={$size(400, 300)}
containerStyle={styles.container}>
<FastImage
source={{ uri: imgUrl }}
style={styles.img}
resizeMode="contain"
/>
return (
<BottomModal
sheetRef={ref => (sheetRef.current = ref)}
height={$size(400, 300)}
containerStyle={styles.container}>
<FastImage
source={{ uri: imgUrl }}
style={styles.img}
resizeMode="contain"
/>
<Button
title={'Надiслати фото'}
type={'border'}
style={styles.sendBtn}
onPress={onSend}
/>
<Button
title={'Надiслати фото'}
type={'border'}
style={styles.sendBtn}
onPress={onSend}
/>
<Button title={'Скасувати'} onPress={onCancel} />
</BottomModal>
)
<Button title={'Скасувати'} onPress={onCancel} />
</BottomModal>
)
}
const createStyles = (theme: PartialTheme) =>
StyleSheet.create({
container: {},
img: {
height: '60%',
width: '100%',
marginBottom: $size(15),
},
sendBtn: {
marginBottom: $size(15),
},
})
StyleSheet.create({
container: {},
img: {
height: '60%',
width: '100%',
marginBottom: $size(15),
},
sendBtn: {
marginBottom: $size(15),
},
})

36
src/modules/chats/configs/chat-card-buttons.config.ts

@ -24,7 +24,12 @@ interface configProps { @@ -24,7 +24,12 @@ interface configProps {
isChatUnread?: boolean
}
const rightBtn = ({ id, theme, onPress }: configProps): BtnsConfigT => {
const rightBtn = ({
id,
theme,
onPress,
afterPress,
}: configProps): BtnsConfigT => {
const { btns: colors } = theme.chats.rowCard
return [
@ -33,28 +38,40 @@ const rightBtn = ({ id, theme, onPress }: configProps): BtnsConfigT => { @@ -33,28 +38,40 @@ const rightBtn = ({ id, theme, onPress }: configProps): BtnsConfigT => {
title: 'Поділитись',
bgColor: colors.$shareBg,
txtColor: colors.$shareTxt,
onPress: () => onPress(ChatCardActionEnum.SHARE, id),
onPress: () => {
onPress(ChatCardActionEnum.SHARE, id)
afterPress()
},
},
{
iconName: 'bin',
title: 'Видалити',
bgColor: colors.$deleteBg,
txtColor: colors.$deleteTxt,
onPress: () => onPress(ChatCardActionEnum.DELETE, id),
onPress: () => {
onPress(ChatCardActionEnum.DELETE, id)
afterPress()
},
},
{
iconName: 'magicwand-1',
title: 'Очистити',
bgColor: colors.$clearBg,
txtColor: colors.$clearTxt,
onPress: () => onPress(ChatCardActionEnum.CLEAR, id),
onPress: () => {
onPress(ChatCardActionEnum.CLEAR, id)
afterPress()
},
},
{
iconName: 'gear-1',
title: 'Редагувати',
bgColor: colors.$editBg,
txtColor: colors.$editTxt,
onPress: () => onPress(ChatCardActionEnum.EDIT, id),
onPress: () => {
onPress(ChatCardActionEnum.EDIT, id)
afterPress()
},
},
]
}
@ -66,7 +83,7 @@ const leftBtn = ({ @@ -66,7 +83,7 @@ const leftBtn = ({
onPress,
afterPress,
isChatFixed,
isChatUnread
isChatUnread,
}: configProps): BtnsConfigT => {
const { btns: colors } = theme.chats.rowCard
@ -79,11 +96,14 @@ const leftBtn = ({ @@ -79,11 +96,14 @@ const leftBtn = ({
onPress: () => {
onPress(ChatCardActionEnum.PIN, id, { unreadMessagesCount })
afterPress()
}
},
},
{
iconName: 'checks-1',
title: isChatUnread || unreadMessagesCount > 0 ? 'Прочитано' : 'Не прочитано',
title:
isChatUnread || unreadMessagesCount > 0
? 'Прочитано'
: 'Не прочитано',
bgColor: colors.$readBg,
txtColor: colors.$readTxt,
onPress: () => {

54
src/modules/chats/hooks/use-chat-messages.hook.ts

@ -19,13 +19,15 @@ import { selectId } from '@/store/account' @@ -19,13 +19,15 @@ import { selectId } from '@/store/account'
import { UnselectChat } from '@/store/chats'
import { simpleDispatch } from '@/store/store-helpers'
import _ from 'lodash'
import { useState } from 'react'
import { Alert } from 'react-native'
import { useEffect, useState } from 'react'
import { useSelector } from 'react-redux'
import { getChatMessageMenuOptions } from '../configs'
import { ChatMessageActionEnum } from '../enums'
import Clipboard from '@react-native-community/clipboard'
import { mediaPermissionsService } from '@/services/system/media-permissions.service'
import mime from 'mime'
import { saveToCameraRoll } from '@/services/system/camera-roll.service'
import Share from 'react-native-share'
export const useChatMessages = (
chatId: number,
@ -129,17 +131,44 @@ export const useChatMessages = ( @@ -129,17 +131,44 @@ export const useChatMessages = (
const onDownload = async (message: IChatMessage) => {
await mediaPermissionsService.requestWriteExternalStorageAndroidPermissions()
appEvents.emit('openInfoModal', {
title: 'Обробка',
message: '',
isLoading: true,
pressButtonText: 'false',
onPressOk: () => {},
})
try {
const path = await fsService.downloadFile({
fileUrl: message.content?.fileUrl,
fileName: message.content?.name,
})
console.log('FILE DOWNLOADED!')
appEvents.emit('openInfoModal', {
title: 'Файл успішно завантажено!',
message: path,
onPressOk: () => {},
})
const fileUrl = message.content?.fileUrl
const mimetype = mime.getType(fileUrl.split('.').pop())
if (mimetype.includes('image')) {
await saveToCameraRoll(fileUrl, 'photo')
appEvents.emit('openInfoModal', {
title: 'Зображення успішно завантажено в галерею!',
message: '',
onPressOk: () => {},
})
} else if (mimetype.includes('video')) {
await saveToCameraRoll(fileUrl, 'video')
appEvents.emit('openInfoModal', {
title: 'Відео успішно завантажено в галерею!',
message: '',
onPressOk: () => {},
})
} else {
const path = await fsService.downloadToDevice({
fileUrl: message.content?.fileUrl,
fileName: message.content?.name,
})
appEvents.emit('closeInfoModal', {})
setTimeout(() => {
Share.open({
url: path,
})
}, 300)
}
} catch (e) {
appEvents.emit('openInfoModal', {
title: 'Сталась помилка!',
@ -334,7 +363,6 @@ export const useChatMessages = ( @@ -334,7 +363,6 @@ export const useChatMessages = (
}
const onChatIsRead = (data: { chatId: number; userId: number }) => {
console.log(Number(data?.chatId) !== chatId, data?.userId === accountId)
if (Number(data?.chatId) !== chatId || data?.userId === accountId)
return

26
src/modules/chats/hooks/use-chats-list.hook.ts

@ -6,7 +6,6 @@ import { @@ -6,7 +6,6 @@ import {
ChatType,
IChat,
IChatMessage,
isAndroid,
RouteKey,
useEventsListener,
useFlatList,
@ -30,9 +29,9 @@ import { @@ -30,9 +29,9 @@ import {
setChatUnreadReq,
unfixChatReq,
} from '@/api'
import RNFS from 'react-native-fs'
import { Buffer } from 'buffer'
import { fsService } from '@/services/system'
import Share from 'react-native-share'
export const useChatList = () => {
const nav = useNav()
@ -169,13 +168,15 @@ export const useChatList = () => { @@ -169,13 +168,15 @@ export const useChatList = () => {
items: [
{
name: 'Видалити у себе',
onPress: () =>
setTimeout(() => handleDeleteForMe(chatId), 300),
onPress: () => {
setTimeout(() => handleDeleteForMe(chatId), 300)
},
},
{
name: 'Видалити у всіх',
onPress: () =>
setTimeout(() => handleDeleteForAll(chatId), 300),
onPress: () => {
setTimeout(() => handleDeleteForAll(chatId), 300)
},
},
],
})
@ -287,11 +288,9 @@ export const useChatList = () => { @@ -287,11 +288,9 @@ export const useChatList = () => {
content: buffString,
fileName: data.fileName,
})
console.log('FILE WRITTEN!')
appEvents.emit('openInfoModal', {
title: 'Файл успішно завантажено!',
message: path,
onPressOk: () => {},
Share.open({
url: path,
})
} catch (e) {
appEvents.emit('openInfoModal', {
@ -399,10 +398,7 @@ export const useChatList = () => { @@ -399,10 +398,7 @@ export const useChatList = () => {
chats: chats as IChat[],
searchString,
loadMore,
resetFlatList: () => {
console.log('refresh')
resetFlatList()
},
resetFlatList: resetFlatList,
setSearchVal,
handleChatAction,
isLoading,

48
src/modules/chats/hooks/use-create-text-message.hook.ts

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
import { isAndroid } from './../../../shared/helpers/platform.helper'
import {
appEvents,
createFullName,
@ -15,6 +16,7 @@ import { ISendTextMessage } from '@/api/chat-messages/requests.interfaces' @@ -15,6 +16,7 @@ import { ISendTextMessage } from '@/api/chat-messages/requests.interfaces'
import { getLinksFromTxt, isImgUrl } from '@/shared/helpers'
import { fsService } from '@/services/system'
import { storeImageMessageReq } from '@/api'
import { Alert } from 'react-native'
interface IProps {
chatMembers: IChatMember[]
@ -48,6 +50,7 @@ export const useCreateTextMessage = ({ @@ -48,6 +50,7 @@ export const useCreateTextMessage = ({
message: transformed,
replyToId: replyToMessage?.id,
}
// якщо в повідомленні присутні звернення, то оригінальний текст з нетрансформованими
// зверненнями зберігається в окремому полі mentionsMessage
// саме це поле використовується для правильного відображення повідомлень зі
@ -66,24 +69,34 @@ export const useCreateTextMessage = ({ @@ -66,24 +69,34 @@ export const useCreateTextMessage = ({
const onSendCopyImg = async (fileUrl: string) => {
setSending(true)
const splitLink = fileUrl.split('/')
const fileName = splitLink[splitLink.length - 1].split('.')
const fileExt = fileName[fileName.length - 1]
const fileExt = fileUrl.split('.').pop()
try {
const filePath = await fsService.downloadFile({
fileUrl,
fileName: `${new Date().toISOString()}.${fileExt.toLowerCase()}`,
})
console.log('filePath', filePath)
const filePath = await fsService.downloadFile({
fileUrl,
fileName: 'copy-img.' + fileExt.toLowerCase(),
})
setTimeout(async () => {
const fileStat = await fsService.getFileStat(filePath)
fileStat.type = 'image/jpg'
const fileStat = await fsService.getFileStat(filePath)
console.log('file', fileStat)
await storeImageMessageReq({
file: fileStat,
chatId,
fileName: fileStat.name,
})
await storeImageMessageReq({
file: fileStat,
chatId,
fileName: fileStat.name,
})
await fsService.deleteFile(filePath)
}, 1000)
} catch (err) {
console.log({ err })
}
await fsService.deleteFile(filePath)
setSending(false)
}
@ -110,6 +123,7 @@ export const useCreateTextMessage = ({ @@ -110,6 +123,7 @@ export const useCreateTextMessage = ({
chatMembers,
member => !member?.isDeleted && member?.userId !== accountId,
)
const allItems = usersCanMention.map(it => {
const { firstName, lastName, avatarUrl } = it?.user
const name = createFullName(firstName, lastName)
@ -120,6 +134,12 @@ export const useCreateTextMessage = ({ @@ -120,6 +134,12 @@ export const useCreateTextMessage = ({
}
})
useEffect(() => {
setMessage(
'http://185.69.154.136:5003/rws/other/chats/23/2022/4/1653080560420.2022-05-20T23:49:15.384Z.jpg',
)
}, [])
return {
suggestionsItems: allItems,
message,

2
src/modules/chats/screens/chat.tsx

@ -152,7 +152,7 @@ export const ChatConversation = ({ navigation }: IProps) => { @@ -152,7 +152,7 @@ export const ChatConversation = ({ navigation }: IProps) => {
return chatDetails?.pinedMessages[currentPinedInd]?.type ===
MessageType.Forwarded
? chatDetails?.pinedMessages[currentPinedInd]?.content
?.originalMessage
?.originalMessage
: chatDetails?.pinedMessages[currentPinedInd]
}

2
src/modules/chats/transforms/chat-messages.transforms.ts

@ -70,7 +70,7 @@ const getDeleteMemberMessage = ( @@ -70,7 +70,7 @@ const getDeleteMemberMessage = (
return `${name} видалив ${deletedUserName} з чату`
}
return `${name} покинув чат`
return `${name} покидає чат`
}
const getNewChatMessage = (item: IMessageInChat) => {

2
src/modules/comments/components/comments-footer.tsx

@ -8,8 +8,6 @@ import { SendButtonComments } from '@/modules/comments/atom' @@ -8,8 +8,6 @@ import { SendButtonComments } from '@/modules/comments/atom'
export const NewCommentMessageInput = (props: any) => {
const { styles } = useTheme(createStyles)
console.log(props)
return (
<InputToolbar
{...props}

8
src/modules/comments/screens/comments.screen.tsx

@ -43,7 +43,13 @@ export const Comments: FC<IProps> = ({ navigation, route }) => { @@ -43,7 +43,13 @@ export const Comments: FC<IProps> = ({ navigation, route }) => {
return (
<CommentsLayout title="Коментарі" goBack={navigation.goBack}>
<View style={{ flex: 1 }}>
<ChatMessages items={comments} userId={accountId} />
<ChatMessages
items={comments}
userId={accountId}
loadPrev={() => {}}
loadNext={() => {}}
lastMessageLoaded={true}
/>
</View>
<ChatBar
message={commentText}

13
src/modules/contacts/hooks/use-fetch-contacts.hook.ts

@ -4,7 +4,7 @@ import { IContact, useFlatList } from '@/shared' @@ -4,7 +4,7 @@ import { IContact, useFlatList } from '@/shared'
import { contactsService } from '@/services/domain'
export const useFetchContacts = () => {
const [searchString, setSearchVal] = useState<string>()
const [searchString, setSearchVal] = useState<string>(null)
const {
items: contacts,
@ -29,21 +29,14 @@ export const useFetchContacts = () => { @@ -29,21 +29,14 @@ export const useFetchContacts = () => {
loadParams: {
sort: 'ASC',
sortField: 'firstName',
searchString,
soonBirthday: false,
},
})
const setSearchString = (value: string) => {
setLoadParams({ searchString: value })
}
const setBirthday = value => {
setLoadParams({ soonBirthday: value })
}
const setBirthday = value => setLoadParams({ soonBirthday: value })
useEffect(() => {
setSearchString(searchString)
if (searchString !== null) setLoadParams({ searchString })
}, [searchString])
return {

2
src/modules/groups/components/groups-list.component.tsx

@ -32,7 +32,7 @@ export const GroupsList: FC<IProps> = ({ data, navigate }) => { @@ -32,7 +32,7 @@ export const GroupsList: FC<IProps> = ({ data, navigate }) => {
navigate(RouteKey.GroupTasks, { groupData: item })
}
onPressInfo={() =>
console.log('i press info', item.title)
navigate(RouteKey.GroupTasks, { groupData: item })
}
/>
)}

7
src/modules/root/index.tsx

@ -63,7 +63,6 @@ export const Navigation: FC = () => { @@ -63,7 +63,6 @@ export const Navigation: FC = () => {
}
const internetConnectionHandler = () => {
console.log('isConnected', isConnected)
try {
if (!_.isBoolean(isConnected)) return
@ -78,7 +77,6 @@ export const Navigation: FC = () => { @@ -78,7 +77,6 @@ export const Navigation: FC = () => {
NavigationService.setModule(prevActiveModule)
} else handleAutoAuth()
} finally {
console.log('Init')
SplashScreen.hide()
}
}
@ -96,10 +94,7 @@ export const Navigation: FC = () => { @@ -96,10 +94,7 @@ export const Navigation: FC = () => {
[NavigationModuleKey.User]: <UsersGroup />,
}
const navigation = useMemo(() => {
console.log('ACTIVE MODULE', activeModule)
return modules[activeModule]
}, [activeModule])
const navigation = useMemo(() => modules[activeModule], [activeModule])
const GlobalComponents = useMemo(() => {
if (activeModule === NavigationModuleKey.User) {

46
src/modules/root/smart-components/info-modal.smart-component.tsx

@ -1,9 +1,16 @@ @@ -1,9 +1,16 @@
import { $size, BottomModal, Button, Txt, useEventsListener } from '@/shared'
import {
$size,
BottomModal,
Button,
Loading,
Txt,
useEventsListener,
} from '@/shared'
import { useTheme } from '@/shared/hooks/use-theme.hook'
import { PartialTheme } from '@/shared/themes/interfaces'
import * as _ from 'lodash'
import React, { FC, useRef, useState } from 'react'
import { StyleSheet } from 'react-native'
import { ActivityIndicator, StyleSheet } from 'react-native'
import RBSheet from 'react-native-raw-bottom-sheet'
export const InfoModalSmartComponent: FC = () => {
@ -16,6 +23,7 @@ export const InfoModalSmartComponent: FC = () => { @@ -16,6 +23,7 @@ export const InfoModalSmartComponent: FC = () => {
const [title, setTitle] = useState<string>('')
const [message, setMessage] = useState<string>('')
const [buttonText, setButtonText] = useState('Готово')
const [isLoading, setLoading] = useState(false)
useEventsListener(
'openInfoModal',
@ -28,6 +36,7 @@ export const InfoModalSmartComponent: FC = () => { @@ -28,6 +36,7 @@ export const InfoModalSmartComponent: FC = () => {
setTitle(data.title)
setMessage(data.message)
setButtonText(_.defaultTo(data.pressButtonText, 'Готово'))
setLoading(_.defaultTo(data.isLoading, false))
} catch (e) {
console.log(e)
}
@ -35,6 +44,22 @@ export const InfoModalSmartComponent: FC = () => { @@ -35,6 +44,22 @@ export const InfoModalSmartComponent: FC = () => {
[sheetRef.current, setTitle, setMessage, settingsRef.current],
)
useEventsListener(
'closeInfoModal',
() => {
try {
sheetRef.current.close()
setTimeout(() => {
setTitle(null)
setMessage(null)
setButtonText(null)
setLoading(false)
}, 300)
} catch (e) {}
},
[sheetRef.current, setTitle, setMessage, settingsRef.current],
)
return (
<BottomModal
sheetRef={ref => (sheetRef.current = ref)}
@ -42,14 +67,17 @@ export const InfoModalSmartComponent: FC = () => { @@ -42,14 +67,17 @@ export const InfoModalSmartComponent: FC = () => {
closeOnPressMask={true}
onClose={settingsRef?.current?.onPressOk}
title={title}>
{isLoading ? <Loading /> : null}
<Txt style={styles.message}>{message}</Txt>
<Button
title={buttonText}
onPress={() => {
sheetRef.current.close()
settingsRef?.current?.onPressOk()
}}
/>
{buttonText === 'false' ? null : (
<Button
title={buttonText}
onPress={() => {
sheetRef.current.close()
settingsRef?.current?.onPressOk()
}}
/>
)}
</BottomModal>
)
}

1
src/modules/root/smart-components/modal-picker.smart-component.tsx

@ -27,7 +27,6 @@ export const ModalPickerSmartComponent: FC = () => { @@ -27,7 +27,6 @@ export const ModalPickerSmartComponent: FC = () => {
'openPickerModal',
data => {
try {
console.log('openPickerModal')
sheetRef.current.open()
settingsRef.current = {
callback: data.callback,

5
src/modules/tasks/components/task-card.component.tsx

@ -19,7 +19,7 @@ interface IProps { @@ -19,7 +19,7 @@ interface IProps {
isChecked?: boolean
wrapperStyles?: any
noIcons?: boolean
hasAttachments?: boolean
onPress?: () => void
onLongPress?: () => void
onSelect?: () => void
@ -36,6 +36,7 @@ export const TaskCard: FC<IProps> = ({ @@ -36,6 +36,7 @@ export const TaskCard: FC<IProps> = ({
isRead,
isFavorite,
hasUnreadComments,
hasAttachments,
onSelect,
wrapperStyles,
noIcons,
@ -46,7 +47,7 @@ export const TaskCard: FC<IProps> = ({ @@ -46,7 +47,7 @@ export const TaskCard: FC<IProps> = ({
const icons = noIcons
? []
: getTaskCardIcons({ isFavorite, hasUnreadComments })
: getTaskCardIcons({ isFavorite, hasUnreadComments, hasAttachments })
return (
<TouchableOpacity

20
src/modules/tasks/components/task-details-attachments-collapse.component.tsx

@ -1,15 +1,29 @@ @@ -1,15 +1,29 @@
import { Accordion } from '@/shared'
import React, { FC, useState } from 'react'
import { TaskDetailsAttachmentsList, TaskDetailsAttachmentsListProps } from './task-details-attachments-list.component'
import { TextStyle } from 'react-native'
import {
TaskDetailsAttachmentsList,
TaskDetailsAttachmentsListProps,
} from './task-details-attachments-list.component'
interface IProps extends TaskDetailsAttachmentsListProps { }
interface IProps extends TaskDetailsAttachmentsListProps {
counter?: number
titleStyle?: TextStyle
}
export const TaskDetailsAttachmentsCollapse: FC<IProps> = ({ allowDelete = false, ...props }) => {
export const TaskDetailsAttachmentsCollapse: FC<IProps> = ({
titleStyle,
counter,
allowDelete = false,
...props
}) => {
const [isExpected, setExpected] = useState<boolean>()
return (
<Accordion
isExpanded={isExpected}
titleStyle={titleStyle}
counter={counter}
headerTitle={'Вкладення'}
onToggle={() => setExpected(!isExpected)}>
<TaskDetailsAttachmentsList {...props} allowDelete={allowDelete} />

7
src/modules/tasks/components/task-details-attachments-list.component.tsx

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
import { $size, IconComponent, ITaskDocument, Txt, useTheme } from '@/shared'
import { $size, IconComponent, Txt, useTheme } from '@/shared'
import { PartialTheme } from '@/shared/themes/interfaces'
import React, { FC } from 'react'
import {
@ -8,9 +8,10 @@ import { @@ -8,9 +8,10 @@ import {
TouchableOpacity,
} from 'react-native'
import { DocumentPreview } from '../atoms'
import { ShortTaskDocument } from '../interfaces'
export interface TaskDetailsAttachmentsListProps {
attachments: ITaskDocument[]
attachments: ShortTaskDocument[]
allowDelete?: boolean
onAddFile: () => void
onDeleteFile: (documentId: number) => void
@ -23,7 +24,7 @@ export const TaskDetailsAttachmentsList: FC<TaskDetailsAttachmentsListProps> = @@ -23,7 +24,7 @@ export const TaskDetailsAttachmentsList: FC<TaskDetailsAttachmentsListProps> =
const renderItem = ({
item,
index: i,
}: ListRenderItemInfo<ITaskDocument>) => {
}: ListRenderItemInfo<ShortTaskDocument>) => {
return (
<TouchableOpacity
key={`${item}---${i}`}

184
src/modules/tasks/config/task-attachments-menu.config.ts

@ -1,87 +1,127 @@ @@ -1,87 +1,127 @@
import { TaskAttachmentsActionMode } from './../enum/task-attachments.enums'
import {
alertFileSizeExceeded,
checkFileSize,
MAX_FILE_SIZE,
} from '@/shared/helpers'
import { taskDocumentsService } from '@/services/domain'
import { mediaService } from '@/services/system'
import _ from 'lodash'
import { ITaskDocument } from '@/shared'
export const getAttachmentsMenusOptions = (params: { taskId: number, setAttachments: (docs: ITaskDocument[]) => void }) => ({
camera: {
name: 'Фото з камери',
onPress: () => {
setTimeout(async () => {
const photo = await mediaService.openCamera()
if (photo.size > MAX_FILE_SIZE) {
alertFileSizeExceeded([photo])
return
}
const docs = await taskDocumentsService.addTaskDocuments({
taskId: params.taskId,
files: [photo],
})
params.setAttachments(docs)
}, 200)
},
},
gallery: {
name: 'Фото з галереї',
onPress: () => {
setTimeout(async () => {
const images = await mediaService.openMultiplePicker()
const { allowed, exceeded } = checkFileSize(images)
if (!_.isEmpty(exceeded)) alertFileSizeExceeded(exceeded)
if (_.isEmpty(allowed)) return
const docs = await taskDocumentsService.addTaskDocuments({
taskId: params.taskId,
files: images,
})
import { appEvents, IFile } from '@/shared'
import { SetStateAction } from 'react'
import { taskDocumentsService } from '@/services/domain'
params.setAttachments(docs)
}, 200)
export const getAttachmentsMenusOptions = (params: {
taskId: number
setFiles: (docs: SetStateAction<Array<IFile>>) => void
mode: TaskAttachmentsActionMode
}) =>
Object.values({
camera: {
name: 'Фото з камери',
onPress: () => {
setTimeout(async () => {
const photo = await mediaService.openCamera()
if (photo.size > MAX_FILE_SIZE) {
alertFileSizeExceeded([photo])
return
}
if (params.mode === TaskAttachmentsActionMode.DETAIL) {
await taskDocumentsService.add({
taskId: params.taskId,
files: [photo],
})
appEvents.emit('reloadTaskDocs', {
taskId: params.taskId,
})
return
}
params.setFiles(prevState => [
...prevState,
{ ...photo, id: Math.random() },
])
}, 200)
},
},
},
file: {
name: 'Файл',
onPress: () => {
setTimeout(async () => {
try {
const files = await mediaService.openFilesPicker()
gallery: {
name: 'Фото з галереї',
onPress: () => {
setTimeout(async () => {
const images = await mediaService.openMultiplePicker()
if (_.isEmpty(files)) return
const preparedFiles = files.map(it => ({
name: it.name,
size: it.size,
type: it.type,
uri: it.uri,
}))
const { allowed, exceeded } = checkFileSize(preparedFiles)
const { allowed, exceeded } = checkFileSize(images)
if (!_.isEmpty(exceeded)) alertFileSizeExceeded(exceeded)
if (_.isEmpty(allowed)) return
const docs = await taskDocumentsService.addTaskDocuments({
taskId: params.taskId,
files: allowed,
})
params.setAttachments(docs)
} catch (err) {
console.log({ 'mediaService.openFilesPicker': err })
}
}, 200)
if (params.mode === TaskAttachmentsActionMode.DETAIL) {
await taskDocumentsService.add({
taskId: params.taskId,
files: allowed,
})
appEvents.emit('reloadTaskDocs', {
taskId: params.taskId,
})
return
}
params.setFiles(prevState => [
...prevState,
...images.map(it => ({ ...it, id: Math.random() })),
])
}, 200)
},
},
file: {
name: 'Файл',
onPress: () => {
setTimeout(async () => {
try {
const files = await mediaService.openFilesPicker()
if (_.isEmpty(files)) return
const preparedFiles = files.map(it => ({
name: it.name,
size: it.size,
type: it.type,
uri: it.uri,
}))
const { allowed, exceeded } =
checkFileSize(preparedFiles)
if (!_.isEmpty(exceeded))
alertFileSizeExceeded(exceeded)
if (_.isEmpty(allowed)) return
if (params.mode === TaskAttachmentsActionMode.DETAIL) {
await taskDocumentsService.add({
taskId: params.taskId,
files: allowed,
})
appEvents.emit('reloadTaskDocs', {
taskId: params.taskId,
})
return
}
params.setFiles(prevState => [
...prevState,
...allowed.map(it => ({
...it,
id: Math.random(),
})),
])
} catch (err) {
console.log({ 'mediaService.openFilesPicker': err })
}
}, 200)
},
},
},
})
})

6
src/modules/tasks/config/task-card-buttons.config.ts

@ -1,10 +1,10 @@ @@ -1,10 +1,10 @@
import {
appEvents,
FullTaskActions,
getTheme,
ITaskActionPayload,
TaskStatus,
} from '@/shared'
import { getTheme } from './../../../shared/themes/index'
export const getTaskCardButtons = (
id: number,
@ -27,7 +27,7 @@ export const getTaskCardButtons = ( @@ -27,7 +27,7 @@ export const getTaskCardButtons = (
allowBtnAction: () => {
setTimeout(() => onPress(FullTaskActions.Destroy, {}), 300)
},
notAllowBtnAction: () => {},
notAllowBtnAction: () => { },
})
},
}
@ -51,7 +51,7 @@ export const getTaskCardButtons = ( @@ -51,7 +51,7 @@ export const getTaskCardButtons = (
else onPress(FullTaskActions.AddFavorite, {})
}, 500)
},
notAllowBtnAction: () => {},
notAllowBtnAction: () => { },
})
},
},

3
src/modules/tasks/config/task-card-icons.config.ts

@ -3,13 +3,14 @@ import { $size } from '@/shared' @@ -3,13 +3,14 @@ import { $size } from '@/shared'
export interface IConfigData {
isFavorite?: boolean
hasUnreadComments?: boolean
hasAttachments?: boolean
}
export const getTaskCardIcons = (data?: IConfigData) => {
return [
{
icon: 'paperclip-1',
showNewIndicator: true,
showNewIndicator: data?.hasAttachments,
},
{
icon: 'chat-2',

8
src/modules/tasks/consts/add-update-task.consts.ts

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
import { TaskAttachmentsActionMode, TaskEditMode } from '../enum'
export const addUpdateTaskAttachmentsMode: Partial<
Record<TaskEditMode, TaskAttachmentsActionMode>
> = {
[TaskEditMode.Create]: TaskAttachmentsActionMode.CREATE,
[TaskEditMode.Update]: TaskAttachmentsActionMode.UPDATE,
}

3
src/modules/tasks/consts/index.ts

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
export * from './task-status-title-relation'
export * from './task-action-permission-relation'
export * from './route-key-edit-mode-relation'
export * from './route-key-edit-mode-relation'
export * from './add-update-task.consts'

2
src/modules/tasks/enum/index.ts

@ -1,4 +1,6 @@ @@ -1,4 +1,6 @@
export * from './task-filter.enums'
export * from './task-attachments.enums'
export enum CreatingTaskErrors {
FulfillAllData = 'Заповніть усі дані',
WrongDate = 'Кінцева дата не може бути меншою, ніж початкова',

5
src/modules/tasks/enum/task-attachments.enums.ts

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
export enum TaskAttachmentsActionMode {
UPDATE = 'u',
CREATE = 'c',
DETAIL = 'd'
}

5
src/modules/tasks/hooks/index.ts

@ -1,9 +1,6 @@ @@ -1,9 +1,6 @@
export * from './use-task-details.hook'
export * from './use-fetch-task-executors.hook'
export * from './use-task-selector.hook'
export * from './use-fetch-user-tasks.hook'
export * from './use-fetch-my-tasks.hook'
export * from './use-fetch-group-tasks.hook'
export * from './use-task-actions.hook'
export * from './use-reasons.hook'
export * from './use-task-filter.hook'
@ -11,4 +8,4 @@ export * from './use-create-update-task.hook' @@ -11,4 +8,4 @@ export * from './use-create-update-task.hook'
export * from './use-change-task-executor.hook'
export * from './use-fetch-tasks-by-filter.hook'
export * from './use-task-list-events.hook'
export * from './use-select-task-executor.hook'
export * from './use-select-task-executor.hook'

42
src/modules/tasks/hooks/use-fetch-group-tasks.hook.ts

@ -1,42 +0,0 @@ @@ -1,42 +0,0 @@
import { ITaskPreview, useFlatList } from '@/shared'
import { tasksService } from '@/services/domain'
import { useEffect } from 'react'
import { ITaskPreviewResponse } from '@/api/tasks/responses.interface'
import { transformTasksInList } from '@/api/tasks/transform'
import { useTaskListEvents } from './use-task-list-events.hook'
export const useFetchGroupTasks = (groupId: number) => {
const {
items,
isLoading,
isLoadingNext,
loadMore,
resetFlatList,
_setItems,
} = useFlatList({
fetchItems: params => tasksService.getGroupTasks(groupId, params),
serrializatorItems: (items: ITaskPreviewResponse[]) =>
transformTasksInList(items),
needInit: true,
loadParams: {
sort: 'DESC',
sortField: 'id',
},
limit: 10,
})
useEffect(() => resetFlatList(), [groupId])
useTaskListEvents({
items,
onReload: () => resetFlatList(),
setItems: (items: ITaskPreview[]) => _setItems(items),
})
return {
items,
isLoading,
isLoadingNext,
loadMore,
}
}

53
src/modules/tasks/hooks/use-fetch-my-tasks.hook.ts

@ -1,53 +0,0 @@ @@ -1,53 +0,0 @@
import { createFullName, ITaskPreview, useFlatList } from '@/shared'
import { tasksService } from '@/services/domain'
import { useEffect } from 'react'
import { useSelector } from 'react-redux'
import { selectAccount } from '@/store/account'
import _ from 'lodash'
import { transformTasksInList } from '@/api/tasks/transform'
import { ITaskPreviewResponse } from '@/api/tasks/responses.interface'
import { useTaskListEvents } from './use-task-list-events.hook'
export const useFetchMyTasks = (
loadParams: { [key: string]: string | boolean },
limit: number,
) => {
const account = useSelector(selectAccount)
const shortAccount = {
id: account.id,
fullName: createFullName(account.info.firstName, account.info.lastName),
avatarUrl: account.info.avatarUrl,
}
const {
items: myTasks,
isLoading,
isLoadingNext,
loadMore,
resetFlatList,
_setItems,
} = useFlatList({
fetchItems: params => tasksService.getMyTasks(params),
serrializatorItems: (items: ITaskPreviewResponse[]) =>
transformTasksInList(items, shortAccount),
needInit: true,
loadParams,
limit,
})
useEffect(() => resetFlatList(), [])
useTaskListEvents({
items: myTasks,
onReload: () => resetFlatList(),
setItems: (items: ITaskPreview[]) => _setItems(items),
})
return {
myTasks,
isLoading,
isLoadingNext,
loadMore,
reloadTasks: resetFlatList
}
}

4
src/modules/tasks/hooks/use-fetch-tasks-by-filter.hook.ts

@ -52,14 +52,13 @@ export const useFetchTasksByFilter = ({ @@ -52,14 +52,13 @@ export const useFetchTasksByFilter = ({
const {
items,
loadParams,
isLoading,
isLoadingNext,
loadMore,
setLoadParams,
resetFlatList,
_setItems,
count
count,
} = useFlatList<ITaskPreview>({
fetchItems: params => tasksService.getTasks(params),
serrializatorItems: (items: ITaskPreviewResponse[]) =>
@ -91,6 +90,7 @@ export const useFetchTasksByFilter = ({ @@ -91,6 +90,7 @@ export const useFetchTasksByFilter = ({
onReload: () => resetFlatList(),
setItems: (items: ITaskPreview[]) => _setItems(items),
onAction,
taskFilterStatus: filter?.status,
})
return {

50
src/modules/tasks/hooks/use-fetch-user-tasks.hook.ts

@ -1,50 +0,0 @@ @@ -1,50 +0,0 @@
import { ITaskPreview, useFlatList } from '@/shared'
import { tasksService } from '@/services/domain'
import { useEffect } from 'react'
import { ITaskPreviewResponse } from '@/api/tasks/responses.interface'
import { transformTasksInList } from '@/api/tasks/transform'
import { useTaskListEvents } from './use-task-list-events.hook'
export const useFetchUserTasks = (user: {
id: number
fullName: string
avatarUrl: string
}) => {
const {
items,
isLoading,
isLoadingNext,
loadMore,
resetFlatList,
_setItems,
} = useFlatList({
fetchItems: params => tasksService.getUserTasks(user.id, params),
serrializatorItems: (items: ITaskPreviewResponse[]) =>
transformTasksInList(items, {
id: user.id,
fullName: user.fullName,
avatarUrl: user.avatarUrl,
}),
needInit: true,
loadParams: {
sort: 'DESC',
sortField: 'id',
},
limit: 10,
})
useTaskListEvents({
items,
onReload: () => resetFlatList(),
setItems: (items: ITaskPreview[]) => _setItems(items),
})
useEffect(() => resetFlatList(), [user.id])
return {
items,
isLoading,
isLoadingNext,
loadMore,
}
}

6
src/modules/tasks/hooks/use-task-actions.hook.ts

@ -1,20 +1,17 @@ @@ -1,20 +1,17 @@
import { removeTasksReq } from '@/api'
import { permissionsService, tasksService } from '@/services/domain'
import {
appEvents,
EUserRole,
FullTaskActions,
ITaskActionPayload,
ITaskDetails,
ITaskPreview,
RouteKey,
TaskStatus,
useNav,
} from '@/shared'
import { selectId, selectRole } from '@/store/account'
import { simpleDispatch } from '@/store/store-helpers'
import { selectSelectedTasks, UnselectAll } from '@/store/tasks'
import _ from 'lodash'
import { useState } from 'react'
import { useSelector } from 'react-redux'
import { TaskActionErrors } from '../enum'
import { useTasksPrint } from './use-tasks-print.hook'
@ -30,7 +27,6 @@ export const useTaskActions = () => { @@ -30,7 +27,6 @@ export const useTaskActions = () => {
const selectedTasks = useSelector(selectSelectedTasks)
const userRole = useSelector(selectRole)
const userId = useSelector(selectId)
const nav = useNav()
const { print, pdf } = useTasksPrint()
// const [error, setError] = useState<TaskActionErrors | string>(null)

7
src/modules/tasks/hooks/use-task-details.hook.ts

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import { IFullTaskInfo, useEventsListener } from '@/shared'
import { tasksService } from '@/services/domain'
import { taskDocumentsService, tasksService } from '@/services/domain'
import { useCallback, useEffect, useState } from 'react'
import _ from 'lodash'
@ -31,6 +31,11 @@ export const useTaskDetails = (id: number) => { @@ -31,6 +31,11 @@ export const useTaskDetails = (id: number) => {
[id, reloadTask],
)
useEventsListener('reloadTaskDocs', async ({ taskId }) => {
const docs = await taskDocumentsService.getByTaskId(taskId)
setTask(prev => ({ ...prev, documents: docs }))
})
useEffect(() => {
if (id) fetchTaskDetails()
}, [])

9
src/modules/tasks/hooks/use-task-filter.hook.ts

@ -64,19 +64,16 @@ export const useTaskFilter = ({ onSubmit, ...props }: IProps) => { @@ -64,19 +64,16 @@ export const useTaskFilter = ({ onSubmit, ...props }: IProps) => {
},
type: 'taskExecutors',
selectedOption: values?.executor,
title: 'Виконавець'
title: 'Виконавець',
})
}
const openSelectCreatorModal = () => {
appEvents.emit('openUserPicker', {
callback: user => {
console.log({ user })
setFormField('creator', user)
},
callback: user => setFormField('creator', user),
type: 'contacts',
selectedOption: values?.creator,
title: 'Хто створив'
title: 'Хто створив',
})
}

27
src/modules/tasks/hooks/use-task-list-events.hook.ts

@ -1,11 +1,17 @@ @@ -1,11 +1,17 @@
import { ITaskPreview, useEventsListener, useSocketListener } from '@/shared'
import {
ITaskPreview,
TaskStatus,
useEventsListener,
useSocketListener,
} from '@/shared'
import _ from 'lodash'
export interface IProps {
interface IProps {
items: ITaskPreview[]
onReload: () => void
setItems: (items: ITaskPreview[]) => void
onAction?: (key: string) => void
taskFilterStatus?: TaskStatus
}
export const useTaskListEvents = (props: IProps) => {
@ -15,12 +21,17 @@ export const useTaskListEvents = (props: IProps) => { @@ -15,12 +21,17 @@ export const useTaskListEvents = (props: IProps) => {
const onTaskDeleted = (data: { ids: number[] }) => {
if (!checkListContainAnyItem(data.ids)) return
const filteredItems = _.filter(
props.items,
item => !_.includes(data.ids, item.id),
)
props.setItems(filteredItems)
props.onAction('delete')
if (
props.taskFilterStatus === TaskStatus.Active ||
props.taskFilterStatus === TaskStatus.Finished
) {
const filteredItems = _.filter(
props.items,
item => !_.includes(data.ids, item.id),
)
props.setItems(filteredItems)
props.onAction('delete')
}
}
const onTaskChanged = (data: { ids: number[] }) => {

6
src/modules/tasks/hooks/use-tasks-print.hook.ts

@ -16,7 +16,6 @@ export const useTasksPrint = () => { @@ -16,7 +16,6 @@ export const useTasksPrint = () => {
await RNPrint.print({
html: html.join(' '),
})
console.log('PRINT END')
}
const pdf = async (tasksIds: number[]) => {
@ -29,7 +28,6 @@ export const useTasksPrint = () => { @@ -29,7 +28,6 @@ export const useTasksPrint = () => {
fileName: tasksIds.join('-'),
base64: false,
})
console.log('result', result)
RNFetchBlob.ios.openDocument(result.filePath)
// Linking.openURL(result.filePath)
@ -57,10 +55,10 @@ export const useTasksPrint = () => { @@ -57,10 +55,10 @@ export const useTasksPrint = () => {
<td style="padding-right: 50px" ><strong>${
task.id
}</strong></td>
<td style="padding-right: 50px"><strong>Початок</strong> ${moment(
<td style="padding-right: 50px"><strong>Початок:</strong> ${moment(
new Date(task.startDate),
).format('DD-MM-YYYY')}</td>
<td><strong>Закінчення</strong>${moment(
<td><strong>Закінчення:</strong>${moment(
new Date(task.endDate),
).format('DD-MM-YYYY')}</td>
</tr>

2
src/modules/tasks/interfaces/index.ts

@ -27,3 +27,5 @@ export interface ITermsConfig { @@ -27,3 +27,5 @@ export interface ITermsConfig {
needIcon: boolean
disabled: boolean
}
export * from './task-attachments.interfaces'

5
src/modules/tasks/interfaces/task-attachments.interfaces.ts

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
import { ITaskDocument } from "@/shared";
export interface ShortTaskDocument extends Pick<ITaskDocument, 'id' | 'url'> {
id: number
}

57
src/modules/tasks/screens/add-update-task.screen.tsx

@ -1,10 +1,11 @@ @@ -1,10 +1,11 @@
import React, { FC, useEffect, useMemo } from 'react'
import React, { FC, useEffect, useMemo, useState } from 'react'
import { Keyboard, StyleSheet } from 'react-native'
import { useSelector } from 'react-redux'
import {
$size,
appEvents,
Button,
IFile,
IRouteParams,
RouteKey,
ScreenLayout,
@ -23,9 +24,13 @@ import { @@ -23,9 +24,13 @@ import {
import { useCreateUpdateTask, useReasons } from '../hooks'
import {
SelectTaskExecutorListSmart,
TaskAttachMentsCollapseSmart,
TaskAttachmentsCollapseSmart,
} from '../smart-components'
import { RouteKeyEditModeRelation, taskStatusTitleRelation } from '../consts'
import {
addUpdateTaskAttachmentsMode,
RouteKeyEditModeRelation,
taskStatusTitleRelation,
} from '../consts'
import { TaskEditMode } from '../enum'
import { getTaskTerms } from '../config'
import { ICreateUpdateTaskValues } from '../interfaces'
@ -33,6 +38,7 @@ import moment from 'moment' @@ -33,6 +38,7 @@ import moment from 'moment'
import _ from 'lodash'
import { simpleDispatch } from '@/store/store-helpers'
import { UnselectAllUsers, UnselectExecutor } from '@/store/users'
import { taskDocumentsService } from '@/services/domain'
interface IProps extends IRouteParams {
taskId?: number
@ -45,6 +51,11 @@ export const AddUpdateTaskScreen: FC<IProps> = ({ route, navigation }) => { @@ -45,6 +51,11 @@ export const AddUpdateTaskScreen: FC<IProps> = ({ route, navigation }) => {
const groupId = routeParams?.groupId
const groups = useSelector(selectGroupTaxonomies)
const [attachmentsToAdd, setAttachmentsToAdd] = useState<IFile[]>([])
const [attachmentsIdsToRemove, setAttachmentsIdsToRemove] = useState<
number[]
>([])
useSelectedExecutor()
const { values, errors, isLoading, setFormField, onSubmit, task } =
@ -80,6 +91,14 @@ export const AddUpdateTaskScreen: FC<IProps> = ({ route, navigation }) => { @@ -80,6 +91,14 @@ export const AddUpdateTaskScreen: FC<IProps> = ({ route, navigation }) => {
if (values.activeReason) selectReason(values.activeReason)
}, [values.activeReason])
const onTackAttachmentsChange = (
filesToAdd: IFile[],
docsIdsToRemove: number[],
) => {
setAttachmentsToAdd(filesToAdd)
setAttachmentsIdsToRemove(docsIdsToRemove)
}
const openSelectInitiatorModal = () => {
Keyboard.dismiss()
appEvents.emit('openUserPicker', {
@ -155,6 +174,20 @@ export const AddUpdateTaskScreen: FC<IProps> = ({ route, navigation }) => { @@ -155,6 +174,20 @@ export const AddUpdateTaskScreen: FC<IProps> = ({ route, navigation }) => {
Keyboard.dismiss()
await onSubmit()
if (attachmentsToAdd.length)
await taskDocumentsService.add({
taskId: routeParams?.taskId,
files: attachmentsToAdd,
})
if (attachmentsIdsToRemove.length)
await Promise.all(
attachmentsIdsToRemove.map(
async id => await taskDocumentsService.remove(id),
),
)
appEvents.emit('reloadTaskDocs', { taskId: routeParams?.taskId })
setTimeout(() => {
if (editMode !== TaskEditMode.Copy) selectReason(null)
}, 200)
@ -259,13 +292,14 @@ export const AddUpdateTaskScreen: FC<IProps> = ({ route, navigation }) => { @@ -259,13 +292,14 @@ export const AddUpdateTaskScreen: FC<IProps> = ({ route, navigation }) => {
error={errors['reasonId']}
/>
{editMode === TaskEditMode.Update ? (
<TaskAttachMentsCollapseSmart
allowDelete
taskId={routeParams?.taskId}
documents={task?.documents}
/>
) : null}
<TaskAttachmentsCollapseSmart
allowDelete
titleStyle={styles.attachmentsTitle}
taskId={routeParams?.taskId}
docs={task?.documents}
onChange={onTackAttachmentsChange}
mode={addUpdateTaskAttachmentsMode[editMode]}
/>
<Button
title={buttonTitle}
@ -290,6 +324,9 @@ const createStyles = (theme: PartialTheme) => @@ -290,6 +324,9 @@ const createStyles = (theme: PartialTheme) =>
borderBottomWidth: 0.3,
borderBottomColor: theme.$border,
},
attachmentsTitle: {
color: theme.input.$title,
},
button: {
minWidth: $size(32, 28),
minHeight: $size(32, 28),

32
src/modules/tasks/screens/filter-tasks-result.screen.tsx

@ -57,38 +57,18 @@ export const FilterTasksResultScreen: FC<IProps> = ({ route, navigation }) => { @@ -57,38 +57,18 @@ export const FilterTasksResultScreen: FC<IProps> = ({ route, navigation }) => {
useSelectExecutor(taskFilter.executor)
const {
items,
loadMore,
reloadTasks,
taskFilterProps,
isLoading,
isLoadingNext,
} = useFetchTasksByFilter({
taskFilter,
searchString: searchValue,
closeFilter: () => setDrawer(false),
})
const { items, loadMore, taskFilterProps, isLoading, isLoadingNext } =
useFetchTasksByFilter({
taskFilter,
searchString: searchValue,
closeFilter: () => setDrawer(false),
})
const { selectedTasks, handleSelectTask, handleResetSelected } =
useTaskSelector()
const { handlePressOneTaskAction } = useTaskActions()
const handleOpenInfoModal = (
title: string,
message: string,
onPress: () => void,
) => {
appEvents.emit('openInfoModal', {
title,
message,
onPressOk: () => onPress(),
})
}
console.log('isLoadingNext', isLoadingNext)
const onPressBack = () => {
simpleDispatch(new UnselectAllUsers())
simpleDispatch(new UnselectExecutor())

3
src/modules/tasks/screens/my-tasks.screen.tsx

@ -40,7 +40,6 @@ export const MyTasksScreen: FC<IProps> = ({ navigation, route }) => { @@ -40,7 +40,6 @@ export const MyTasksScreen: FC<IProps> = ({ navigation, route }) => {
items: myTasks,
loadMore,
taskFilterProps,
isLoading,
isLoadingNext,
} = useFetchTasksByFilter({
closeFilter: () => setDrawer(false),
@ -56,8 +55,6 @@ export const MyTasksScreen: FC<IProps> = ({ navigation, route }) => { @@ -56,8 +55,6 @@ export const MyTasksScreen: FC<IProps> = ({ navigation, route }) => {
)
}
console.log('isLoadingNext', isLoadingNext)
return (
<ScreenLayout
filter={{

1
src/modules/tasks/screens/print-tasks.screen.tsx

@ -24,7 +24,6 @@ export const PrintTasks: FC<IProps> = ({ navigation, route: { params } }) => { @@ -24,7 +24,6 @@ export const PrintTasks: FC<IProps> = ({ navigation, route: { params } }) => {
await RNPrint.print({
html: html.join(' '),
})
console.log('PRINT END')
}
const getTask = async (id: number): Promise<IFullTaskInfo> => {

12
src/modules/tasks/screens/task-details.screen.tsx

@ -21,8 +21,9 @@ import { @@ -21,8 +21,9 @@ import {
TaskTermsCollapse,
} from '../components'
import { getMenuOptions, getTaskDetailsRows, getTaskTerms } from '../config'
import { TaskAttachmentsActionMode } from '../enum'
import { useReasons, useTaskActions, useTaskDetails } from '../hooks'
import { TaskAttachMentsCollapseSmart } from '../smart-components'
import { TaskAttachmentsCollapseSmart } from '../smart-components'
interface IProps extends IRouteParams {
route: {
@ -99,7 +100,7 @@ export const TaskDetailsScreen: FC<IProps> = ({ @@ -99,7 +100,7 @@ export const TaskDetailsScreen: FC<IProps> = ({
name="title"
value={taskDetails?.name}
placeHolder="Назва"
onChange={val => {}}
onChange={val => { }}
title={<Text style={styles.inputLabel}>Назва</Text>}
/>
@ -109,7 +110,7 @@ export const TaskDetailsScreen: FC<IProps> = ({ @@ -109,7 +110,7 @@ export const TaskDetailsScreen: FC<IProps> = ({
label="Опис"
value={taskDetails?.description}
placeholder="Опис"
onChange={() => {}}
onChange={() => { }}
/>
<TaskDetailsList
@ -126,9 +127,10 @@ export const TaskDetailsScreen: FC<IProps> = ({ @@ -126,9 +127,10 @@ export const TaskDetailsScreen: FC<IProps> = ({
createNewReason={createNewReason}
/>
<TaskAttachMentsCollapseSmart
<TaskAttachmentsCollapseSmart
taskId={taskDetails.id}
documents={taskDetails?.documents}
docs={taskDetails?.documents}
mode={TaskAttachmentsActionMode.DETAIL}
/>
</>
)}

1
src/modules/tasks/smart-components/swipable-task-card.smart-component.tsx

@ -27,6 +27,7 @@ interface IProps { @@ -27,6 +27,7 @@ interface IProps {
isFinished?: boolean
overdue?: boolean
isChecked?: boolean
hasAttachments?: boolean
onSelect?: () => void
onPress?: () => void
onPressActionButton?: (

86
src/modules/tasks/smart-components/task-attachments-collapse.smart-component.tsx

@ -1,39 +1,89 @@ @@ -1,39 +1,89 @@
import React, { FC, useEffect, useState } from 'react'
import { taskDocumentsService } from '@/services/domain'
import { appEvents, ITaskDocument } from '@/shared'
import { appEvents, IFile, ITaskDocument } from '@/shared'
import { TaskDetailsAttachmentsCollapse } from '../components'
import { getAttachmentsMenusOptions } from '../config'
import { TextStyle } from 'react-native'
import { TaskAttachmentsActionMode } from '../enum'
import { ShortTaskDocument } from '../interfaces'
import { transformDocToShortDoc, transformFileToShortDoc } from '../transforms'
interface TaskAttachMentsCollapseSmartProps {
interface TaskAttachmentsCollapseSmartProps {
taskId: number
documents: ITaskDocument[]
docs: ITaskDocument[]
titleStyle?: TextStyle
allowDelete?: boolean
mode: TaskAttachmentsActionMode
onChange?: (filesToAdd: IFile[], docsIdsToRemove: number[]) => void
}
export const TaskAttachMentsCollapseSmart: FC<TaskAttachMentsCollapseSmartProps> =
({ taskId, documents, allowDelete = false }) => {
const [attachments, setAttachments] =
useState<ITaskDocument[]>([])
useEffect(() => {
setAttachments(documents)
}, [documents])
export const TaskAttachmentsCollapseSmart: FC<TaskAttachmentsCollapseSmartProps> =
({
taskId,
docs,
titleStyle,
mode,
allowDelete = false,
onChange,
}) => {
const [attachments, setAttachments] = useState<ShortTaskDocument[]>([])
const [attachmentsIds, setAttachmentsIds] = useState<{ docs: number[], files: number[] }>(null)
const [files, setFiles] = useState<IFile[]>([])
const openAddFileMenu = () => {
const menuOptions = Object.values(
getAttachmentsMenusOptions({ taskId, setAttachments }),
)
const menuOptions = getAttachmentsMenusOptions({
taskId,
setFiles,
mode
})
appEvents.emit('openActionSheet', { items: menuOptions })
}
const onDeleteFile = async (documentId: number) => {
await taskDocumentsService.removeTaskDocument(documentId)
setAttachments(attachments.filter(doc => doc.id !== documentId && doc))
const onDeleteFile = async (docId: number) => {
const filteredAttachments = attachments.filter(it => it.id !== docId)
setAttachments(filteredAttachments)
const docsIds = attachmentsIds?.docs?.filter(id => id !== docId)
const filesIds = attachmentsIds?.files?.filter(id => id !== docId)
setAttachmentsIds({ docs: docsIds, files: filesIds })
}
useEffect(() => {
if (docs?.length) {
const docsShort = docs.map(it => transformDocToShortDoc(it))
setAttachmentsIds(prev => ({ ...prev, docs: docsShort.map(it => it.id) }))
setAttachments(prev => [
...prev.filter(it => !attachmentsIds?.docs?.includes(it.id)),
...docsShort,
])
}
}, [docs])
useEffect(() => {
if (onChange) {
const docsIds = docs?.map(it => it.id)
onChange(files, docsIds?.filter(it => !attachmentsIds?.docs?.includes(it)))
}
}, [docs, attachmentsIds, files])
useEffect(() => {
if (mode === TaskAttachmentsActionMode.DETAIL) return
if (files.length) {
const filesShort = files.map(it => transformFileToShortDoc(it))
setAttachmentsIds(prev => ({ ...prev, files: filesShort.map(it => it.id) }))
setAttachments(prev => [
...prev.filter(it => !attachmentsIds?.files?.includes(it.id)),
...filesShort,
])
}
}, [files])
return (
<TaskDetailsAttachmentsCollapse
attachments={attachments}
titleStyle={titleStyle}
counter={attachments.length}
allowDelete={allowDelete}
onDeleteFile={onDeleteFile}
onAddFile={openAddFileMenu}

1
src/modules/tasks/smart-components/tasks-list.smart-component.tsx

@ -55,6 +55,7 @@ export const TasksListSmart = ({ @@ -55,6 +55,7 @@ export const TasksListSmart = ({
isRead={checkIsTaskReadByUser(item, account.id)}
isFavorite={item.isFavorite}
hasUnreadComments={item.hasUnreadComments}
hasAttachments={item?.hasAttachments}
onPressActionButton={(action, payload) =>
onPressActionBtn(action, item, payload)
}

1
src/modules/tasks/transforms/index.ts

@ -0,0 +1 @@ @@ -0,0 +1 @@
export * from './task-attachments.transforms'

15
src/modules/tasks/transforms/task-attachments.transforms.ts

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
import { IFile, ITaskDocument } from '@/shared'
import { ShortTaskDocument } from '../interfaces'
export const transformDocToShortDoc = ({
id,
url,
}: ITaskDocument): ShortTaskDocument => ({
id,
url,
})
export const transformFileToShortDoc = ({
id,
uri: url,
}: IFile): ShortTaskDocument => ({ id, url })

6
src/modules/tasks/validations/create-task.validations.ts

@ -5,13 +5,11 @@ import { TaskEditMode } from '../enum' @@ -5,13 +5,11 @@ import { TaskEditMode } from '../enum'
validate.extend(validate.validators.datetime, {
parse: function (value, options) {
console.log('parse', value, +moment.utc(value))
return +moment.utc(value)
},
format: function (value, options) {
var format = options.dateOnly ? 'YYYY-MM-DD' : 'YYYY-MM-DD hh:mm:ss'
console.log('format', value, moment.utc(value).format(format))
return moment.utc(value).format(format)
},
})
@ -70,7 +68,7 @@ const constraintsByMode = { @@ -70,7 +68,7 @@ const constraintsByMode = {
},
[TaskEditMode.Copy]: {
...commonConstraints,
...commonConstraints,
executorId: {
presence: presenceCost,
},
@ -121,7 +119,7 @@ const constraintsByMode = { @@ -121,7 +119,7 @@ const constraintsByMode = {
}
export const validateCreateTask = <T>(data: T, mode: TaskEditMode) => {
const errors = validate(data, constraintsByMode[mode])
const errors = validate(data, constraintsByMode[mode])
if (_.isEmpty(errors)) return null

3
src/modules/taxonomies/smart-components/select-taxonomies-modal/index.tsx

@ -4,11 +4,12 @@ import { @@ -4,11 +4,12 @@ import {
View,
Text,
StyleSheet,
FlatList,
// FlatList,
TouchableOpacity,
Dimensions,
Platform,
} from 'react-native'
import { FlatList } from 'react-native-gesture-handler'
import RBSheet from 'react-native-raw-bottom-sheet'
import {
$size,

3
src/modules/users/smart-components/select-user-modal/index.tsx

@ -3,11 +3,11 @@ import { @@ -3,11 +3,11 @@ import {
View,
Text,
StyleSheet,
FlatList,
TouchableOpacity,
Dimensions,
Platform,
} from 'react-native'
import { FlatList } from 'react-native-gesture-handler'
import _ from 'lodash'
import { contactsService, tasksService } from '@/services/domain'
@ -169,6 +169,7 @@ export const SelectUserModalSmart = () => { @@ -169,6 +169,7 @@ export const SelectUserModalSmart = () => {
contentContainerStyle={styles.listContent}
keyboardShouldPersistTaps="handled"
/>
<View style={styles.content}>
<Button
title="Вибрати"

5
src/services/domain/auth.service.ts

@ -59,11 +59,8 @@ const autoAuth = async () => { @@ -59,11 +59,8 @@ const autoAuth = async () => {
await refreshSession()
await loadDataAfterAuth()
await loadNotImportantData()
console.log('DATA LOADED')
NavigationService.setModule(NavigationModuleKey.User)
} catch (e) {
console.log('ACCOUNT LOADING ERROR', e)
@ -85,7 +82,7 @@ const loadNotImportantData = async () => { @@ -85,7 +82,7 @@ const loadNotImportantData = async () => {
await taxonomiesService.fetchReasons().catch(() => {})
await contactsService.fetchBirthdayCountToday().catch(() => {})
await tasksService.getUnreadTasksCount().catch(() => { })
await tasksService.getUnreadTasksCount().catch(() => {})
await chatsService.getUnreadMessagesCount().catch(() => {})
await notificationService.initAndSaveDevice().catch(() => {})
}

23
src/services/domain/task-documents.service.ts

@ -1,17 +1,27 @@ @@ -1,17 +1,27 @@
import { addTaskDocumentsReq, removeTaskDocumentsReq } from "@/api"
import { addTaskDocumentsReq, getDocumentsByTaskIdReq, removeTaskDocumentsReq } from "@/api"
import { AddTaskDocumentsParams } from "@/api/tasks-documents/requests.interfaces"
import { ITaskDocument } from "@/shared"
const addTaskDocuments = async (params: AddTaskDocumentsParams): Promise<ITaskDocument[]> => {
const getByTaskId = async (taskId: number): Promise<ITaskDocument[]> => {
try {
const { data: docs } = await addTaskDocumentsReq(params)
const { data: docs } = await getDocumentsByTaskIdReq(taskId)
return docs
} catch (err) {
console.log('getDocumentsByTaskId ERR: ', err)
}
}
const add = async (params: AddTaskDocumentsParams): Promise<void> => {
try {
await addTaskDocumentsReq(params)
} catch (err) {
console.log('ADD TASK DOCUMENT ERR: ', err)
}
}
const removeTaskDocument = async (documentId: number): Promise<void> => {
const remove = async (documentId: number): Promise<void> => {
try {
await removeTaskDocumentsReq(documentId)
} catch (err) {
@ -20,6 +30,7 @@ const removeTaskDocument = async (documentId: number): Promise<void> => { @@ -20,6 +30,7 @@ const removeTaskDocument = async (documentId: number): Promise<void> => {
}
export const taskDocumentsService = {
addTaskDocuments,
removeTaskDocument
getByTaskId,
add,
remove
}

48
src/services/system/camera-roll.service.ts

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
import { PermissionsAndroid, Platform } from 'react-native'
import CameraRoll from '@react-native-community/cameraroll'
import RNFS from 'react-native-fs'
import { createUniqueFileName } from '@/shared/helpers'
async function hasAndroidPermission() {
const permission = PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
const hasPermission = await PermissionsAndroid.check(permission)
if (hasPermission) {
return true
}
const status = await PermissionsAndroid.request(permission)
return status === 'granted'
}
export async function saveToCameraRoll(
url: string,
type: 'photo' | 'video' | 'auto',
) {
if (Platform.OS === 'android' && !(await hasAndroidPermission())) {
return
}
const filename = url.substring(url.lastIndexOf('/') + 1)
const path = await createPath(RNFS.CachesDirectoryPath, filename)
const { promise } = RNFS.downloadFile({
fromUrl: url,
toFile: path,
})
await promise
await CameraRoll.save(path, { type })
return path
}
const createPath = async (directoryPath: string, fileName: string) => {
const path = directoryPath + `/${fileName}`
const fileExists = await RNFS.exists(path)
if (!fileExists) return path
const newName = createUniqueFileName(fileName)
return createPath(directoryPath, newName)
}

90
src/services/system/fs.service.ts

@ -1,12 +1,16 @@ @@ -1,12 +1,16 @@
import { IFile } from '@/shared'
import {
getDownloadDirectoryPath,
createUniqueFileName,
getFileExtension,
getFileName,
getFileUri,
isAndroid,
isImage,
} from '@/shared/helpers'
import RNFS from 'react-native-fs'
import { Alert, Platform } from 'react-native'
import RNFetchBlob from 'rn-fetch-blob'
interface IWriteFileData {
@ -19,8 +23,7 @@ interface IDownloadFileParams { @@ -19,8 +23,7 @@ interface IDownloadFileParams {
fileName: string
}
const prepareUriToRNFS = (uri: string) =>
'file:///' + uri.replace('file://', '')
const prepareUriToRNFS = (uri: string) => 'file://' + uri.replace('file://', '')
const prepareRNFSFile = (data: RNFS.StatResult): IFile => {
return {
@ -34,6 +37,7 @@ const prepareRNFSFile = (data: RNFS.StatResult): IFile => { @@ -34,6 +37,7 @@ const prepareRNFSFile = (data: RNFS.StatResult): IFile => {
const getFileStat = async (filePath: string): Promise<IFile> => {
try {
const fileStat = await RNFS.stat(prepareUriToRNFS(filePath))
return prepareRNFSFile(fileStat)
} catch (err) {
console.log('getFileStat ERR:', err)
@ -51,10 +55,24 @@ const writeFile = (data: IWriteFileData) => { @@ -51,10 +55,24 @@ const writeFile = (data: IWriteFileData) => {
})
}
const downloadFile = (params: IDownloadFileParams) => {
const createPath = async (directoryPath: string, fileName: string) => {
const path = directoryPath + `/${fileName}`
const fileExists = await RNFS.exists(path)
if (!fileExists) return path
const newName = createUniqueFileName(fileName)
return createPath(directoryPath, newName)
}
const downloadToDevice = (params: IDownloadFileParams) => {
return new Promise<string>(async (resolve, reject) => {
const directory = getDownloadDirectoryPath()
const path = directory + `/${params.fileName}`
const directory = isAndroid(
RNFS.DownloadDirectoryPath,
RNFS.LibraryDirectoryPath,
)
const path = await createPath(directory, params.fileName)
const { promise } = RNFS.downloadFile({
fromUrl: params.fileUrl,
@ -65,24 +83,70 @@ const downloadFile = (params: IDownloadFileParams) => { @@ -65,24 +83,70 @@ const downloadFile = (params: IDownloadFileParams) => {
await promise
resolve(path)
} catch (e) {
Alert.alert('Error', JSON.stringify(e))
console.log('downloadFile ERR:', e)
reject()
}
})
}
const downloadFile = (params: IDownloadFileParams) => {
return new Promise<string>(async (resolve, reject) => {
try {
if (Platform.OS === 'ios') {
const path = await _downloadFile(params.fileUrl)
resolve(path)
} else {
const directory = isImage(params.fileUrl)
? RNFS.CachesDirectoryPath
: RNFS.DocumentDirectoryPath
const path = directory + `/${params.fileName}`
const { promise } = RNFS.downloadFile({
fromUrl: params.fileUrl,
toFile: path,
})
await promise
resolve(path)
}
} catch (e) {
console.log('downloadFile ERR:', e)
reject()
}
})
}
const deleteFile = async (filepath: string) => {
let exists = await RNFS.exists(filepath);
if (exists) {
await RNFS.unlink(filepath);
console.log("File Deleted");
} else {
console.log("File Not Available")
try {
let exists = await RNFS.exists(filepath)
if (exists) {
await RNFS.unlink(filepath)
}
} catch (err) {
console.log('deleteFile ERR:', err)
}
}
export const fsService = {
getFileStat,
writeFile,
downloadFile,
deleteFile
deleteFile,
downloadToDevice,
}
const _downloadFile = (fileUrl: string): Promise<string> => {
return new Promise((resolve, reject) => {
let FILE_URL = fileUrl
RNFetchBlob.config({
fileCache: true,
appendExt: 'jpg',
})
.fetch('GET', FILE_URL)
.then(res => {
resolve(res.data)
})
})
}

3
src/services/system/real-time.service.ts

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
import { config } from '@/config'
import { socketEvents } from '@/shared/events'
import { Alert } from 'react-native'
import io from 'socket.io-client'
import { GlobalContainerService } from './global-container.service'
@ -18,7 +18,6 @@ export class SocketIo { @@ -18,7 +18,6 @@ export class SocketIo {
this._on('connect', () => {
this.emit('join-user', store().getState().account.account)
this.initSockets()
console.log('Success init socket')
})
} catch (e) {
console.log('ERROR')

18
src/shared/components/plugins/accordion.component.tsx

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
import React, { FC } from 'react'
import { StyleSheet, TouchableOpacity, View } from 'react-native'
import { StyleSheet, TextStyle, TouchableOpacity, View } from 'react-native'
import { PartialTheme } from '@/shared/themes/interfaces'
import { useTheme } from '@/shared/hooks'
import { IconComponent, Txt } from '../elements'
@ -8,7 +8,9 @@ import { $size } from '@/shared/helpers' @@ -8,7 +8,9 @@ import { $size } from '@/shared/helpers'
interface IProps {
isExpanded: boolean
headerTitle: string
titleStyle?: TextStyle
children: JSX.Element
counter?: number
leftHeaderContent?: number | string
onToggle: () => void
}
@ -16,7 +18,9 @@ interface IProps { @@ -16,7 +18,9 @@ interface IProps {
export const Accordion: FC<IProps> = ({
isExpanded,
headerTitle,
titleStyle,
children,
counter,
leftHeaderContent,
onToggle,
}): JSX.Element => {
@ -25,7 +29,7 @@ export const Accordion: FC<IProps> = ({ @@ -25,7 +29,7 @@ export const Accordion: FC<IProps> = ({
return (
<View>
<TouchableOpacity onPress={onToggle} style={styles.header}>
<Txt style={styles.text}>{headerTitle}</Txt>
<Txt style={[styles.text, titleStyle]}>{headerTitle}</Txt>
<View style={styles.leftHeaderContent}>
{leftHeaderContent ? (
@ -34,6 +38,12 @@ export const Accordion: FC<IProps> = ({ @@ -34,6 +38,12 @@ export const Accordion: FC<IProps> = ({
</Txt>
) : null}
{counter ? (
<Txt style={[styles.text, styles.counterTxt]}>
{counter}
</Txt>
) : null}
<IconComponent
size={$size(22, 20)}
color={theme.iconComponent.$primaryColor}
@ -61,6 +71,10 @@ const createStyles = (theme: PartialTheme) => @@ -61,6 +71,10 @@ const createStyles = (theme: PartialTheme) =>
color: theme.$textPrimary,
fontWeight: '300',
},
counterTxt: {
color: theme.iconComponent.$primaryColor,
marginRight: $size(15)
},
leftHeaderContent: {
flexDirection: 'row',
alignItems: 'center',

21
src/shared/components/plugins/chat/chat-bar.component.tsx

@ -7,7 +7,6 @@ import { Dimensions } from 'react-native' @@ -7,7 +7,6 @@ import { Dimensions } from 'react-native'
import { ActivityIndicator, Platform, StyleSheet, View } from 'react-native'
import { MentionInput } from 'react-native-controlled-mentions'
import { TouchableOpacity, FlatList } from 'react-native-gesture-handler'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { Avatar, IconComponent, Txt } from '../../elements'
import { KeyboardSpacer } from '../../tools'
@ -38,6 +37,7 @@ export const ChatBar: FC<ChatBarProps> = props => { @@ -38,6 +37,7 @@ export const ChatBar: FC<ChatBarProps> = props => {
const insets = useSafeAreaInsets()
const [isFocus, setFocus] = useState(false)
const [inputHeight, setInputHeight] = useState(0)
const [block, setBlock] = useState(false)
const inputRef = useRef(null)
@ -61,9 +61,8 @@ export const ChatBar: FC<ChatBarProps> = props => { @@ -61,9 +61,8 @@ export const ChatBar: FC<ChatBarProps> = props => {
key={item?.id}
style={styles.item}
onPress={() => {
onSuggestionPress(item)
inputRef.current.focus()
setTimeout(() => onSuggestionPress(item), 100)
}}>
<Avatar
imageUrl={item.avatar}
@ -119,7 +118,10 @@ export const ChatBar: FC<ChatBarProps> = props => { @@ -119,7 +118,10 @@ export const ChatBar: FC<ChatBarProps> = props => {
} else {
return (
<ChatSendButton
onSend={props.onPressSend}
onSend={() => {
setBlock(true)
props.onPressSend()
}}
onSendVoice={props.onSendVoice}
/>
)
@ -145,7 +147,7 @@ export const ChatBar: FC<ChatBarProps> = props => { @@ -145,7 +147,7 @@ export const ChatBar: FC<ChatBarProps> = props => {
onLayout={e =>
setInputHeight(e.nativeEvent.layout.height)
}
editable={!props.isDisabled}
editable={true}
multiline={true}
autoCorrect={false}
autoCompleteType="off"
@ -153,7 +155,14 @@ export const ChatBar: FC<ChatBarProps> = props => { @@ -153,7 +155,14 @@ export const ChatBar: FC<ChatBarProps> = props => {
containerStyle={styles.input}
style={{ color: theme?.$secondaryText }}
value={props.message}
onChange={val => props.onChangeMessage(val)}
onChange={val => {
if (block) {
setBlock(false)
props.onChangeMessage('')
} else {
props.onChangeMessage(val)
}
}}
placeholderTextColor={theme?.$secondaryText}
partTypes={[
{

14
src/shared/components/plugins/chat/chat-messages.component.tsx

@ -319,6 +319,7 @@ export const ChatMessages: FC<ChatMessagesProps> = ({ items, ...props }) => { @@ -319,6 +319,7 @@ export const ChatMessages: FC<ChatMessagesProps> = ({ items, ...props }) => {
if (!props.lastMessageLoaded && swipeDirection === 'up') {
lastIdRef.current = messagesGroups[0]?.id
props.loadNext(3)
}
}}
@ -363,6 +364,19 @@ export const ChatMessages: FC<ChatMessagesProps> = ({ items, ...props }) => { @@ -363,6 +364,19 @@ export const ChatMessages: FC<ChatMessagesProps> = ({ items, ...props }) => {
/>
)}
{lastOffset > 50 && !_.isEmpty(items) && (
<ChatDownButton
onPress={() => {
listRef.current.scrollToIndex({
animated: true,
index: 0,
viewPosition: 0,
})
setOffset(0)
}}
/>
)}
{!props.lastMessageLoaded && !_.isEmpty(items) && (
<ChatDownButton onPress={props.onRefresh} />
)}

2
src/shared/components/tools/keyboard-spacer.component.tsx

@ -121,8 +121,6 @@ export class KeyboardSpacer extends Component< @@ -121,8 +121,6 @@ export class KeyboardSpacer extends Component<
keyboardSpace = keyboardSpace - Number(this.props.addSpace)
}
console.log('keyboardSpace', event.endCoordinates.screenY)
this.setState(
{
keyboardSpace,

7
src/shared/events/index.ts

@ -36,7 +36,9 @@ export type AppEvents = { @@ -36,7 +36,9 @@ export type AppEvents = {
message: string
pressButtonText?: string
onPressOk?: () => void
isLoading?: boolean
}
closeInfoModal: {}
openDatePickerModal: {
defaultValue: Date
minValue?: Date
@ -70,13 +72,14 @@ export type AppEvents = { @@ -70,13 +72,14 @@ export type AppEvents = {
onTaskAddToFavorite: { ids: number[] }
onTaskRemoveFromFavorite: { ids: number[] }
onTaskCommentsRead: { taskId: number }
reloadTaskDocs: { taskId: number }
reloadTask: {}
onReadChat: { chatId: number; unreadCount?: number }
onDeleteChatForMe: { chatId: number }
onClearChatForMe: { chatId: number }
chatSendImgModal: {
url: string,
url: string
onSend(): void
}
onClearAllChats: {}
@ -106,7 +109,7 @@ export type SocketEvents = { @@ -106,7 +109,7 @@ export type SocketEvents = {
'chat/delete-member': { chatId: number }
'chat/edit-chat': { chatId: number }
'chat/pined-message': { messageId: number; chatId: number }
'chat/is-read': { chatId: number, userId: number }
'chat/is-read': { chatId: number; userId: number }
'task/new-comment': { taskId: number }
}

26
src/shared/helpers/fs.helpers.ts

@ -5,6 +5,8 @@ import { isAndroid } from './platform.helper' @@ -5,6 +5,8 @@ import { isAndroid } from './platform.helper'
export const MAX_FILE_SIZE = 50000000
export const MAX_VIDEO_SIZE = 100000000
const FILE_TAIL_REG_EXP = /^\(\d+\)$/
const DIGITS_REG_EXP = /\d+/
export const getFileName = (file: any) => {
try {
@ -15,7 +17,7 @@ export const getFileName = (file: any) => { @@ -15,7 +17,7 @@ export const getFileName = (file: any) => {
}
}
export const getFileExtension = (uri: string) => uri.split('.')[1]
export const getFileExtension = (uri: string) => uri.split('.').pop()
export const getFileUri = (file: any): string =>
Platform.select<string>({
@ -101,4 +103,24 @@ export const alertFileSizeExceeded = (files: IFile[]) => { @@ -101,4 +103,24 @@ export const alertFileSizeExceeded = (files: IFile[]) => {
export const getDownloadDirectoryPath = () => Platform.select({
ios: LibraryDirectoryPath,
android: DownloadDirectoryPath
})
})
export const createUniqueFileName = (fileName: string) => {
const arrFromName = fileName.split('.')
const ext = arrFromName[arrFromName.length - 1]
const body =
arrFromName.length == 1 ? ext : arrFromName[arrFromName.length - 2]
const bodyArr = body.split('_')
const tail = bodyArr[bodyArr.length - 1]
const additionalTail = tail.match(FILE_TAIL_REG_EXP)
let newName = arrFromName.length == 1 ? `${body}_(1)` : `${body}_(1).${ext}`
if (!additionalTail) return newName
const doublesCount = additionalTail[0].match(DIGITS_REG_EXP)
if (!doublesCount) return newName
return arrFromName.length == 1
? `${bodyArr.slice(0, -1).join('_')}_(${Number(doublesCount) + 1})`
: `${bodyArr.slice(0, -1).join('_')}_(${Number(doublesCount) + 1}).${ext}`
}

1
src/shared/interfaces/media.interfaces.ts

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
export interface IFile {
id?: number
uri: string
type: string
name: string

5
src/shared/interfaces/tasks.interfaces.ts

@ -13,6 +13,7 @@ export interface ITaskPreview { @@ -13,6 +13,7 @@ export interface ITaskPreview {
executor?: IShortUser
isFavorite?: boolean
hasUnreadComments?: boolean
hasAttachments?: boolean
events?: Record<string, TaskEvent[]>
doneDate: string
}
@ -42,8 +43,7 @@ export interface IFullTaskInfo extends ITaskDetails { @@ -42,8 +43,7 @@ export interface IFullTaskInfo extends ITaskDetails {
executor?: IShortUser
group?: ITaxonomyShortInfo
reason?: ITaxonomyShortInfo
documents?: ITaskDocument[]
events?: Record<string, TaskEvent[]>
events?: Record<string, TaskEvent>[]
}
export interface ITaskActionPayload {
@ -64,6 +64,7 @@ export interface ITaskDocument { @@ -64,6 +64,7 @@ export interface ITaskDocument {
createdAt: string
}
export interface ITaskEvent {
id: number
taskId: number

1
src/store/shared/reducer.ts

@ -18,7 +18,6 @@ export const sharedReducer = createReducer<ISharedState, TAuthActions>( @@ -18,7 +18,6 @@ export const sharedReducer = createReducer<ISharedState, TAuthActions>(
initialState,
{
SET_NAVIGATION_MODULE: (state, action) => {
console.log('SET_NAVIGATION_MODULE', action.payload)
return {
...state,
prevModule: state.activeNavigationModule,

70
yarn.lock

@ -1197,6 +1197,11 @@ @@ -1197,6 +1197,11 @@
dependencies:
"deep-assign" "^3.0.0"
"@react-native-community/cameraroll@^4.1.2":
"integrity" "sha512-jkdhMByMKD2CZ/5MPeBieYn8vkCfC4MOTouPpBpps3I8N6HUYJk+1JnDdktVYl2WINnqXpQptDA2YptVyifYAg=="
"resolved" "https://registry.npmjs.org/@react-native-community/cameraroll/-/cameraroll-4.1.2.tgz"
"version" "4.1.2"
"@react-native-community/cli-debugger-ui@^5.0.1":
"integrity" "sha512-5gGKaaXYOVE423BUqxIfvfAVSj5Cg1cU/TpGbeg/iqpy2CfqyWqJB3tTuVUbOOiOvR5wbU8tti6pIi1pchJ+oA=="
"resolved" "https://registry.npmjs.org/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-5.0.1.tgz"
@ -4462,6 +4467,11 @@ @@ -4462,6 +4467,11 @@
"resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
"version" "2.0.4"
"inherits@2.0.3":
"integrity" "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
"resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz"
"version" "2.0.3"
"ini@~1.3.0":
"integrity" "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
"resolved" "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz"
@ -5994,35 +6004,28 @@ @@ -5994,35 +6004,28 @@
"braces" "^3.0.1"
"picomatch" "^2.2.3"
"mime-db@>= 1.43.0 < 2", "mime-db@1.49.0":
"integrity" "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA=="
"resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.49.0.tgz"
"version" "1.49.0"
"mime-db@>= 1.43.0 < 2", "mime-db@1.52.0":
"integrity" "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
"resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
"version" "1.52.0"
"mime-db@1.51.0":
"integrity" "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="
"resolved" "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz"
"version" "1.51.0"
"mime-types@^2.1.12", "mime-types@^2.1.27", "mime-types@~2.1.19":
"integrity" "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A=="
"resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz"
"version" "2.1.32"
dependencies:
"mime-db" "1.49.0"
"mime-types@~2.1.34":
"integrity" "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A=="
"resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz"
"version" "2.1.34"
"mime-types@^2.1.12", "mime-types@^2.1.27", "mime-types@^2.1.35", "mime-types@~2.1.19", "mime-types@~2.1.34":
"integrity" "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="
"resolved" "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
"version" "2.1.35"
dependencies:
"mime-db" "1.51.0"
"mime-db" "1.52.0"
"mime@^2.4.1":
"integrity" "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="
"resolved" "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz"
"version" "2.6.0"
"mime@^3.0.0":
"integrity" "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="
"resolved" "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz"
"version" "3.0.0"
"mime@1.6.0":
"integrity" "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
"resolved" "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz"
@ -6620,6 +6623,14 @@ @@ -6620,6 +6623,14 @@
"resolved" "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz"
"version" "1.0.7"
"path@^0.12.7":
"integrity" "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8="
"resolved" "https://registry.npmjs.org/path/-/path-0.12.7.tgz"
"version" "0.12.7"
dependencies:
"process" "^0.11.1"
"util" "^0.10.3"
"performance-now@^2.1.0":
"integrity" "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
"resolved" "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz"
@ -6744,6 +6755,11 @@ @@ -6744,6 +6755,11 @@
"resolved" "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz"
"version" "2.0.1"
"process@^0.11.1":
"integrity" "sha1-czIwDoQBYb2j5podHZGn1LwW8YI="
"resolved" "https://registry.npmjs.org/process/-/process-0.11.10.tgz"
"version" "0.11.10"
"progress@^2.0.0":
"integrity" "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="
"resolved" "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz"
@ -7184,6 +7200,11 @@ @@ -7184,6 +7200,11 @@
"resolved" "https://registry.npmjs.org/react-native-shadow/-/react-native-shadow-1.2.2.tgz"
"version" "1.2.2"
"react-native-share@^7.4.1":
"integrity" "sha512-NFYkq9ytOP2kAAa33ZVOfBQLrpcBqwsNHsuDgU9DUcFpAlu2hYsjBy0MgLyJk8+uTzCBa5UCIlFFjZDwqFOKuw=="
"resolved" "https://registry.npmjs.org/react-native-share/-/react-native-share-7.4.1.tgz"
"version" "7.4.1"
"react-native-sound-level@^1.1.5":
"integrity" "sha512-H4dVYbif/PfzMOp9IpNjJUtbODYXeHk8tI0d9mWcJcBEB/ITcP4jSvDtAOH8B/Vcu4h4Owrvsuz/Y+UfS0biuw=="
"resolved" "https://registry.npmjs.org/react-native-sound-level/-/react-native-sound-level-1.1.5.tgz"
@ -8865,6 +8886,13 @@ @@ -8865,6 +8886,13 @@
"has-symbols" "^1.0.1"
"object.getownpropertydescriptors" "^2.1.0"
"util@^0.10.3":
"integrity" "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A=="
"resolved" "https://registry.npmjs.org/util/-/util-0.10.4.tgz"
"version" "0.10.4"
dependencies:
"inherits" "2.0.3"
"utils-merge@1.0.1":
"integrity" "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
"resolved" "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"

Loading…
Cancel
Save