audio messages
Signed-off-by: Andrea Maria Piana <andrea.maria.piana@gmail.com>
This commit is contained in:
parent
2cc8c9822f
commit
86700f2b5c
|
@ -21,6 +21,8 @@
|
|||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
|
||||
<uses-permission android:name="com.android.vending.BILLING" />
|
||||
|
||||
<!-- these permissions should be removed -->
|
||||
|
|
|
@ -81,6 +81,10 @@ abstract_target 'Status' do
|
|||
pod 'SQLCipher', '~>3.0'
|
||||
pod 'SSZipArchive'
|
||||
|
||||
permissions_path = '../node_modules/react-native-permissions/ios'
|
||||
pod 'Permission-Microphone', :path => "#{permissions_path}/Microphone.podspec"
|
||||
pod 'Permission-Camera', :path => "#{permissions_path}/Camera.podspec"
|
||||
|
||||
target 'StatusIm' do
|
||||
target 'StatusImTests' do
|
||||
inherit! :complete
|
||||
|
|
|
@ -70,6 +70,10 @@ PODS:
|
|||
- OpenSSL-Universal (1.0.2.19):
|
||||
- OpenSSL-Universal/Static (= 1.0.2.19)
|
||||
- OpenSSL-Universal/Static (1.0.2.19)
|
||||
- Permission-Camera (2.1.5):
|
||||
- RNPermissions
|
||||
- Permission-Microphone (2.1.5):
|
||||
- RNPermissions
|
||||
- RCTRequired (0.62.2)
|
||||
- RCTTypeSafety (0.62.2):
|
||||
- FBLazyVector (= 0.62.2)
|
||||
|
@ -255,6 +259,8 @@ PODS:
|
|||
- React
|
||||
- react-native-shake (3.4.0):
|
||||
- React
|
||||
- react-native-slider (3.0.0):
|
||||
- React
|
||||
- react-native-splash-screen (3.2.0):
|
||||
- React
|
||||
- react-native-webview (10.3.1):
|
||||
|
@ -318,6 +324,8 @@ PODS:
|
|||
- React-cxxreact (= 0.62.2)
|
||||
- React-jsi (= 0.62.2)
|
||||
- ReactCommon/callinvoker (= 0.62.2)
|
||||
- ReactNativeAudioToolkit (2.0.3):
|
||||
- React
|
||||
- ReactNativeDarkMode (0.2.2):
|
||||
- React
|
||||
- RNCClipboard (1.2.2):
|
||||
|
@ -341,6 +349,8 @@ PODS:
|
|||
- React
|
||||
- RNLanguages (3.0.2):
|
||||
- React
|
||||
- RNPermissions (2.1.5):
|
||||
- React
|
||||
- RNReactNativeHapticFeedback (1.9.0):
|
||||
- React
|
||||
- RNReanimated (1.8.0):
|
||||
|
@ -354,8 +364,8 @@ PODS:
|
|||
- SQLCipher/common (3.4.2)
|
||||
- SQLCipher/standard (3.4.2):
|
||||
- SQLCipher/common
|
||||
- SSZipArchive (2.2.2)
|
||||
- TOCropViewController (2.5.2)
|
||||
- SSZipArchive (2.2.3)
|
||||
- TOCropViewController (2.5.3)
|
||||
- TouchID (4.4.1):
|
||||
- React
|
||||
- Yoga (1.14.0)
|
||||
|
@ -387,6 +397,8 @@ DEPENDENCIES:
|
|||
- FlipperKit/SKIOSNetworkPlugin (~> 0.37.0)
|
||||
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
|
||||
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||
- Permission-Camera (from `../node_modules/react-native-permissions/ios/Camera.podspec`)
|
||||
- Permission-Microphone (from `../node_modules/react-native-permissions/ios/Microphone.podspec`)
|
||||
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
|
||||
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
|
||||
- React (from `../node_modules/react-native/`)
|
||||
|
@ -406,6 +418,7 @@ DEPENDENCIES:
|
|||
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
|
||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||
- react-native-shake (from `../node_modules/react-native-shake`)
|
||||
- "react-native-slider (from `../node_modules/@react-native-community/slider`)"
|
||||
- react-native-splash-screen (from `../node_modules/react-native-splash-screen`)
|
||||
- react-native-webview (from `../node_modules/react-native-webview`)
|
||||
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
|
||||
|
@ -419,6 +432,7 @@ DEPENDENCIES:
|
|||
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
|
||||
- ReactCommon/callinvoker (from `../node_modules/react-native/ReactCommon`)
|
||||
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
|
||||
- "ReactNativeAudioToolkit (from `../node_modules/@react-native-community/audio-toolkit`)"
|
||||
- ReactNativeDarkMode (from `../node_modules/react-native-dark-mode`)
|
||||
- "RNCClipboard (from `../node_modules/@react-native-community/clipboard`)"
|
||||
- "RNCMaskedView (from `../node_modules/@react-native-community/masked-view`)"
|
||||
|
@ -427,6 +441,7 @@ DEPENDENCIES:
|
|||
- RNImageCropPicker (from `../node_modules/react-native-image-crop-picker`)
|
||||
- RNKeychain (from `../node_modules/react-native-keychain`)
|
||||
- RNLanguages (from `../node_modules/react-native-languages`)
|
||||
- RNPermissions (from `../node_modules/react-native-permissions`)
|
||||
- RNReactNativeHapticFeedback (from `../node_modules/react-native-haptic-feedback`)
|
||||
- RNReanimated (from `../node_modules/react-native-reanimated`)
|
||||
- RNScreens (from `../node_modules/react-native-screens`)
|
||||
|
@ -437,11 +452,8 @@ DEPENDENCIES:
|
|||
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/CocoaPods/Specs.git:
|
||||
- boost-for-react-native
|
||||
- SQLCipher
|
||||
- SSZipArchive
|
||||
trunk:
|
||||
- boost-for-react-native
|
||||
- CocoaAsyncSocket
|
||||
- CocoaLibEvent
|
||||
- Flipper
|
||||
|
@ -452,6 +464,8 @@ SPEC REPOS:
|
|||
- Flipper-RSocket
|
||||
- FlipperKit
|
||||
- OpenSSL-Universal
|
||||
- SQLCipher
|
||||
- SSZipArchive
|
||||
- TOCropViewController
|
||||
- YogaKit
|
||||
|
||||
|
@ -466,6 +480,10 @@ EXTERNAL SOURCES:
|
|||
:podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec"
|
||||
glog:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec"
|
||||
Permission-Camera:
|
||||
:path: "../node_modules/react-native-permissions/ios/Camera.podspec"
|
||||
Permission-Microphone:
|
||||
:path: "../node_modules/react-native-permissions/ios/Microphone.podspec"
|
||||
RCTRequired:
|
||||
:path: "../node_modules/react-native/Libraries/RCTRequired"
|
||||
RCTTypeSafety:
|
||||
|
@ -500,6 +518,8 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/react-native-safe-area-context"
|
||||
react-native-shake:
|
||||
:path: "../node_modules/react-native-shake"
|
||||
react-native-slider:
|
||||
:path: "../node_modules/@react-native-community/slider"
|
||||
react-native-splash-screen:
|
||||
:path: "../node_modules/react-native-splash-screen"
|
||||
react-native-webview:
|
||||
|
@ -524,6 +544,8 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/react-native/Libraries/Vibration"
|
||||
ReactCommon:
|
||||
:path: "../node_modules/react-native/ReactCommon"
|
||||
ReactNativeAudioToolkit:
|
||||
:path: "../node_modules/@react-native-community/audio-toolkit"
|
||||
ReactNativeDarkMode:
|
||||
:path: "../node_modules/react-native-dark-mode"
|
||||
RNCClipboard:
|
||||
|
@ -540,6 +562,8 @@ EXTERNAL SOURCES:
|
|||
:path: "../node_modules/react-native-keychain"
|
||||
RNLanguages:
|
||||
:path: "../node_modules/react-native-languages"
|
||||
RNPermissions:
|
||||
:path: "../node_modules/react-native-permissions"
|
||||
RNReactNativeHapticFeedback:
|
||||
:path: "../node_modules/react-native-haptic-feedback"
|
||||
RNReanimated:
|
||||
|
@ -568,8 +592,10 @@ SPEC CHECKSUMS:
|
|||
Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7
|
||||
FlipperKit: afd4259ef9eadeeb2d30250b37d95cb3b6b97a69
|
||||
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
|
||||
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
|
||||
glog: 682164e7ac67e41afd8f7b6a37a96d04caf61cc0
|
||||
OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355
|
||||
Permission-Camera: afad27bf90337684d4a86f3825112d648c8c4d3b
|
||||
Permission-Microphone: 0ffabc3fe1c75cfb260525ee3f529383c9f4368c
|
||||
RCTRequired: cec6a34b3ac8a9915c37e7e4ad3aa74726ce4035
|
||||
RCTTypeSafety: 93006131180074cffa227a1075802c89a49dd4ce
|
||||
React: 29a8b1a02bd764fb7644ef04019270849b9a7ac3
|
||||
|
@ -587,6 +613,7 @@ SPEC CHECKSUMS:
|
|||
react-native-netinfo: ddaca8bbb9e6e914b1a23787ccb879bc642931c9
|
||||
react-native-safe-area-context: 60f654e00b6cc416573f6d5dbfce3839958eb57a
|
||||
react-native-shake: de052eaa3eadc4a326b8ddd7ac80c06e8d84528c
|
||||
react-native-slider: 12bd76d3d568c9c5500825db54123d44b48e4ad4
|
||||
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
|
||||
react-native-webview: 40bbeb6d011226f34cb83f845aeb0fdf515cfc5f
|
||||
React-RCTActionSheet: f41ea8a811aac770e0cc6e0ad6b270c644ea8b7c
|
||||
|
@ -599,6 +626,7 @@ SPEC CHECKSUMS:
|
|||
React-RCTText: fae545b10cfdb3d247c36c56f61a94cfd6dba41d
|
||||
React-RCTVibration: 4356114dbcba4ce66991096e51a66e61eda51256
|
||||
ReactCommon: ed4e11d27609d571e7eee8b65548efc191116eb3
|
||||
ReactNativeAudioToolkit: de9610f323e855ac6574be8c99621f3d57c5df06
|
||||
ReactNativeDarkMode: 0178ffca3b10f6a7c9f49d6f9810232b328fa949
|
||||
RNCClipboard: 8148e21ac347c51fd6cd4b683389094c216bb543
|
||||
RNCMaskedView: 71fc32d971f03b7f03d6ab6b86b730c4ee64f5b6
|
||||
|
@ -607,17 +635,18 @@ SPEC CHECKSUMS:
|
|||
RNImageCropPicker: 38865ab4af1b0b2146ad66061196bc0184946855
|
||||
RNKeychain: 216f37338fcb9e5c3a2530f1e3295f737a690cb1
|
||||
RNLanguages: 962e562af0d34ab1958d89bcfdb64fafc37c513e
|
||||
RNPermissions: ad71dd4f767ec254f2cd57592fbee02afee75467
|
||||
RNReactNativeHapticFeedback: 2566b468cc8d0e7bb2f84b23adc0f4614594d071
|
||||
RNReanimated: 955cf4068714003d2f1a6e2bae3fb1118f359aff
|
||||
RNScreens: ac02d0e4529f08ced69f5580d416f968a6ec3a1d
|
||||
RNSVG: 8ba35cbeb385a52fd960fd28db9d7d18b4c2974f
|
||||
SQLCipher: f9fcf29b2e59ced7defc2a2bdd0ebe79b40d4990
|
||||
SSZipArchive: fa16b8cc4cdeceb698e5e5d9f67e9558532fbf23
|
||||
TOCropViewController: e9da34f484aedd4e5d5a8ab230ba217cfe16c729
|
||||
SSZipArchive: 62d4947b08730e4cda640473b0066d209ff033c9
|
||||
TOCropViewController: 20a14b6a7a098308bf369e7c8d700dc983a974e6
|
||||
TouchID: ba4c656d849cceabc2e4eef722dea5e55959ecf4
|
||||
Yoga: 3ebccbdd559724312790e7742142d062476b698e
|
||||
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
|
||||
|
||||
PODFILE CHECKSUM: f66349c5bfb9c21ac968307ea5a2d6c2dd4091ed
|
||||
PODFILE CHECKSUM: 5faa578ff5cb7a30abc18b9d620df288750a72fe
|
||||
|
||||
COCOAPODS: 1.9.1
|
||||
|
|
|
@ -83,6 +83,8 @@
|
|||
<string>Location access is required for some DApps to function properly.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Photos access is required to give you the ability to send images.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Need microphone access for sending audio messages.</string>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>Inter-Bold.otf</string>
|
||||
|
|
|
@ -83,6 +83,8 @@
|
|||
<string>Location access is required for some DApps to function properly.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Photos access is required to give you the ability to send images.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Need microphone access for sending audio messages.</string>
|
||||
<key>UIAppFonts</key>
|
||||
<array>
|
||||
<string>Inter-Bold.otf</string>
|
||||
|
|
|
@ -1346,5 +1346,33 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
|||
|
||||
StatusThreadPoolExecutor.getInstance().execute(r);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void activateKeepAwake() {
|
||||
final Activity activity = getCurrentActivity();
|
||||
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void deactivateKeepAwake() {
|
||||
final Activity activity = getCurrentActivity();
|
||||
|
||||
if (activity != null) {
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
activity.getWindow().clearFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -749,6 +749,20 @@ RCT_EXPORT_METHOD(setBlankPreviewFlag:(BOOL *)newValue)
|
|||
[userDefaults synchronize];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(activateKeepAwake)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[UIApplication sharedApplication] setIdleTimerDisabled:YES];
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(deactivateKeepAwake)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[UIApplication sharedApplication] setIdleTimerDisabled:NO];
|
||||
});
|
||||
}
|
||||
|
||||
//// deviceinfo
|
||||
|
||||
- (bool) is24Hour
|
||||
|
|
|
@ -5794,16 +5794,16 @@
|
|||
},
|
||||
|
||||
{
|
||||
"path": "com/google/protobuf/protobuf-java/3.12.2/protobuf-java-3.12.2",
|
||||
"path": "com/google/protobuf/protobuf-java/4.0.0-rc-2/protobuf-java-4.0.0-rc-2",
|
||||
"host": "https://repo.maven.apache.org/maven2",
|
||||
"type": "jar",
|
||||
"pom": {
|
||||
"sha1": "da3bb9dce2a722503f0d3e4f3b5eea93f737ba48",
|
||||
"sha256": "1ijicnm83gq33px9c5r4man0n2zw6axrxsqw0jnm1miixyjz08cv"
|
||||
"sha1": "8fab0fe0746b1d370663a3db7dd483c2c4d34a6f",
|
||||
"sha256": "17agfgbdfxdk304fgzk0iwsf7ggwqrwb44b3nnjc0xjdgqlxmzxb"
|
||||
},
|
||||
"jar": {
|
||||
"sha1": "f0029524002295154c6b546d4e6bd1cfc5081874",
|
||||
"sha256": "186a1lgmcfr3iml7bfxk9gnbsafyxmriqfyglvkwa85d7gwvx1dk"
|
||||
"sha1": "ae590f34383d2d6fde2b3b8e2ffb668e64579099",
|
||||
"sha256": "0al23s9s97sxlrrndb91cnra07fdchbcgvhcacyd6yjzysz55hiw"
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -5828,12 +5828,12 @@
|
|||
},
|
||||
|
||||
{
|
||||
"path": "com/google/protobuf/protobuf-parent/3.12.2/protobuf-parent-3.12.2",
|
||||
"path": "com/google/protobuf/protobuf-parent/4.0.0-rc-2/protobuf-parent-4.0.0-rc-2",
|
||||
"host": "https://repo.maven.apache.org/maven2",
|
||||
"type": "pom",
|
||||
"pom": {
|
||||
"sha1": "f5b5d4cb5e6d512f2fac5d2b08e599644a53c087",
|
||||
"sha256": "0md51xi5af8yi9l1dpqbmysrvlll05zpvmalag6p4f6fykwcry05"
|
||||
"sha1": "c5391c7917ca1197cbbda4efc6454a87e7e7107e",
|
||||
"sha256": "0bvynr52xjyd42n23ih635h2cmyillfbac0yjymhlp505f0p1lp0"
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -417,10 +417,10 @@ https://repo.maven.apache.org/maven2/com/google/jimfs/jimfs/1.1/jimfs-1.1.pom
|
|||
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-java-util/3.4.0/protobuf-java-util-3.4.0.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-java/3.0.0/protobuf-java-3.0.0.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-java/3.4.0/protobuf-java-3.4.0.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-java/3.12.2/protobuf-java-3.12.2.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-java/4.0.0-rc-2/protobuf-java-4.0.0-rc-2.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-parent/3.0.0/protobuf-parent-3.0.0.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-parent/3.4.0/protobuf-parent-3.4.0.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-parent/3.12.2/protobuf-parent-3.12.2.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/protobuf/protobuf-parent/4.0.0-rc-2/protobuf-parent-4.0.0-rc-2.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/truth/truth-parent/1.0.1/truth-parent-1.0.1.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/truth/truth/1.0.1/truth-1.0.1.pom
|
||||
https://repo.maven.apache.org/maven2/com/google/zxing/core/3.3.3/core-3.3.3.pom
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
app
|
||||
react-native-background-timer
|
||||
react-native-camera
|
||||
react-native-community_audio-toolkit
|
||||
react-native-community_cameraroll
|
||||
react-native-community_clipboard
|
||||
react-native-community_masked-view
|
||||
react-native-community_netinfo
|
||||
react-native-community_slider
|
||||
react-native-config
|
||||
react-native-dark-mode
|
||||
react-native-dialogs
|
||||
|
@ -18,6 +20,7 @@ react-native-languages
|
|||
react-native-mail
|
||||
react-native-navigation-bar-color
|
||||
react-native-nfc-manager
|
||||
react-native-permissions
|
||||
react-native-reanimated
|
||||
react-native-safe-area-context
|
||||
react-native-screens
|
||||
|
|
|
@ -10,11 +10,13 @@
|
|||
"app:android": "react-native run-android"
|
||||
},
|
||||
"dependencies": {
|
||||
"@react-native-community/audio-toolkit": "git+https://github.com/tbenr/react-native-audio-toolkit.git#v2.0.3-status-v6",
|
||||
"@react-native-community/cameraroll": "^1.6.1",
|
||||
"@react-native-community/clipboard": "^1.2.2",
|
||||
"@react-native-community/hooks": "^2.5.1",
|
||||
"@react-native-community/masked-view": "^0.1.6",
|
||||
"@react-native-community/netinfo": "^4.4.0",
|
||||
"@react-native-community/slider": "^3.0.0",
|
||||
"@react-navigation/bottom-tabs": "^5.7.0",
|
||||
"@react-navigation/native": "^5.7.0",
|
||||
"@react-navigation/stack": "^5.7.0",
|
||||
|
@ -46,6 +48,7 @@
|
|||
"react-native-languages": "^3.0.2",
|
||||
"react-native-mail": "git+https://github.com/status-im/react-native-mail.git#v4.0.0-status",
|
||||
"react-native-navigation-bar-color": "^2.0.1",
|
||||
"react-native-permissions": "^2.1.5",
|
||||
"react-native-reanimated": "^1.7.0",
|
||||
"react-native-redash": "^14.2.2",
|
||||
"react-native-safe-area-context": "^2.0.0",
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 216 B |
Binary file not shown.
After Width: | Height: | Size: 420 B |
Binary file not shown.
After Width: | Height: | Size: 288 B |
Binary file not shown.
After Width: | Height: | Size: 400 B |
Binary file not shown.
After Width: | Height: | Size: 502 B |
Binary file not shown.
After Width: | Height: | Size: 762 B |
|
@ -0,0 +1,129 @@
|
|||
(ns status-im.audio.core
|
||||
(:require ["@react-native-community/audio-toolkit" :refer (Player Recorder MediaStates)]))
|
||||
|
||||
;; get mediastates from react module
|
||||
(def PLAYING (.-PLAYING ^js MediaStates))
|
||||
(def PAUSED (.-PAUSED ^js MediaStates))
|
||||
(def RECORDING (.-RECORDING ^js MediaStates))
|
||||
(def PREPARED (.-PREPARED ^js MediaStates))
|
||||
(def IDLE (.-IDLE ^js MediaStates))
|
||||
(def ERROR (.-ERROR ^js MediaStates))
|
||||
(def DESTROYED (.-DESTROYED ^js MediaStates))
|
||||
(def SEEKING (.-SEEKING ^js MediaStates))
|
||||
|
||||
(def default-recorder-options {:filename "recording.aac"
|
||||
:bitrate 32000
|
||||
:channels 1
|
||||
:sampleRate 22050
|
||||
:quality "medium" ; ios only
|
||||
:meteringInterval 50})
|
||||
|
||||
(defn get-state [player-recorder]
|
||||
(when player-recorder
|
||||
(.-state ^js player-recorder)))
|
||||
|
||||
(defn new-recorder [options on-meter on-ended]
|
||||
(let [recorder (new ^js Recorder
|
||||
(:filename options)
|
||||
(clj->js options))]
|
||||
(when on-meter
|
||||
(.on ^js recorder "meter" on-meter))
|
||||
(when on-ended
|
||||
(.on ^js recorder "ended" on-ended))))
|
||||
|
||||
(defn new-player [audio options on-ended]
|
||||
(let [player (new ^js Player
|
||||
audio
|
||||
(clj->js options))]
|
||||
(when on-ended
|
||||
(.on ^js player "ended" on-ended))))
|
||||
|
||||
(defn prepare-player [player on-prepared on-error]
|
||||
(when (and player (.-canPrepare ^js player))
|
||||
(.prepare ^js player #(if %
|
||||
(on-error {:error (.-err %) :message (.-message %)})
|
||||
(on-prepared)))))
|
||||
|
||||
(defn prepare-recorder [recorder on-prepared on-error]
|
||||
(when (and recorder (.-canPrepare ^js recorder))
|
||||
(.prepare ^js recorder (fn [err _]
|
||||
(if err
|
||||
(on-error {:error (.-err err) :message (.-message err)})
|
||||
(on-prepared))))))
|
||||
|
||||
(defn start-recording [recorder on-start on-error]
|
||||
(when (and recorder
|
||||
(or
|
||||
(.-canRecord ^js recorder)
|
||||
(.-canPrepare ^js recorder)))
|
||||
(.record ^js recorder #(if %
|
||||
(on-error {:error (.-err %) :message (.-message %)})
|
||||
(on-start)))))
|
||||
|
||||
(defn stop-recording [recorder on-stop on-error]
|
||||
(if (and recorder (#{RECORDING PAUSED} (get-state recorder)))
|
||||
(.stop ^js recorder #(if %
|
||||
(on-error {:error (.-err %) :message (.-message %)})
|
||||
(on-stop)))
|
||||
(on-stop)))
|
||||
|
||||
(defn pause-recording [recorder on-pause on-error]
|
||||
(when (and recorder (.-isRecording ^js recorder))
|
||||
(.pause ^js recorder #(if %
|
||||
(on-error {:error (.-err %) :message (.-message %)})
|
||||
(on-pause)))))
|
||||
|
||||
(defn start-playing [player on-start on-error]
|
||||
(when (and player (.-canPlay ^js player))
|
||||
(.play ^js player #(if %
|
||||
(on-error {:error (.-err %) :message (.-message %)})
|
||||
(on-start)))))
|
||||
|
||||
(defn stop-playing [player on-stop on-error]
|
||||
(if (and player (.-isPlaying ^js player))
|
||||
(.stop ^js player #(if %
|
||||
(on-error {:error (.-err %) :message (.-message %)})
|
||||
(on-stop)))
|
||||
(on-stop)))
|
||||
|
||||
(defn get-recorder-file-path [recorder]
|
||||
(when recorder
|
||||
(.-fsPath ^js recorder)))
|
||||
|
||||
(defn get-player-duration [player]
|
||||
(when (and player (.-canPlay ^js player))
|
||||
(.-duration ^js player)))
|
||||
|
||||
(defn get-player-current-time [player]
|
||||
(when (and player (.-canPlay ^js player))
|
||||
(.-currentTime ^js player)))
|
||||
|
||||
(defn toggle-playpause-player [player on-play on-pause on-error]
|
||||
(when (and player (.-canPlay ^js player))
|
||||
(.playPause ^js player (fn [error pause?]
|
||||
(if error
|
||||
(on-error {:error (.-err error) :message (.-message error)})
|
||||
(if pause?
|
||||
(on-pause)
|
||||
(on-play)))))))
|
||||
|
||||
(defn seek-player [player value on-seek on-error]
|
||||
(when (and player (.-canPlay ^js player))
|
||||
(.seek ^js player value #(if %
|
||||
(on-error {:error (.-err %) :message (.-message %)})
|
||||
(on-seek)))))
|
||||
|
||||
(defn can-play? [player]
|
||||
(and player (.-canPlay ^js player)))
|
||||
|
||||
(defn destroy-recorder [recorder]
|
||||
(stop-recording recorder
|
||||
#(when (and recorder (not= (get-state recorder) DESTROYED))
|
||||
(.destroy ^js recorder))
|
||||
#()))
|
||||
|
||||
(defn destroy-player [player]
|
||||
(stop-playing player
|
||||
#(when (and player (not= (get-state player) IDLE))
|
||||
(.destroy ^js player))
|
||||
#()))
|
|
@ -6,6 +6,7 @@
|
|||
[status-im.chat.models.message :as chat.message]
|
||||
[status-im.chat.models.message-content :as message-content]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.utils.datetime :as datetime]
|
||||
[status-im.utils.fx :as fx]
|
||||
["emojilib" :as emojis]))
|
||||
|
@ -106,7 +107,16 @@
|
|||
(chat.message/send-message {:chat-id current-chat-id
|
||||
:content-type constants/content-type-image
|
||||
:image-path (string/replace image-path #"file://" "")
|
||||
:text "Update to latest version to see a nice image here!"})))))
|
||||
:text (i18n/label :t/update-to-see-image)})))))
|
||||
|
||||
(fx/defn send-audio-message
|
||||
[cofx audio-path duration current-chat-id]
|
||||
(when-not (string/blank? audio-path)
|
||||
(chat.message/send-message cofx {:chat-id current-chat-id
|
||||
:content-type constants/content-type-audio
|
||||
:audio-path audio-path
|
||||
:audio-duration-ms duration
|
||||
:text (i18n/label :t/update-to-listen-audio)})))
|
||||
|
||||
(fx/defn send-sticker-message
|
||||
[cofx {:keys [hash pack]} current-chat-id]
|
||||
|
@ -115,7 +125,7 @@
|
|||
:content-type constants/content-type-sticker
|
||||
:sticker {:hash hash
|
||||
:pack pack}
|
||||
:text "Update to latest version to see a nice sticker here!"})))
|
||||
:text (i18n/label :t/update-to-see-sticker)})))
|
||||
|
||||
(fx/defn send-current-message
|
||||
"Sends message from current chat input"
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
(def content-type-command 5)
|
||||
(def content-type-system-text 6)
|
||||
(def content-type-image 7)
|
||||
(def content-type-audio 8)
|
||||
|
||||
(def message-type-one-to-one 1)
|
||||
(def message-type-public-group 2)
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
:contentType :content-type
|
||||
:clock :clock-value
|
||||
:quotedMessage :quoted-message
|
||||
:outgoingStatus :outgoing-status})
|
||||
:outgoingStatus :outgoing-status
|
||||
:audioDurationMs :audio-duration-ms})
|
||||
|
||||
(update :outgoing-status keyword)
|
||||
(update :command-parameters clojure.set/rename-keys {:transactionHash :transaction-hash
|
||||
|
|
|
@ -560,6 +560,11 @@
|
|||
{})
|
||||
(chat.input/send-sticker-message sticker current-chat-id))))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:chat/send-audio
|
||||
(fn [{{:keys [current-chat-id]} :db :as cofx} [_ audio-path duration]]
|
||||
(chat.input/send-audio-message cofx audio-path duration current-chat-id)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:chat/disable-cooldown
|
||||
(fn [cofx _]
|
||||
|
@ -1238,7 +1243,8 @@
|
|||
(fx/defn on-going-in-background [{:keys [db now]}]
|
||||
{:db (-> db
|
||||
(dissoc :app-active-since)
|
||||
(assoc :app-in-background-since now))})
|
||||
(assoc :app-in-background-since now))
|
||||
:dispatch-n [[:audio-recorder/on-background] [:audio-message/on-background]]})
|
||||
|
||||
(defn app-state-change [state {:keys [db] :as cofx}]
|
||||
(let [app-coming-from-background? (= state "active")
|
||||
|
|
|
@ -364,3 +364,11 @@
|
|||
[key-uid callback]
|
||||
(log/debug "[native-module] delete-multiaccount")
|
||||
(.deleteMultiaccount ^js (status) key-uid callback))
|
||||
|
||||
(defn activate-keep-awake []
|
||||
(log/debug "[native-module] activateKeepAwake")
|
||||
(.activateKeepAwake ^js (status)))
|
||||
|
||||
(defn deactivate-keep-awake []
|
||||
(log/debug "[native-module] deactivateKeepAwake")
|
||||
(.deactivateKeepAwake ^js (status)))
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
response-to
|
||||
ens-name
|
||||
image-path
|
||||
audio-path
|
||||
audio-duration-ms
|
||||
message-type
|
||||
sticker
|
||||
content-type]
|
||||
|
@ -22,6 +24,8 @@
|
|||
:responseTo response-to
|
||||
:ensName ens-name
|
||||
:imagePath image-path
|
||||
:audioPath audio-path
|
||||
:audioDurationMs audio-duration-ms
|
||||
:sticker sticker
|
||||
:contentType content-type}]
|
||||
:on-success
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
;; RED
|
||||
(def red (:red light)) ;; Used to highlight errors or "dangerous" actions
|
||||
(def red-transparent-10 (alpha red 0.1)) ;;action-row ;; ttt finish
|
||||
(def red-audio-recorder "#fa6565")
|
||||
|
||||
;; GREEN
|
||||
(def green "#44d058") ;; icon for successful inboud transaction
|
||||
|
|
|
@ -1,26 +1,33 @@
|
|||
(ns status-im.ui.components.permissions
|
||||
(:require [status-im.utils.platform :as platform]
|
||||
["react-native" :refer (PermissionsAndroid)]))
|
||||
["react-native-permissions" :refer (requestMultiple PERMISSIONS RESULTS)]))
|
||||
|
||||
(def permissions-map
|
||||
{:read-external-storage "android.permission.READ_EXTERNAL_STORAGE"
|
||||
:write-external-storage "android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
:camera "android.permission.CAMERA"})
|
||||
{:read-external-storage (cond
|
||||
platform/android? (.-READ_EXTERNAL_STORAGE (.-ANDROID PERMISSIONS)))
|
||||
:write-external-storage (cond
|
||||
platform/android? (.-WRITE_EXTERNAL_STORAGE (.-ANDROID PERMISSIONS)))
|
||||
:camera (cond
|
||||
platform/android? (.-CAMERA (.-ANDROID PERMISSIONS))
|
||||
platform/ios? (.-CAMERA (.-IOS PERMISSIONS)))
|
||||
:record-audio (cond
|
||||
platform/android? (.-RECORD_AUDIO (.-ANDROID PERMISSIONS))
|
||||
platform/ios? (.-MICROPHONE (.-IOS PERMISSIONS)))})
|
||||
|
||||
(defn all-granted? [permissions]
|
||||
(let [permission-vals (distinct (vals permissions))]
|
||||
(and (= (count permission-vals) 1)
|
||||
(not (#{"denied" "never_ask_again"} (first permission-vals))))))
|
||||
(not (#{(.-BLOCKED RESULTS) (.-DENIED RESULTS)} (first permission-vals))))))
|
||||
|
||||
(defn request-permissions
|
||||
[{:keys [permissions on-allowed on-denied]
|
||||
:or {on-allowed #()
|
||||
on-denied #()}}]
|
||||
(if platform/android?
|
||||
(let [permissions (mapv #(get permissions-map %) permissions)]
|
||||
(-> (.requestMultiple PermissionsAndroid (clj->js permissions))
|
||||
(let [permissions (remove nil? (mapv #(get permissions-map %) permissions))]
|
||||
(if (empty? permissions)
|
||||
(on-allowed)
|
||||
(-> (requestMultiple (clj->js permissions))
|
||||
(.then #(if (all-granted? (js->clj %))
|
||||
(on-allowed)
|
||||
(on-denied)))
|
||||
(.catch on-denied)))
|
||||
(on-allowed)))
|
||||
(.catch on-denied)))))
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
(ns status-im.ui.components.slider
|
||||
(:require [reagent.core :as reagent]
|
||||
["react-native" :refer (Animated)]
|
||||
["@react-native-community/slider" :default Slider]))
|
||||
|
||||
(def slider (reagent/adapt-react-class Slider))
|
||||
|
||||
(def animated-slider
|
||||
(reagent/adapt-react-class (.createAnimatedComponent Animated Slider)))
|
|
@ -0,0 +1,48 @@
|
|||
(ns status-im.ui.screens.chat.audio-message.styles
|
||||
(:require [status-im.ui.components.colors :as colors]))
|
||||
|
||||
(def container
|
||||
{:flex 1
|
||||
:flex-direction :column
|
||||
:justify-content :space-around})
|
||||
|
||||
(def timer
|
||||
{:font-size 28
|
||||
:line-height 38
|
||||
:align-self :center})
|
||||
|
||||
(def buttons-container
|
||||
{:flex 1
|
||||
:max-height 80
|
||||
:flex-direction :row
|
||||
:align-items :center
|
||||
:justify-content :space-around
|
||||
:align-self :stretch
|
||||
:padding-horizontal 80})
|
||||
|
||||
(def rec-button-base-size 61)
|
||||
|
||||
(def rec-button-container
|
||||
{:width rec-button-base-size
|
||||
:height rec-button-base-size
|
||||
:align-items "center"})
|
||||
|
||||
(defn rec-outer-circle [scale-anim]
|
||||
{:position "absolute"
|
||||
:width rec-button-base-size
|
||||
:height rec-button-base-size
|
||||
:top 0
|
||||
:border-width 4
|
||||
:transform [{:scale scale-anim}]
|
||||
:border-color colors/red-audio-recorder
|
||||
:border-radius rec-button-base-size})
|
||||
|
||||
(defn rec-inner-circle [scale-anim border-radius-anim]
|
||||
{:position "absolute"
|
||||
:top 6
|
||||
:left 6
|
||||
:bottom 6
|
||||
:right 6
|
||||
:transform [{:scale scale-anim}]
|
||||
:border-radius border-radius-anim
|
||||
:background-color colors/red-audio-recorder})
|
|
@ -0,0 +1,292 @@
|
|||
(ns status-im.ui.screens.chat.audio-message.views
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require
|
||||
[goog.string :as gstring]
|
||||
[reagent.core :as reagent]
|
||||
[status-im.audio.core :as audio]
|
||||
[status-im.ui.components.react :as react]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.i18n :as i18n]
|
||||
[quo.components.animated.pressable :as pressable]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.ui.screens.chat.components.input :as input]
|
||||
[status-im.ui.screens.chat.components.style :as input.style]
|
||||
[status-im.ui.screens.chat.audio-message.styles :as styles]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.animation :as anim]
|
||||
[status-im.ui.components.icons.vector-icons :as icons]
|
||||
[status-im.utils.utils :as utils.utils]
|
||||
[status-im.utils.fs :as fs]
|
||||
[status-im.utils.fx :as fx]))
|
||||
|
||||
;; reference db levels
|
||||
(def total-silence-db -160)
|
||||
(def silence-db -35)
|
||||
(def max-db 0)
|
||||
|
||||
;; update interval for the pulsing rec button
|
||||
(def metering-interval 100)
|
||||
|
||||
;; rec pulse animation target
|
||||
(defonce visual-target-value (anim/create-value total-silence-db))
|
||||
;;ensure animation finishes before next meter update
|
||||
(defonce metering-anim-duration (int (* metering-interval 0.9)))
|
||||
|
||||
(defn update-meter [meter-data]
|
||||
(let [value (if meter-data
|
||||
(.-value ^js meter-data)
|
||||
total-silence-db)]
|
||||
(anim/start (anim/timing visual-target-value {:toValue value
|
||||
:duration metering-anim-duration
|
||||
:useNativeDriver true}))))
|
||||
|
||||
(def base-filename "am.")
|
||||
(def default-format "aac")
|
||||
(def rec-options (merge
|
||||
audio/default-recorder-options
|
||||
{:filename (str base-filename default-format)
|
||||
:meteringInterval metering-interval}))
|
||||
|
||||
;; maximum 2 minutes of recordings time
|
||||
;; to keep data under 900k
|
||||
(def max-recording-ms (* 2 60 1000))
|
||||
|
||||
;; audio objects
|
||||
(defonce recorder-ref (atom nil))
|
||||
(defonce player-ref (atom nil))
|
||||
|
||||
(defn destroy-recorder []
|
||||
(audio/destroy-recorder @recorder-ref)
|
||||
(reset! recorder-ref nil))
|
||||
|
||||
(defn destroy-player []
|
||||
(audio/destroy-player @player-ref)
|
||||
(reset! player-ref nil))
|
||||
|
||||
;; state update callback
|
||||
(defonce state-cb (atom #()))
|
||||
|
||||
;; max recording ms reached callback
|
||||
(defonce max-recording-reached-cb (atom #()))
|
||||
|
||||
;; to be called when app goes in background
|
||||
(defonce on-background-cb (atom #()))
|
||||
|
||||
(fx/defn on-background
|
||||
{:events [:audio-recorder/on-background]}
|
||||
[_]
|
||||
(when @on-background-cb
|
||||
(@on-background-cb))
|
||||
nil)
|
||||
|
||||
;; during recording
|
||||
(defonce recording-timer (atom nil))
|
||||
(defonce recording-start-ts (atom nil))
|
||||
(defonce recording-backlog-ms (atom 0))
|
||||
|
||||
;; updates timer UI
|
||||
(defn update-timer [timer]
|
||||
(let [ms (if @recording-start-ts
|
||||
(+
|
||||
(- (js/Date.now) @recording-start-ts)
|
||||
@recording-backlog-ms)
|
||||
@recording-backlog-ms)
|
||||
s (quot ms 1000)]
|
||||
(if (> ms max-recording-ms)
|
||||
(@max-recording-reached-cb)
|
||||
(reset! timer (gstring/format "%d:%02d" (quot s 60) (mod s 60))))))
|
||||
|
||||
(defn reset-timer [timer]
|
||||
(reset! timer "0:00")
|
||||
(reset! recording-backlog-ms 0))
|
||||
|
||||
(defn animate-buttons [rec? show-ctrl? {:keys [rec-button-anim-value ctrl-buttons-anim-value]}]
|
||||
(anim/start
|
||||
(anim/parallel
|
||||
[(anim/timing rec-button-anim-value {:toValue (if rec? 1 0)
|
||||
:duration 100
|
||||
:useNativeDriver true})
|
||||
(anim/timing ctrl-buttons-anim-value {:toValue (if show-ctrl? 1 0)
|
||||
:duration 100
|
||||
:useNativeDriver true})])))
|
||||
|
||||
(defn start-recording [{:keys [timer] :as params}]
|
||||
(if (> @recording-backlog-ms max-recording-ms)
|
||||
(@max-recording-reached-cb)
|
||||
(do
|
||||
(animate-buttons true true params)
|
||||
(reset! recording-start-ts (js/Date.now))
|
||||
(reset! recording-timer (utils.utils/set-interval #(update-timer timer) 1000))
|
||||
(audio/start-recording
|
||||
@recorder-ref
|
||||
@state-cb
|
||||
#(utils.utils/show-popup (i18n/label :t/audio-recorder-error) (:message %))))))
|
||||
|
||||
(defn reload-recorder []
|
||||
(when @recorder-ref
|
||||
(destroy-recorder))
|
||||
(reset! recorder-ref (audio/new-recorder rec-options #(update-meter %) @state-cb))
|
||||
;; we skip preparation since if a recorder is prepared, player wont play
|
||||
(@state-cb))
|
||||
|
||||
(defn reload-player
|
||||
([] (reload-player nil))
|
||||
([on-success]
|
||||
(when @player-ref
|
||||
(destroy-player))
|
||||
(reset! player-ref (audio/new-player
|
||||
(:filename rec-options)
|
||||
{:autoDestroy false
|
||||
:continuesToPlayInBackground false}
|
||||
@state-cb))
|
||||
(audio/prepare-player
|
||||
@player-ref
|
||||
#(do (@state-cb) (when on-success (on-success)))
|
||||
#(utils.utils/show-popup (i18n/label :t/audio-recorder-error) (:message %)))))
|
||||
|
||||
(defn stop-recording [{:keys [on-success timer max-recording-reached?] :as params}]
|
||||
(when @recording-timer
|
||||
(utils.utils/clear-interval @recording-timer)
|
||||
(reset! recording-timer nil))
|
||||
(if max-recording-reached?
|
||||
(reset! recording-backlog-ms (+ @recording-backlog-ms (- (js/Date.now) @recording-start-ts)))
|
||||
(reset-timer timer))
|
||||
(audio/stop-recording
|
||||
@recorder-ref
|
||||
#(do
|
||||
(update-meter nil)
|
||||
(reload-recorder)
|
||||
(reload-player on-success))
|
||||
#(utils.utils/show-popup (i18n/label :t/audio-recorder-error) (:message %)))
|
||||
(animate-buttons false max-recording-reached? params))
|
||||
|
||||
(defn pause-recording [{:keys [timer] :as params}]
|
||||
(when @recording-timer
|
||||
(utils.utils/clear-interval @recording-timer)
|
||||
(reset! recording-backlog-ms (+ @recording-backlog-ms (- (js/Date.now) @recording-start-ts)))
|
||||
(reset! recording-start-ts nil)
|
||||
(reset! recording-timer nil)
|
||||
(update-timer timer))
|
||||
(audio/pause-recording
|
||||
@recorder-ref
|
||||
#(do (update-meter nil)
|
||||
(@state-cb))
|
||||
#(utils.utils/show-popup (i18n/label :t/audio-recorder-error) (:message %)))
|
||||
(animate-buttons false true params))
|
||||
|
||||
(defn update-state
|
||||
"update main UI state.
|
||||
general states are:
|
||||
- :recording
|
||||
- :playing
|
||||
- :ready-to-send
|
||||
- :recording-paused
|
||||
- :ready-to-record"
|
||||
[state-ref]
|
||||
(let [player-state (audio/get-state @player-ref)
|
||||
recorder-state (audio/get-state @recorder-ref)
|
||||
output-file (or
|
||||
(audio/get-recorder-file-path @recorder-ref)
|
||||
(:output-file @state-ref))
|
||||
general (cond
|
||||
(= recorder-state audio/RECORDING) :recording
|
||||
(= player-state audio/PLAYING) :playing
|
||||
(= player-state audio/PREPARED) :ready-to-send
|
||||
(= recorder-state audio/PAUSED) :recording-paused
|
||||
:else :ready-to-record)
|
||||
new-state {:general general
|
||||
:cancel-disabled? (nil? (#{:recording :recording-paused :ready-to-send} general))
|
||||
:output-file output-file
|
||||
:duration (audio/get-player-duration @player-ref)}]
|
||||
(if (#{:recording :recording-paused} general)
|
||||
(status/activate-keep-awake)
|
||||
(status/deactivate-keep-awake))
|
||||
(when (not= @state-ref new-state)
|
||||
(reset! state-ref new-state))))
|
||||
|
||||
(defn send-audio-msessage [state-ref]
|
||||
(re-frame/dispatch [:chat/send-audio
|
||||
(:output-file @state-ref)
|
||||
(int (:duration @state-ref))])
|
||||
(destroy-player)
|
||||
(@state-cb))
|
||||
|
||||
;; rec-button-anim-value 0 => stopped, 1 => recording
|
||||
(defview rec-button-view [{:keys [rec-button-anim-value state] :as params}]
|
||||
(letsubs [outer-scale (anim/interpolate visual-target-value {:inputRange [total-silence-db silence-db 0]
|
||||
:outputRange [1 0.8 1.2]})
|
||||
inner-scale (anim/interpolate rec-button-anim-value {:inputRange [0 1]
|
||||
:outputRange [1 0.5]})
|
||||
inner-border-radius (anim/interpolate rec-button-anim-value {:inputRange [0 1]
|
||||
:outputRange [styles/rec-button-base-size 16]})]
|
||||
[react/touchable-highlight {:on-press #(if (= (:general @state) :recording)
|
||||
(pause-recording params)
|
||||
(start-recording params))}
|
||||
[react/view {:style styles/rec-button-container}
|
||||
[react/animated-view {:style (styles/rec-outer-circle outer-scale)}]
|
||||
[react/animated-view {:style (styles/rec-inner-circle inner-scale inner-border-radius)}]]]))
|
||||
|
||||
(defn- cancel-button [disabled? on-press]
|
||||
[pressable/pressable {:type :scale
|
||||
:disabled disabled?
|
||||
:on-press on-press}
|
||||
[react/view {:style (input.style/send-message-button)}
|
||||
[icons/icon :main-icons/close
|
||||
{:container-style (merge (input.style/send-message-container) {:background-color colors/gray})
|
||||
:accessibility-label :cancel-message-button
|
||||
:color colors/white-persist}]]])
|
||||
|
||||
(defview audio-message-view []
|
||||
(letsubs [rec-button-anim-value (anim/create-value 0)
|
||||
ctrl-buttons-anim-value (anim/create-value 0)
|
||||
timer (reagent/atom "")
|
||||
state (reagent/atom nil)]
|
||||
{:component-did-mount (fn []
|
||||
(reset-timer timer)
|
||||
(reset! state-cb #(update-state state))
|
||||
(reset! max-recording-reached-cb #(do
|
||||
(when (= (:general @state) :recording)
|
||||
(stop-recording {:rec-button-anim-value rec-button-anim-value
|
||||
:ctrl-buttons-anim-value ctrl-buttons-anim-value
|
||||
:timer timer
|
||||
:max-recording-reached? true}))
|
||||
(utils.utils/show-popup (i18n/label :t/audio-recorder)
|
||||
(i18n/label :t/audio-recorder-max-ms-reached))))
|
||||
(reset! on-background-cb #(when (= (:general @state) :recording)
|
||||
(pause-recording {:rec-button-anim-value rec-button-anim-value
|
||||
:ctrl-buttons-anim-value ctrl-buttons-anim-value
|
||||
:timer timer})))
|
||||
(reload-recorder))
|
||||
:component-will-unmount (fn []
|
||||
(when @recording-timer
|
||||
(utils.utils/clear-interval @recording-timer)
|
||||
(reset! recording-timer nil))
|
||||
(destroy-recorder)
|
||||
(destroy-player)
|
||||
(when (:output-file @state)
|
||||
; possible issue if message is not yet sent?
|
||||
(fs/unlink (:output-file @state)))
|
||||
(reset! state-cb nil)
|
||||
(reset! max-recording-reached-cb nil)
|
||||
(reset! on-background-cb nil))}
|
||||
(let [base-params {:rec-button-anim-value rec-button-anim-value
|
||||
:ctrl-buttons-anim-value ctrl-buttons-anim-value
|
||||
:timer timer}]
|
||||
[react/view {:style styles/container}
|
||||
[react/text {:style styles/timer} @timer]
|
||||
[react/view {:style styles/buttons-container}
|
||||
[react/animated-view {:style {:opacity ctrl-buttons-anim-value}}
|
||||
[cancel-button (:cancel-disabled? @state) #(stop-recording base-params)]]
|
||||
[rec-button-view (merge base-params {:state state})]
|
||||
[react/animated-view {:style {:opacity ctrl-buttons-anim-value}}
|
||||
[input/send-button {:on-send-press (fn [] (cond
|
||||
(= :ready-to-send (:general @state))
|
||||
(do
|
||||
(reset-timer timer)
|
||||
(animate-buttons false false base-params)
|
||||
(send-audio-msessage state))
|
||||
|
||||
(#{:recording :recording-paused} (:general @state))
|
||||
(stop-recording (merge base-params
|
||||
{:on-success
|
||||
#(send-audio-msessage state)}))))}]]]])))
|
|
@ -6,6 +6,7 @@
|
|||
[quo.design-system.colors :as colors]
|
||||
[status-im.ui.screens.chat.components.style :as styles]
|
||||
[status-im.ui.screens.chat.components.reply :as reply]
|
||||
[status-im.utils.utils :as utils.utils]
|
||||
[quo.components.animated.pressable :as pressable]
|
||||
[quo.animated :as animated]
|
||||
[status-im.utils.config :as config]
|
||||
|
@ -37,12 +38,25 @@
|
|||
[icons/icon :main-icons/keyboard (styles/icon false)]
|
||||
[icons/icon :main-icons/stickers (styles/icon false)])]])
|
||||
|
||||
(defn- request-record-audio-permission [set-active panel]
|
||||
(re-frame/dispatch
|
||||
[:request-permissions
|
||||
{:permissions [:record-audio]
|
||||
:on-allowed
|
||||
#(set-active panel)
|
||||
:on-denied
|
||||
#(utils.utils/set-timeout
|
||||
(fn []
|
||||
(utils.utils/show-popup (i18n/label :t/audio-recorder-error)
|
||||
(i18n/label :t/audio-recorder-permissions-error)))
|
||||
50)}]))
|
||||
|
||||
(defn touchable-audio-icon [{:keys [panel active set-active accessibility-label input-focus]}]
|
||||
[pressable/pressable {:type :scale
|
||||
:accessibility-label accessibility-label
|
||||
:on-press #(if (= active panel)
|
||||
(input-focus)
|
||||
(set-active panel))}
|
||||
(request-record-audio-permission set-active panel))}
|
||||
[rn/view {:style (styles/in-input-touchable-icon)}
|
||||
[icons/icon
|
||||
(panel->icons panel)
|
||||
|
@ -116,6 +130,7 @@
|
|||
[touchable-audio-icon {:panel :audio
|
||||
:accessibility-label :show-audio-message-icon
|
||||
:active active-panel
|
||||
:input-focus input-focus
|
||||
:set-active set-active-panel}])]]]])
|
||||
|
||||
(defn chat-toolbar []
|
||||
|
@ -151,8 +166,8 @@
|
|||
one-to-one-chat?
|
||||
(or config/commands-enabled? mainnet?)
|
||||
(not reply))
|
||||
show-audio (and false ;TODO: remove to enable audio icon
|
||||
empty-text
|
||||
show-audio (and empty-text
|
||||
(not sending-image)
|
||||
(not reply)
|
||||
(not public?))]
|
||||
(when-not (= reply @had-reply)
|
||||
|
|
|
@ -103,7 +103,8 @@
|
|||
:align-items :center})
|
||||
|
||||
(defn in-input-buttons []
|
||||
{:height 34})
|
||||
{:flex-direction :row
|
||||
:height 34})
|
||||
|
||||
(defn send-icon-color []
|
||||
colors/white)
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
(ns status-im.ui.screens.chat.message.audio
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [status-im.utils.utils :as utils]
|
||||
[reagent.core :as reagent]
|
||||
[goog.string :as gstring]
|
||||
[status-im.audio.core :as audio]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.ui.screens.chat.styles.message.audio :as style]
|
||||
[status-im.ui.components.animation :as anim]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.icons.vector-icons :as icons]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.components.slider :as slider]))
|
||||
|
||||
(defn message-press-handlers [_]
|
||||
;;TBI save audio file?
|
||||
)
|
||||
|
||||
(defonce player-ref (atom nil))
|
||||
(defonce current-player-message-id (atom nil))
|
||||
(defonce current-active-state-ref-ref (atom nil))
|
||||
(defonce progress-timer (atom nil))
|
||||
|
||||
(defn start-stop-progress-timer [{:keys [state-ref progress-ref progress-anim]} start?]
|
||||
(when @progress-timer
|
||||
(utils/clear-interval @progress-timer)
|
||||
(when-not start?
|
||||
(reset! progress-timer nil)))
|
||||
(when start?
|
||||
(when @progress-timer
|
||||
(utils/clear-interval @progress-timer))
|
||||
(reset! progress-timer (utils/set-interval
|
||||
#(when (and @state-ref (not (:slider-seeking @state-ref)))
|
||||
(let [ct (audio/get-player-current-time @player-ref)]
|
||||
(reset! progress-ref ct)
|
||||
(when ct
|
||||
(anim/start (anim/timing progress-anim {:toValue @progress-ref
|
||||
:duration 100
|
||||
:easing (.-linear ^js anim/easing)
|
||||
:useNativeDriver true})))))
|
||||
100))))
|
||||
|
||||
(defn update-state [{:keys [state-ref progress-ref progress-anim message-id seek-to-ms audio-duration-ms slider-new-state-seeking? unloaded? error]}]
|
||||
(let [player-state (audio/get-state @player-ref)
|
||||
slider-seeking (if (some? slider-new-state-seeking?)
|
||||
slider-new-state-seeking?
|
||||
(:slider-seeking @state-ref))
|
||||
general (cond
|
||||
(some? error) :error
|
||||
(or unloaded? (not= message-id @current-player-message-id)) :not-loaded
|
||||
slider-seeking (:general @state-ref) ; persist player state at the time user started sliding
|
||||
(= player-state audio/PLAYING) :playing
|
||||
(= player-state audio/PAUSED) :paused
|
||||
(= player-state audio/SEEKING) :seeking
|
||||
(= player-state audio/PREPARED) :ready-to-play
|
||||
:else :preparing)
|
||||
new-state {:general general
|
||||
:error-msg error
|
||||
:duration (cond (not (#{:preparing :not-loaded :error} general))
|
||||
(audio/get-player-duration @player-ref)
|
||||
|
||||
audio-duration-ms audio-duration-ms
|
||||
|
||||
:else (:duration @state-ref))
|
||||
:progress-ref (or progress-ref (:progress-ref @state-ref))
|
||||
:progress-anim (or progress-anim (:progress-anim @state-ref))
|
||||
:slider-seeking slider-seeking
|
||||
|
||||
; persist seek-to-ms while seeking or audio is not loaded
|
||||
:seek-to-ms (when (or
|
||||
slider-seeking
|
||||
(#{:preparing :not-loaded :error} general))
|
||||
(or seek-to-ms (:seek-to-ms @state-ref)))}]
|
||||
; update state if needed
|
||||
(when (not= @state-ref new-state)
|
||||
(reset! state-ref new-state))
|
||||
|
||||
; update progress UI on slider release
|
||||
(when (and (some? slider-new-state-seeking?) (not slider-new-state-seeking?) (some? seek-to-ms))
|
||||
(reset! (:progress-ref new-state) seek-to-ms))
|
||||
|
||||
; update progres anim value to follow the slider
|
||||
(when (and slider-seeking (some? seek-to-ms))
|
||||
(anim/set-value (:progress-anim new-state) seek-to-ms))
|
||||
|
||||
; on unload, reset values
|
||||
(when unloaded?
|
||||
(reset! (:progress-ref new-state) 0)
|
||||
(anim/set-value (:progress-anim new-state) 0))))
|
||||
|
||||
(defn destroy-player [{:keys [message-id reloading?]}]
|
||||
(when (and @player-ref (or reloading?
|
||||
(= message-id @current-player-message-id)))
|
||||
(audio/destroy-player @player-ref)
|
||||
(reset! player-ref nil)
|
||||
(when @current-active-state-ref-ref
|
||||
(update-state {:state-ref @current-active-state-ref-ref :unloaded? true}))
|
||||
(reset! current-player-message-id nil)
|
||||
(reset! current-active-state-ref-ref nil)))
|
||||
|
||||
(defonce last-seek (atom (js/Date.now)))
|
||||
|
||||
(defn seek [{:keys [message-id] :as params} value immediate? on-success]
|
||||
(when (and @player-ref (= message-id @current-player-message-id))
|
||||
(let [now (js/Date.now)]
|
||||
(when (or immediate? (> (- now @last-seek) 200))
|
||||
(reset! last-seek (js/Date.now))
|
||||
(audio/seek-player
|
||||
@player-ref
|
||||
value
|
||||
#(do
|
||||
(update-state params)
|
||||
(when on-success (on-success)))
|
||||
#(update-state (merge params {:error (:message %)}))))))
|
||||
(update-state (merge params {:seek-to-ms value})))
|
||||
|
||||
(defn reload-player [{:keys [message-id state-ref] :as params} base64-data on-success]
|
||||
;; to avoid reloading player while is initializing,
|
||||
;; we go ahead only if there is no player or
|
||||
;; if it is already prepared
|
||||
(when (or (nil? @player-ref) (audio/can-play? @player-ref))
|
||||
(when @player-ref
|
||||
(destroy-player (merge params {:reloading? true})))
|
||||
(reset! player-ref (audio/new-player
|
||||
base64-data
|
||||
{:autoDestroy false
|
||||
:continuesToPlayInBackground false}
|
||||
#(seek params 0 true nil)))
|
||||
(audio/prepare-player
|
||||
@player-ref
|
||||
#(when on-success (on-success))
|
||||
#(update-state (merge params {:error (:message %)})))
|
||||
(reset! current-player-message-id message-id)
|
||||
(reset! current-active-state-ref-ref state-ref)
|
||||
(update-state params)))
|
||||
|
||||
(defn play-pause [{:keys [message-id state-ref] :as params} audio]
|
||||
(if (not= message-id @current-player-message-id)
|
||||
;; player has audio from another message, we need to reload
|
||||
(reload-player params
|
||||
audio
|
||||
;; on-success: audio is loaded, do we have an existing value to seek to?
|
||||
#(if-some [seek-time (:seek-to-ms @state-ref)]
|
||||
;; check seek time against real audio duration and play
|
||||
(let [checked-seek-time (min (audio/get-player-duration @player-ref) seek-time)]
|
||||
(seek params
|
||||
checked-seek-time
|
||||
true
|
||||
(fn [] (play-pause params audio))))
|
||||
|
||||
;; nothing to seek to, play
|
||||
(play-pause params audio)))
|
||||
|
||||
;; loaded audio corresponds to current message we can play
|
||||
(when @player-ref
|
||||
(audio/toggle-playpause-player
|
||||
@player-ref
|
||||
#(do
|
||||
(start-stop-progress-timer params true)
|
||||
(update-state params))
|
||||
#(do
|
||||
(start-stop-progress-timer params false)
|
||||
(update-state params))
|
||||
#(update-state (merge params {:error (:message %)}))))))
|
||||
|
||||
(defn- play-pause-button [state-ref outgoing on-press]
|
||||
(let [color (if outgoing colors/blue colors/white-persist)]
|
||||
(if (= (:general @state-ref) :preparing)
|
||||
[react/view {:style (style/play-pause-container outgoing true)}
|
||||
[react/small-loading-indicator color]]
|
||||
[react/touchable-highlight {:on-press on-press}
|
||||
[icons/icon (case (:general @state-ref)
|
||||
:playing :main-icons/pause
|
||||
:main-icons/play)
|
||||
{:container-style (style/play-pause-container outgoing false)
|
||||
:accessibility-label :play-pause-audio-message-button
|
||||
:color color}]])))
|
||||
|
||||
(fx/defn on-background
|
||||
{:events [:audio-message/on-background]}
|
||||
[_]
|
||||
(when (and @current-active-state-ref-ref
|
||||
@@current-active-state-ref-ref)
|
||||
(update-state {:state-ref @current-active-state-ref-ref
|
||||
:message-id @current-player-message-id}))
|
||||
nil)
|
||||
|
||||
(defview message-content [{:keys [audio audio-duration-ms message-id outgoing]} timestamp-view]
|
||||
(letsubs [state (reagent/atom nil)
|
||||
progress (reagent/atom 0)
|
||||
progress-anim (anim/create-value 0)
|
||||
width [:dimensions/window-width]]
|
||||
{:component-did-mount (fn []
|
||||
(update-state {:state-ref state
|
||||
:audio-duration-ms audio-duration-ms
|
||||
:message-id message-id
|
||||
:unloaded? true
|
||||
:progress-ref progress
|
||||
:progress-anim progress-anim}))
|
||||
:component-will-unmount (fn []
|
||||
(destroy-player {:state-ref state :message-id message-id})
|
||||
(when (= @current-player-message-id message-id)
|
||||
(reset! current-active-state-ref-ref nil)
|
||||
(reset! current-player-message-id nil))
|
||||
(reset! state nil))}
|
||||
|
||||
(let [base-params {:state-ref state :message-id message-id :progress-ref progress :progress-anim progress-anim}]
|
||||
(if (= (:general @state) :error)
|
||||
[react/text {:style {:typography :main-medium
|
||||
:margin-bottom 16}} (:error-msg @state)]
|
||||
[react/view (style/container width)
|
||||
[react/view style/play-pause-slider-container
|
||||
[play-pause-button state outgoing #(play-pause base-params audio)]
|
||||
[react/view style/slider-container
|
||||
[slider/animated-slider (merge (style/slider outgoing)
|
||||
{:minimum-value 0
|
||||
:maximum-value (:duration @state)
|
||||
:value progress-anim
|
||||
:on-value-change #(seek base-params % false nil)
|
||||
:on-sliding-start #(seek (merge base-params {:slider-new-state-seeking? true}) % true nil)
|
||||
:on-sliding-complete #(seek (merge base-params {:slider-new-state-seeking? false}) % true nil)})]]]
|
||||
|
||||
[react/view style/times-container
|
||||
[react/text {:style (style/timestamp outgoing)}
|
||||
(let [time (cond
|
||||
(or (:slider-seeking @state) (> (:seek-to-ms @state) 0)) (:seek-to-ms @state)
|
||||
(#{:playing :paused :seeking} (:general @state)) @progress
|
||||
:else (:duration @state))
|
||||
s (quot time 1000)]
|
||||
(gstring/format "%02d:%02d" (quot s 60) (mod s 60)))]
|
||||
timestamp-view]]))))
|
|
@ -5,6 +5,7 @@
|
|||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.icons.vector-icons :as vector-icons]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.ui.screens.chat.message.audio :as message.audio]
|
||||
[status-im.ui.screens.chat.message.command :as message.command]
|
||||
[status-im.ui.screens.chat.photos :as photos]
|
||||
[status-im.ui.screens.chat.sheets :as sheets]
|
||||
|
@ -289,6 +290,12 @@
|
|||
{:content (sheets/sticker-long-press message)
|
||||
:height 64}])}))
|
||||
|
||||
(defn message-content-audio
|
||||
[message]
|
||||
[react/touchable-highlight (message.audio/message-press-handlers message)
|
||||
[message-bubble-wrapper message
|
||||
[message.audio/message-content message [message-timestamp message false]]]])
|
||||
|
||||
(defn chat-message [{:keys [public? content content-type] :as message}]
|
||||
(if (= content-type constants/content-type-command)
|
||||
[message.command/command-content message-content-wrapper message]
|
||||
|
@ -313,4 +320,8 @@
|
|||
(not public?))
|
||||
[react/touchable-highlight (image-message-press-handlers message)
|
||||
[message-content-image message]]
|
||||
[unknown-content-type message])))))])))
|
||||
(if (and (= content-type constants/content-type-audio)
|
||||
;; Disabling audio for public-chats
|
||||
(not public?))
|
||||
[message-content-audio message]
|
||||
[unknown-content-type message]))))))])))
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
(ns status-im.ui.screens.chat.styles.message.audio
|
||||
(:require [status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.screens.chat.styles.message.message :as message.style]
|
||||
[status-im.utils.platform :as platform]))
|
||||
|
||||
(defn container [window-width]
|
||||
{:width (* window-width 0.60)
|
||||
:flex-direction :column
|
||||
:justify-content :space-between})
|
||||
|
||||
(def play-pause-slider-container
|
||||
{:flex-direction :row
|
||||
:align-items :center})
|
||||
|
||||
(def slider-container
|
||||
{:flex-direction :column
|
||||
:align-items :stretch
|
||||
:flex-grow 1})
|
||||
|
||||
(defn slider [outgoing]
|
||||
{:style (merge {:margin-left 12
|
||||
:height 34}
|
||||
(when platform/ios? {:margin-bottom 4}))
|
||||
:thumb-tint-color (if outgoing
|
||||
colors/white
|
||||
colors/blue)
|
||||
:minimum-track-tint-color (if outgoing
|
||||
colors/white
|
||||
colors/blue)
|
||||
:maximum-track-tint-color (if outgoing
|
||||
colors/white-transparent
|
||||
colors/gray-transparent-40)})
|
||||
|
||||
(defn play-pause-container [outgoing? loading?]
|
||||
{:background-color (if outgoing? colors/white-persist colors/blue)
|
||||
:width 28
|
||||
:height 28
|
||||
:padding (if loading? 4 2)
|
||||
:border-radius 15})
|
||||
|
||||
(def times-container
|
||||
{:flex-direction :row
|
||||
:justify-content :space-between})
|
||||
|
||||
(defn timestamp [outgoing]
|
||||
(merge (message.style/message-timestamp-text
|
||||
false
|
||||
outgoing
|
||||
false) {:margin-left 40}))
|
|
@ -11,6 +11,7 @@
|
|||
[status-im.ui.screens.chat.sheets :as sheets]
|
||||
[quo.animated :as animated]
|
||||
[quo.react-native :as rn]
|
||||
[status-im.ui.screens.chat.audio-message.views :as audio-message]
|
||||
[status-im.ui.screens.chat.message.message :as message]
|
||||
[status-im.ui.screens.chat.stickers.views :as stickers]
|
||||
[status-im.ui.screens.chat.styles.main :as style]
|
||||
|
@ -177,6 +178,8 @@
|
|||
[extensions/extensions-view]
|
||||
:images
|
||||
[image/image-view]
|
||||
:audio
|
||||
[audio-message/audio-message-view]
|
||||
nil))
|
||||
|
||||
(defn chat []
|
||||
|
|
|
@ -87,6 +87,11 @@
|
|||
:accessibility-label :no-messages-text}
|
||||
(i18n/label :t/image)]
|
||||
|
||||
(= constants/content-type-audio content-type)
|
||||
[react/text {:style styles/last-message-text
|
||||
:accessibility-label :no-messages-text}
|
||||
(i18n/label :t/audio)]
|
||||
|
||||
(string/blank? (:text content))
|
||||
[react/text {:style styles/last-message-text}
|
||||
""]
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
(defn move-file [src dst]
|
||||
(.moveFile react-native-fs src dst))
|
||||
|
||||
(defn stat [path on-stat on-error]
|
||||
(-> (.stat react-native-fs path)
|
||||
(.then on-stat)
|
||||
(.catch on-error)))
|
||||
|
||||
(defn read-file [path encoding on-read on-error]
|
||||
(-> (.readFile react-native-fs path encoding)
|
||||
(.then on-read)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"_comment": "DO NOT EDIT THIS FILE BY HAND. USE 'scripts/update-status-go.sh <tag>' instead",
|
||||
"owner": "status-im",
|
||||
"repo": "status-go",
|
||||
"version": "v0.55.2",
|
||||
"commit-sha1": "0b3cdf7362bbdf9ba7fc11da803105f9417dfbac",
|
||||
"src-sha256": "1vq3z150p0fbwjc1mqmi8lz4vg28dzqhlpsn7kar8j5z4rx5z5hn"
|
||||
"version": "v0.56.1",
|
||||
"commit-sha1": "4574ab4c22ee6b662a7f87c39d0f714998c567dc",
|
||||
"src-sha256": "0jd684lv7x93gxgvvhsv4ihxr5sw2rck2divsxfiql5g2c1v4alg"
|
||||
}
|
||||
|
|
|
@ -1169,5 +1169,13 @@
|
|||
"private-notifications": "Private notifications",
|
||||
"private-notifications-descr": "Status will notify you about new messages. You can edit your notification preferences later in settings.",
|
||||
"maybe-later": "Maybe later",
|
||||
"join": "Join"
|
||||
"join": "Join",
|
||||
"audio-recorder-error": "Recorder error",
|
||||
"audio-recorder": "Recorder",
|
||||
"audio-recorder-max-ms-reached": "Maximum recording time reached",
|
||||
"audio-recorder-permissions-error": "You have to give permission to send audio messages",
|
||||
"audio": "Audio",
|
||||
"update-to-see-image": "Update to latest version to see a nice image here!",
|
||||
"update-to-listen-audio": "Update to latest version to listen to an audio message here!",
|
||||
"update-to-see-sticker": "Update to latest version to see a nice sticker here!"
|
||||
}
|
||||
|
|
25
yarn.lock
25
yarn.lock
|
@ -1207,6 +1207,14 @@
|
|||
"@types/yargs" "^15.0.0"
|
||||
chalk "^3.0.0"
|
||||
|
||||
"@react-native-community/audio-toolkit@git+https://github.com/tbenr/react-native-audio-toolkit.git#v2.0.3-status-v6":
|
||||
version "2.0.3"
|
||||
resolved "git+https://github.com/tbenr/react-native-audio-toolkit.git#7ae9055cf6169b30f5089bda7bfcfc1c40a715e5"
|
||||
dependencies:
|
||||
async "^2.6.3"
|
||||
eventemitter3 "^1.2.0"
|
||||
lodash "^4.17.15"
|
||||
|
||||
"@react-native-community/cameraroll@^1.6.1":
|
||||
version "1.6.2"
|
||||
resolved "https://registry.yarnpkg.com/@react-native-community/cameraroll/-/cameraroll-1.6.2.tgz#a4dedcf8ba7bc938f805dd07dd43a275edb1f411"
|
||||
|
@ -1329,6 +1337,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@react-native-community/netinfo/-/netinfo-4.7.0.tgz#7482d36836cac69d0a0ae25581f65bc472639930"
|
||||
integrity sha512-a/sDB+AsLEUNmhAUlAaTYeXKyQdFGBUfatqKkX5jluBo2CB3OAuTHfm7rSjcaLB9EmG5iSq3fOTpync2E7EYTA==
|
||||
|
||||
"@react-native-community/slider@^3.0.0":
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@react-native-community/slider/-/slider-3.0.0.tgz#ffbf78689fc0572fb5c1e2ccb61b2ef074d3dcd2"
|
||||
integrity sha512-deNc3JHBHz24YN+0DTAocXfrYFIFc1DvsIriMJSsJlR/MvsLzoq2+qwaEN+0/LJ37pstv85wZWY0pNugk4e41g==
|
||||
|
||||
"@react-navigation/bottom-tabs@^5.7.0":
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-5.7.1.tgz#d6b4e61676f8b4ab11864e792c819c56fde32a44"
|
||||
|
@ -1822,7 +1835,7 @@ async-limiter@~1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
|
||||
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
|
||||
|
||||
async@^2.4.0:
|
||||
async@^2.4.0, async@^2.6.3:
|
||||
version "2.6.3"
|
||||
resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
|
||||
integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==
|
||||
|
@ -3251,6 +3264,11 @@ event-target-shim@^5.0.0, event-target-shim@^5.0.1:
|
|||
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
|
||||
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
|
||||
|
||||
eventemitter3@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
|
||||
integrity sha1-HIaZHYFq0eUEdQ5zh0Ik7PO+xQg=
|
||||
|
||||
eventemitter3@^3.0.0:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
|
||||
|
@ -6554,6 +6572,11 @@ react-native-navigation-bar-color@^2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/react-native-navigation-bar-color/-/react-native-navigation-bar-color-2.0.1.tgz#ee2be25cc37105f7da355717b0a9a32c9c059ae6"
|
||||
integrity sha512-1kE/oxWt+HYjRxdZdvke9tJ365xaee5n3+euOQA1En8zQuSbOxiE4SYEGM7TeaWnmLJ0l37mRnPHaB2H4mGh0A==
|
||||
|
||||
react-native-permissions@^2.1.5:
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/react-native-permissions/-/react-native-permissions-2.1.5.tgz#6cfc4f1ab1590f4952299b7cdc9698525ad540e0"
|
||||
integrity sha512-b9KO/4UEV9qddl+kcSybmdk8nlAifclSDBR2rSvc5KZM06vIaJWJNIzK2ZwPXqDQ5yD3CJLuKTRj7Fz+jM9qyQ==
|
||||
|
||||
react-native-reanimated@^1.7.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-1.8.0.tgz#0b5719b20c1fed9aaf8afd9a12e21c9bd46ee428"
|
||||
|
|
Loading…
Reference in New Issue