{"id":382314,"date":"2024-06-29T04:12:06","date_gmt":"2024-06-29T04:12:06","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=382314"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=382314","title":{"rendered":"<span>\u0417\u0430\u043f\u0438\u0441\u044c \u0438 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0433\u043e\u043b\u043e\u0441\u0430, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e\u0442\u043c\u0435\u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u0438 \u0441\u0432\u0430\u0439\u043f\u043e\u043c \u0432\u043f\u0440\u0430\u0432\u043e \u0441 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0435\u0439 \u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c \u0438\u043a\u043e\u043d\u043a\u0438 \u043d\u0430 React Native<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0425\u0430\u0442\u0430\u043c. \u042f \u0440\u0430\u0431\u043e\u0442\u0430\u044e \u0432 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 Neti. \u041a\u043e\u0433\u0434\u0430-\u0442\u043e \u044f \u0431\u044b\u043b \u0432\u0435\u0440\u0441\u0442\u0430\u043b\u044c\u0449\u0438\u043a\u043e\u043c \u0441\u0430\u0439\u0442\u043e\u0432, \u043d\u043e \u043c\u043d\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u0430\u043b\u044c\u0448\u0435. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u0438\u0437\u0443\u0447\u0438\u043b React \u0438 \u043d\u0430\u0443\u0447\u0438\u043b\u0441\u044f \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0430 \u0437\u0430\u0442\u0435\u043c \u0440\u0435\u0448\u0438\u043b \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u0441\u0438\u043b\u044b \u0432 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0434\u0435\u043b\u044e\u0441\u044c \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u043c\u0438 \u0440\u0435\u0448\u0435\u043d\u0438\u0439, \u043a \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043f\u0440\u0438\u0448\u0435\u043b, \u0440\u0430\u0431\u043e\u0442\u0430\u044f \u043d\u0430\u0434 \u0437\u0430\u0434\u0430\u0447\u0435\u0439 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432. \u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0447\u0442\u043e \u043c\u043e\u0439 \u043e\u043f\u044b\u0442 \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u043c\u0443-\u0442\u043e \u043f\u043e\u043b\u0435\u0437\u0435\u043d.<\/p>\n<h2>\u041f\u0440\u043e\u0435\u043a\u0442<\/h2>\n<p>\u041c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u043e\u0435 \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0439 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430\u0445 \u0438 \u043e\u043f\u0430\u0441\u043d\u044b\u0445 \u0444\u0430\u043a\u0442\u043e\u0440\u0430\u0445 \u0434\u043b\u044f \u0436\u0438\u0437\u043d\u0438 \u0438 \u0437\u0434\u043e\u0440\u043e\u0432\u044c\u044f \u043b\u044e\u0434\u0435\u0439 \u043d\u0430 \u0442\u0435\u0440\u0440\u0438\u0442\u043e\u0440\u0438\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u0435\u0434\u043f\u0440\u0438\u044f\u0442\u0438\u044f.<\/p>\n<p>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438: \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u0438 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438, \u043f\u043e\u0434\u0440\u044f\u0434\u0447\u0438\u043a\u0438 \u0438 \u0433\u043e\u0441\u0442\u0438 \u043f\u0440\u0435\u0434\u043f\u0440\u0438\u044f\u0442\u0438\u044f.<\/p>\n<p>\u041f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b: Android, IOS.<\/p>\n<p>\u0421\u0442\u0435\u043a \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439: React Native, MobX.<\/p>\n<h2>\u0417\u0430\u0434\u0430\u0447\u0430<\/h2>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0431\u044b\u043b\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c:\u00a0<\/p>\n<ul>\n<li>\n<p>\u0417\u0430\u043f\u0438\u0441\u044c \u0433\u043e\u043b\u043e\u0441\u0430;<\/p>\n<\/li>\n<li>\n<p>\u041e\u0442\u043c\u0435\u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u0438 \u0441\u0432\u0430\u0439\u043f\u043e\u043c \u0432\u043f\u0440\u0430\u0432\u043e \u0441 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0435\u0439 \u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c \u0438\u043a\u043e\u043d\u043a\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0433\u043e \u0437\u0432\u0443\u043a\u0430.\u00a0<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0433\u043e\u043b\u043e\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u043b \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u0430\u0434\u043c\u0438\u043d\u043a\u0435 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435.<\/p>\n<p>\u0412\u043e\u0442 \u043a\u0430\u043a \u044d\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/708\/233\/19b\/70823319bb74672dbb79ff8195c19b97.gif\" alt=\"\" title=\"\" width=\"800\" height=\"286\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/708\/233\/19b\/70823319bb74672dbb79ff8195c19b97.gif\"\/><figcaption><\/figcaption><\/figure>\n<h2>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/h2>\n<p>\u042f \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043b \u043a \u043f\u043e\u0438\u0441\u043a\u0443 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u0418\u0437 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u0441\u0430\u043c\u044b\u043c \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u043c \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f react-native-audio-recorder-player. \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u0435\u0433\u043e \u043f\u043b\u044e\u0441\u043e\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u043d\u0446\u0438\u043f \u00ab2 \u0432 1\u00bb, \u0442\u043e \u0435\u0441\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c \u0438 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0433\u043e \u0430\u0443\u0434\u0438\u043e.<\/p>\n<p>\u0421\u0434\u0435\u043b\u0430\u043b \u0434\u0432\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430: AudioRecorder \u0438 AudioPlayer.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 AudioRecorder.tsx. \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u043d\u0430\u0448\u0435\u043b, \u043d\u043e \u043e\u043d\u0430 \u043d\u0435 \u0443\u043c\u0435\u0435\u0442 \u043f\u043e \u0441\u0432\u0430\u0439\u043f\u0443 \u043e\u0442\u043c\u0435\u043d\u044f\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c. \u042d\u0442\u043e \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0434\u043e\u043b\u0433\u0438\u0445 \u043f\u043e\u0438\u0441\u043a\u043e\u0432 \u0438 \u0440\u0430\u0437\u0434\u0443\u043c\u0438\u0439 \u0432 \u0433\u043e\u043b\u043e\u0432\u0443 \u043f\u0440\u0438\u0448\u043b\u0430 \u0438\u0434\u0435\u044f: \u0430 \u0447\u0442\u043e, \u0435\u0441\u043b\u0438 \u044d\u0442\u043e\u0442 \u0441\u0432\u0430\u0439\u043f \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u043e\u043c? \u041f\u043e\u0447\u0435\u043c\u0443 \u0431\u044b \u0438 \u043d\u0435\u0442: \u0443 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430 \u0435\u0441\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043b\u0430\u0432\u043d\u043e \u0430\u043d\u0438\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0438 \u043f\u0440\u043e\u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0442\u043c\u0435\u043d\u0443 \u043d\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0439 \u043f\u043e\u0437\u0438\u0446\u0438\u0438. \u041d\u043e \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u043a\u043d\u043e\u043f\u043a\u0443, \u043a\u0430\u043a\u0430\u044f \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u0430, \u0441\u043b\u0430\u0439\u0434\u0435\u0440 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 react-native-slider-custom. \u0421\u043a\u0440\u044b\u043b \u043f\u043e\u043b\u043e\u0441\u0443 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430, \u0441\u0434\u0435\u043b\u0430\u043b \u043a\u043d\u043e\u043f\u043a\u0443 \u00ab\u0437\u0430\u043f\u0438\u0441\u044c\/\u043e\u0442\u043c\u0435\u043d\u0430\u00bb \u0441 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u043d\u044b\u043c\u0438 \u0438\u043a\u043e\u043d\u043a\u0430\u043c\u0438, \u0441\u0434\u0435\u043b\u0430\u043b \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e\u0435 \u043f\u043e\u0437\u0438\u0446\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0432\u0435\u0440\u0445 \u0434\u0432\u0443\u0445 \u0431\u043b\u043e\u043a\u043e\u0432: \u0442\u0435\u043a\u0441\u0442\u0430 \u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0437\u0430\u043f\u0438\u0441\u0438. \u0412\u0441\u0435 \u0433\u043e\u0442\u043e\u0432\u043e, \u0438\u0434\u0435\u044f \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0430 ?<\/p>\n<h2>AudioRecorder.tsx<\/h2>\n<pre><code class=\"javascript\">import React, { useEffect, useState } from 'react'; import { PermissionsAndroid, Platform, StyleSheet, Text, ToastAndroid, View } from 'react-native'; import { observer } from 'mobx-react-lite'; import Slider from 'react-native-slider-custom'; import {   AudioEncoderAndroidType,   AudioSet,   AudioSourceAndroidType,   AVEncoderAudioQualityIOSType,   AVEncodingOption, } from 'react-native-audio-recorder-player';   import { Colors } from '..\/..\/..\/styles\/Colors'; import MicroPhoneIcon from '..\/..\/svg\/MicroPhoneIcon'; import { TextStyle } from '..\/..\/..\/styles\/TextStyle'; import RecordingIcon from '..\/..\/svg\/RecordingIcon'; import TrashIcon from '..\/..\/svg\/TrashIcon'; import { playerStyles } from '.\/playerStyles'; import { useStores } from '..\/..\/..\/hooks\/use-stores';   const CANCEL_RECORDING_SLIDER_VALUE = 0.8; const MAX_AUDIO_DURATION = 180000; \/\/ ms = 180000=3m   interface IAudioRecorderProps {}   const AudioRecorder: React.FC&lt;IAudioRecorderProps> = observer(({}) => {   const { audioRecPlayStore: store } = useStores();   const [androidGranted, setAndroidGranted] = useState(false);   const [recordSlidingValue, setRecordSlidingValue] = useState(0);   const [recordTime, setRecordTime] = useState('00:00:00');     \/\/ Effects   useEffect(() => {     (async () => {       if (Platform.OS === 'android') {         const hasPermissionWrite = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);         const hasPermissionRecord = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.RECORD_AUDIO);           if (hasPermissionRecord &amp;&amp; hasPermissionWrite) {           setAndroidGranted(true);         } else {           setAndroidGranted(false);         }       } else {         setAndroidGranted(true);       }     })();   }, []);     \/\/ Handlers   const handleSlidingStart = () => {     onStartRecord();   };     const handleSlidingChange = (value: number) => {     setRecordSlidingValue(value);   };     const handleSlidingComplete = (value: number) => {     onStopRecord(value);   };     \/\/ Actions   const onStartRecord = async () => {     if (!androidGranted) {       if (Platform.OS === 'android') {         const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.RECORD_AUDIO);           if (granted !== PermissionsAndroid.RESULTS.GRANTED) {           ToastAndroid.show('\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0430 \u0437\u0430\u043f\u0440\u0435\u0449\u0435\u043d\u0430', ToastAndroid.LONG);           return false;         }       }         if (Platform.OS === 'android') {         const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);           if (granted !== PermissionsAndroid.RESULTS.GRANTED) {           ToastAndroid.show('\u0417\u0430\u043f\u0438\u0441\u044c \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0448\u0435 \u0437\u0430\u043f\u0440\u0435\u0449\u0435\u043d\u043e', ToastAndroid.LONG);           return false;         }       }         setAndroidGranted(true);     } else {       const path = Platform.select({         ios: 'akkerman_voice_message.m4a',         android: 'sdcard\/akkerman_voice_message.mp4',       });         const audioSet: AudioSet = {         AudioEncoderAndroid: AudioEncoderAndroidType.AAC,         AudioSourceAndroid: AudioSourceAndroidType.MIC,         AVEncoderAudioQualityKeyIOS: AVEncoderAudioQualityIOSType.high,         AVNumberOfChannelsKeyIOS: 2,         AVFormatIDKeyIOS: AVEncodingOption.aac,       };         await store.audioRecorderPlayer.startRecorder(path, audioSet);         store.setIsRecording(true);       store.audioRecorderPlayer.addRecordBackListener((e: any) => {         setRecordTime(store.audioRecorderPlayer.mmssss(Math.floor(e.current_position)));           \/\/ Stop and save recording         if (e.current_position > MAX_AUDIO_DURATION) {           onStopRecord(0);         }           return;       });     }       return;   };     const onStopRecord = async (value: number) => {     const result = await store.audioRecorderPlayer.stopRecorder();       setRecordSlidingValue(0);     store.setIsRecording(false);     store.audioRecorderPlayer.removeRecordBackListener();       if (value > CANCEL_RECORDING_SLIDER_VALUE) {       \/\/ Cancel recording       store.setAudio(null);     } else {       \/\/ Save recording       if (!result.includes('stop')) {         store.setAudio({ duration: recordTime, uri: result });       }     }   };     \/\/ Renders   return (     &lt;View>       &lt;Slider         value={recordSlidingValue}         minimumTrackTintColor=\"transparent\"         maximumTrackTintColor=\"transparent\"         onSlidingStart={handleSlidingStart}         onSlidingComplete={handleSlidingComplete}         onValueChange={handleSlidingChange}         customThumb={           store.isRecording ? (             &lt;View style={styles.animatedIconsContainer}>               &lt;View style={[styles.animatedIconsBG, { opacity: recordSlidingValue }]} \/>               &lt;View style={[styles.animatedIconsWrap]}>                 &lt;MicroPhoneIcon color={Colors.white} style={[styles.animatedIcon, { opacity: 1 - recordSlidingValue }]} \/>                 &lt;TrashIcon style={[styles.animatedIcon, { opacity: recordSlidingValue > CANCEL_RECORDING_SLIDER_VALUE ? 1 : recordSlidingValue }]} \/>               &lt;\/View>             &lt;\/View>           ) : (             &lt;MicroPhoneIcon \/>           )         }         thumbStyle={[           playerStyles.button,           store.isRecording &amp;&amp; playerStyles.buttonShadow,           { backgroundColor: recordSlidingValue > CANCEL_RECORDING_SLIDER_VALUE ? Colors.error : Colors.primary },         ]}         style={styles.swipeableButton}         animateTransitions       \/>         &lt;View style={[styles.recorder, { opacity: 1 - recordSlidingValue }]}>         &lt;View style={{ flexDirection: 'row', alignItems: 'center' }}>           &lt;Text style={{ ...TextStyle.caption, color: Colors.whiteMedium, marginLeft: 40 + 8 }}>             {store.isRecording ? '\u0412\u043f\u0440\u0430\u0432\u043e \u0434\u043b\u044f \u043e\u0442\u043c\u0435\u043d\u044b' : '\u0417\u0430\u0436\u043c\u0438\u0442\u0435 \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438'}           &lt;\/Text>         &lt;\/View>           {store.isRecording &amp;&amp; (           &lt;View style={{ flexDirection: 'row', alignItems: 'center' }}>             &lt;Text style={{ ...TextStyle.body1, marginRight: 10 }}>{recordTime}&lt;\/Text>             &lt;RecordingIcon \/>           &lt;\/View>         )}       &lt;\/View>     &lt;\/View>   ); });   export default AudioRecorder;   const styles = StyleSheet.create({   recorder: {     height: 40,     flexDirection: 'row',     alignItems: 'center',     justifyContent: 'space-between',   },   swipeableButton: {     position: 'absolute',     top: 0,     left: 0,     width: '100%',     height: 40,     zIndex: 1,   },   animatedIconsContainer: {     width: '100%',     height: '100%',     alignItems: 'center',     justifyContent: 'center',   },   animatedIconsBG: {     position: 'absolute',     top: 0,     left: 0,     bottom: 0,     right: 0,     backgroundColor: Colors.error,   },   animatedIconsWrap: {     position: 'relative',     width: 24,     height: 24,   },   animatedIcon: {     position: 'absolute',     top: 0,     left: 0,     bottom: 0,     right: 0,   }, });<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0442\u043c\u0435\u043d\u044b \u0437\u0430\u043f\u0438\u0441\u0438 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u043c onSlidingComplete \u0443 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0438\u0438 \u043f\u043e\u043b\u0437\u0443\u043d\u043a\u0430. \u041f\u043e\u0437\u0438\u0446\u0438\u044e \u043e\u0442\u043c\u0435\u043d\u044b \u0440\u0435\u0448\u0438\u043b \u0441\u0434\u0435\u043b\u0430\u0442\u044c 80%. \u0412\u044b\u043d\u0435\u0441 \u0432 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u0443 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 CANCEL_RECORDING_SLIDER_VALUE, \u0440\u0430\u0432\u043d\u044b\u0439 0.8, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0441\u043b\u0430\u0439\u0434\u0435\u0440 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043e\u0442 0 \u0434\u043e 1.<\/p>\n<p>\u041f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0438\u0438 \u043a\u043d\u043e\u043f\u043a\u0438 \u0437\u0430\u043f\u0438\u0441\u0438 (\u043f\u043e\u043b\u0437\u0443\u043d\u043a\u0430 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430) \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a handleSlidingComplete. \u041e\u043d, \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044e onStopRecord, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u044f \u0442\u0443\u0434\u0430 \u043f\u043e\u0437\u0438\u0446\u0438\u044e \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430 \u043d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0438\u044f. \u0412 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 onStopRecord \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430 \u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443: \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c \u0438\u043b\u0438 \u043e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c. \u0427\u0442\u043e\u0431\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c, \u043a\u0430\u043a\u043e\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c, \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0443\u0441\u043b\u043e\u0432\u0438\u0435 value > CANCEL_RECORDING_SLIDER_VALUE. \u0415\u0441\u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430 \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0438\u0438 \u043a\u043d\u043e\u043f\u043a\u0438 \u0437\u0430\u043f\u0438\u0441\u0438 \u0431\u043e\u043b\u044c\u0448\u0435 0.8 \u2014 \u043e\u0442\u043c\u0435\u043d\u044f\u0435\u043c \u0437\u0430\u043f\u0438\u0441\u044c, \u0435\u0441\u043b\u0438 \u043c\u0435\u043d\u044c\u0448\u0435 \u2014 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c.<\/p>\n<p>\u0412\u043e\u0442 \u0438 \u0432\u0441\u0435!<\/p>\n<h2>AudioPlayer.tsx<\/h2>\n<pre><code class=\"javascript\">import React, { useCallback, useEffect, useState } from 'react'; import { ActivityIndicator, Platform, Pressable, StyleSheet, Text, View } from 'react-native'; import { observer } from 'mobx-react-lite'; import Slider from 'react-native-slider-custom'; import { useFocusEffect } from '@react-navigation\/native';   import { useStores } from '..\/..\/..\/hooks\/use-stores'; import { Colors } from '..\/..\/..\/styles\/Colors'; import { TextStyle } from '..\/..\/..\/styles\/TextStyle'; import PlayIcon from '..\/..\/svg\/PlayIcon'; import PauseIcon from '..\/..\/svg\/PauseIcon'; import { playerStyles } from '.\/playerStyles';   interface IAudioPlayerProps {   audio?: string; }   const AudioPlayer: React.FC&lt;IAudioPlayerProps> = observer(({ audio }) => {   const { audioRecPlayStore: store } = useStores();   const [currentPositionSec, setCurrentPositionSec] = useState(0);   const [currentDurationSec, setCurrentDurationSec] = useState(0);   const [playTime, setPlayTime] = useState('00:00:00');   const [isPlaying, setIsPlaying] = useState(false);   const [loading, setLoading] = useState(false);     useFocusEffect(     React.useCallback(() => {       return () => {         onStopPlay();       };     }, []),   );     useEffect(() => {     return () => {       onStopPlay();     };   }, []);     \/\/ Actions   const onPlayPause = () => {     if (!loading) {       if (isPlaying) {         onPausePlay();       } else {         onStartPlay();       }     }   };     const onStartPlay = useCallback(async () => {     const path = Platform.select({       ios: audio ? audio : 'akkerman_voice_message.m4a',       android: audio ? audio : 'sdcard\/akkerman_voice_message.mp4',     });       setLoading(true);     await store.audioRecorderPlayer.startPlayer(path);       setLoading(false);     setIsPlaying(true);       store.audioRecorderPlayer.addPlayBackListener((e: any) => {       if (e.current_position === e.duration) {         setIsPlaying(false);         onStopPlay();       }         setCurrentPositionSec(e.current_position);       setCurrentDurationSec(e.duration);       setPlayTime(store.audioRecorderPlayer.mmssss(Math.floor(e.current_position)));         return;     });   }, [store.audioRecorderPlayer]);     const onPausePlay = async () => {     await store.audioRecorderPlayer.pausePlayer();     setIsPlaying(false);   };     const onStopPlay = async () => {     store.audioRecorderPlayer.stopPlayer();     store.audioRecorderPlayer.removePlayBackListener();   };     \/\/ Renders   return (     &lt;View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>       &lt;Pressable style={[playerStyles.button, { marginRight: 12 }]} onPress={onPlayPause}>         {loading ? &lt;ActivityIndicator size={24} color={Colors.black} \/> : isPlaying ? &lt;PauseIcon \/> : &lt;PlayIcon \/>}       &lt;\/Pressable>       &lt;View style={{ flex: 1 }}>         &lt;Slider           maximumValue={Math.floor(currentDurationSec)}           value={Math.floor(currentPositionSec)}           minimumTrackTintColor={Colors.primary}           maximumTrackTintColor={Colors.whiteMedium}           thumbStyle={{ width: 12, height: 12, backgroundColor: Colors.primary }}           style={{ height: 12 + 8 }}           disabled         \/>         &lt;Text style={{ ...TextStyle.caption, color: Colors.white }}>{playTime}&lt;\/Text>       &lt;\/View>     &lt;\/View>   ); });   export default AudioPlayer;<\/code><\/pre>\n<p>\u042d\u0442\u043e \u043e\u0431\u044b\u0447\u043d\u044b\u0439 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u044b\u0439 \u0430\u0443\u0434\u0438\u043e\u043f\u043b\u0435\u0435\u0440 \u0441 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u043e\u0442\u043b\u0438\u0447\u0438\u044f\u043c\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438.\u00a0<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0440\u0435\u043a\u043e\u0440\u0434\u0435\u0440 \u0438 \u043f\u043b\u0435\u0435\u0440 \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e, \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0434\u0438\u043d \u0438 \u0442\u043e\u0442 \u0436\u0435 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043f\u043b\u0435\u0435\u0440\u0430. \u0422\u0430\u043a\u0436\u0435 \u0434\u043b\u044f \u0445\u043e\u0440\u043e\u0448\u0435\u0433\u043e UX \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u043e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0441\u043a\u0440\u043e\u043b\u043b \u044d\u043a\u0440\u0430\u043d\u0430 &#171;\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u043e \u0440\u0438\u0441\u043a\u0435&#187; \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u043f\u0438\u0441\u0438 \u0433\u043e\u043b\u043e\u0441\u0430, \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u043b \u0442\u043e\u043b\u044c\u043a\u043e \u0441\u043b\u0430\u0439\u0434\u0435\u0440. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0434\u0435\u043b\u0430\u043b MobX \u0441\u0442\u043e\u0440 AudioRecPlayStore.ts.<\/p>\n<h2>AudioRecPlayStore.ts <\/h2>\n<pre><code class=\"javascript\">import { makeAutoObservable } from 'mobx'; import AudioRecorderPlayer from 'react-native-audio-recorder-player';   import { Nullable } from '..\/..\/..\/types\/CommonTypes'; import { IAudioObject } from '.\/AudioRecPlay';   export class AudioRecPlayStore {   isRecording = false;     audioRecorderPlayer: AudioRecorderPlayer;   audio: Nullable&lt;IAudioObject> = null;     constructor() {     makeAutoObservable(this);     this.audioRecorderPlayer = new AudioRecorderPlayer();   }     setIsRecording = (state: boolean) => {     this.isRecording = state;   };     setAudio = (audio: Nullable&lt;IAudioObject>) => {     this.audio = audio;   };     clear = () => {     this.isRecording = false;     this.audio = null;   }; }<\/code><\/pre>\n<p><strong>\u0412 \u0441\u0442\u043e\u0440\u0435 \u0435\u0441\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430:<\/strong><\/p>\n<ul>\n<li>\n<p>\u042d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043f\u043b\u0435\u0435\u0440\u0430 audioRecorderPlayer \u0434\u043b\u044f \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u0438\u0437 \u0440\u0435\u043a\u043e\u0440\u0434\u0435\u0440\u0430 \u0432 \u043f\u043b\u0435\u0435\u0440 \u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u043e;<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0438 isRecording \u0434\u043b\u044f \u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0437\u0430\u043f\u0438\u0441\u0438.<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"javascript\">&lt;ScrollView scrollEnabled={!audioRecPlayStore.isRecording}><\/code><\/pre>\n<p>\u0417\u0430 \u0440\u0435\u043d\u0434\u0435\u0440 \u0440\u0435\u043a\u043e\u0440\u0434\u0435\u0440\u0430 \u0438\u043b\u0438 \u043f\u043b\u0435\u0435\u0440\u0430 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442 MessageAudioMessage.tsx.<\/p>\n<h2>MessageAudioMessage.tsx<\/h2>\n<pre><code class=\"javascript\">import React, { useEffect } from 'react'; import { Text, TouchableOpacity, View } from 'react-native'; import { observer } from 'mobx-react-lite';   import { useStores } from '..\/..\/..\/hooks\/use-stores'; import InputLabel from '..\/..\/InputLabel'; import { messageStyles } from '..\/messageStyles'; import { Colors } from '..\/..\/..\/styles\/Colors'; import { TextStyle } from '..\/..\/..\/styles\/TextStyle'; import AudioRecorder from '.\/AudioRecorder'; import AudioPlayer from '.\/AudioPlayer'; import { FIleData } from '..\/..\/..\/modules\/message\/Message'; import { Nullable } from '..\/..\/..\/types\/CommonTypes';   interface IMessageAudioMessageProps {   onlyPlayer?: boolean;   audio?: Nullable&lt;FIleData>; }   const MessageAudioMessage: React.FC&lt;IMessageAudioMessageProps> = observer(({ onlyPlayer, audio }) => {   const { audioRecPlayStore: store } = useStores();     useEffect(() => {     return () => {       store.clear();     };   }, []);     const handleRemoveAudio = () => {     store.setAudio(null);     store.audioRecorderPlayer.stopPlayer();     store.audioRecorderPlayer.removePlayBackListener();   };     return (     &lt;View style={{ ...messageStyles.card, flexDirection: 'row', alignItems: 'center' }}>       &lt;View style={{ flex: 1 }}>         &lt;View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>           &lt;InputLabel label=\"\u0410\u0443\u0434\u0438\u043e\u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\" \/>           {!onlyPlayer &amp;&amp; !!store.audio &amp;&amp; (             &lt;TouchableOpacity onPress={handleRemoveAudio} activeOpacity={0.8}>               &lt;Text style={{ ...TextStyle.subtitle, color: Colors.error, letterSpacing: 0 }}>\u0423\u0434\u0430\u043b\u0438\u0442\u044c&lt;\/Text>             &lt;\/TouchableOpacity>           )}         &lt;\/View>         {onlyPlayer ? &lt;AudioPlayer audio={audio?.fullUrl || ''} \/> : !!store.audio ? &lt;AudioPlayer \/> : &lt;AudioRecorder \/>}       &lt;\/View>     &lt;\/View>   ); });   export default MessageAudioMessage;<\/code><\/pre>\n<p>\u0411\u044b\u043b\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0441 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435\u043c \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0433\u043e \u0433\u043e\u043b\u043e\u0441\u0430 \u043d\u0430 \u0440\u0430\u0437\u043d\u044b\u0445 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430\u0445 \u0438\u0437 \u0440\u0430\u0437\u043d\u044b\u0445 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c: \u043e\u043d\u0438 \u0431\u044b\u043b\u0438 \u043d\u0435\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b. \u0420\u0435\u0448\u0435\u043d\u0438\u0435 \u0435\u0441\u0442\u044c \u0432 \u0441\u0430\u043c\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0440\u0435\u043a\u043e\u0440\u0434\u0435\u0440\u0430. <\/p>\n<pre><code class=\"javascript\">\u0441onst audioSet: AudioSet = {   AudioEncoderAndroid: AudioEncoderAndroidType.AAC,   AudioSourceAndroid: AudioSourceAndroidType.MIC,   AVEncoderAudioQualityKeyIOS: AVEncoderAudioQualityIOSType.high,   AVNumberOfChannelsKeyIOS: 2,   AVFormatIDKeyIOS: AVEncodingOption.aac, };   await store.audioRecorderPlayer.startRecorder(path, audioSet); <\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 audioSet \u0441 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u043c\u0438 \u0437\u0430\u043f\u0438\u0441\u0438 \u0430\u0443\u0434\u0438\u043e\u0444\u0430\u0439\u043b\u0430 \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u043a\u0430\u043a \u0432\u0442\u043e\u0440\u043e\u0439 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u043c\u0435\u0442\u043e\u0434\u0443 startRecorder. \u042d\u0442\u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043a\u0440\u043e\u0441\u0441\u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435\u043d\u043d\u044b\u0435, \u0442\u043e \u0435\u0441\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0433\u043e\u043b\u043e\u0441 \u043d\u0430 Android\/iOS \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430 \u0432\u0435\u0431\u0435 \u0432 \u043d\u0430\u0442\u0438\u0432\u043d\u043e\u043c html5 \u043f\u043b\u0435\u0435\u0440\u0435 \u0438 \u043d\u0430 Android\/iOS.<\/p>\n<p><strong>\u041f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b:<\/strong><\/p>\n<p><a href=\"https:\/\/www.npmjs.com\/package\/react-native-audio-recorder-player\" rel=\"noopener noreferrer nofollow\"><u>https:\/\/www.npmjs.com\/package\/react-native-audio-recorder-player<\/u><\/a>\u00a0<\/p>\n<p><a href=\"https:\/\/www.npmjs.com\/package\/react-native-slider-custom\" rel=\"noopener noreferrer nofollow\"><u>https:\/\/www.npmjs.com\/package\/react-native-slider-custom<\/u><\/a><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/585726\/\"> https:\/\/habr.com\/ru\/articles\/585726\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u0425\u0430\u0442\u0430\u043c. \u042f \u0440\u0430\u0431\u043e\u0442\u0430\u044e \u0432 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 Neti. \u041a\u043e\u0433\u0434\u0430-\u0442\u043e \u044f \u0431\u044b\u043b \u0432\u0435\u0440\u0441\u0442\u0430\u043b\u044c\u0449\u0438\u043a\u043e\u043c \u0441\u0430\u0439\u0442\u043e\u0432, \u043d\u043e \u043c\u043d\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0440\u0430\u0437\u0432\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u0434\u0430\u043b\u044c\u0448\u0435. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u0438\u0437\u0443\u0447\u0438\u043b React \u0438 \u043d\u0430\u0443\u0447\u0438\u043b\u0441\u044f \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0435\u0431-\u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f, \u0430 \u0437\u0430\u0442\u0435\u043c \u0440\u0435\u0448\u0438\u043b \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u0441\u0438\u043b\u044b \u0432 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0439 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435. \u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0434\u0435\u043b\u044e\u0441\u044c \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u043c\u0438 \u0440\u0435\u0448\u0435\u043d\u0438\u0439, \u043a \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043f\u0440\u0438\u0448\u0435\u043b, \u0440\u0430\u0431\u043e\u0442\u0430\u044f \u043d\u0430\u0434 \u0437\u0430\u0434\u0430\u0447\u0435\u0439 \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432. \u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0447\u0442\u043e \u043c\u043e\u0439 \u043e\u043f\u044b\u0442 \u0431\u0443\u0434\u0435\u0442 \u043a\u043e\u043c\u0443-\u0442\u043e \u043f\u043e\u043b\u0435\u0437\u0435\u043d.<\/p>\n<h2>\u041f\u0440\u043e\u0435\u043a\u0442<\/h2>\n<p>\u041c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435, \u043f\u0440\u0435\u0434\u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u043d\u043e\u0435 \u0434\u043b\u044f \u0431\u044b\u0441\u0442\u0440\u043e\u0439 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430\u0445 \u0438 \u043e\u043f\u0430\u0441\u043d\u044b\u0445 \u0444\u0430\u043a\u0442\u043e\u0440\u0430\u0445 \u0434\u043b\u044f \u0436\u0438\u0437\u043d\u0438 \u0438 \u0437\u0434\u043e\u0440\u043e\u0432\u044c\u044f \u043b\u044e\u0434\u0435\u0439 \u043d\u0430 \u0442\u0435\u0440\u0440\u0438\u0442\u043e\u0440\u0438\u0438 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u043f\u0440\u0435\u0434\u043f\u0440\u0438\u044f\u0442\u0438\u044f.<\/p>\n<p>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438: \u0441\u043e\u0442\u0440\u0443\u0434\u043d\u0438\u043a\u0438 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438, \u043f\u043e\u0434\u0440\u044f\u0434\u0447\u0438\u043a\u0438 \u0438 \u0433\u043e\u0441\u0442\u0438 \u043f\u0440\u0435\u0434\u043f\u0440\u0438\u044f\u0442\u0438\u044f.<\/p>\n<p>\u041f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b: Android, IOS.<\/p>\n<p>\u0421\u0442\u0435\u043a \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0439: React Native, MobX.<\/p>\n<h2>\u0417\u0430\u0434\u0430\u0447\u0430<\/h2>\n<p>\u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0431\u044b\u043b\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c:\u00a0<\/p>\n<ul>\n<li>\n<p>\u0417\u0430\u043f\u0438\u0441\u044c \u0433\u043e\u043b\u043e\u0441\u0430;<\/p>\n<\/li>\n<li>\n<p>\u041e\u0442\u043c\u0435\u043d\u0430 \u0437\u0430\u043f\u0438\u0441\u0438 \u0441\u0432\u0430\u0439\u043f\u043e\u043c \u0432\u043f\u0440\u0430\u0432\u043e \u0441 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0435\u0439 \u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c \u0438\u043a\u043e\u043d\u043a\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0412\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0433\u043e \u0437\u0432\u0443\u043a\u0430.\u00a0<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0430\u043a\u0436\u0435 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u044b\u0439 \u0433\u043e\u043b\u043e\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u043b \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u0430\u0434\u043c\u0438\u043d\u043a\u0435 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435.<\/p>\n<p>\u0412\u043e\u0442 \u043a\u0430\u043a \u044d\u0442\u043e \u0434\u043e\u043b\u0436\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c:<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<h2>\u0420\u0435\u0448\u0435\u043d\u0438\u0435<\/h2>\n<p>\u042f \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043b \u043a \u043f\u043e\u0438\u0441\u043a\u0443 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u0418\u0437 \u043d\u0430\u0439\u0434\u0435\u043d\u043d\u044b\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u0441\u0430\u043c\u044b\u043c \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b\u043c \u043e\u043a\u0430\u0437\u0430\u043b\u0441\u044f react-native-audio-recorder-player. \u041e\u0441\u043d\u043e\u0432\u043d\u044b\u043c \u0435\u0433\u043e \u043f\u043b\u044e\u0441\u043e\u043c \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u0438\u043d\u0446\u0438\u043f \u00ab2 \u0432 1\u00bb, \u0442\u043e \u0435\u0441\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c \u0438 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u0437\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0433\u043e \u0430\u0443\u0434\u0438\u043e.<\/p>\n<p>\u0421\u0434\u0435\u043b\u0430\u043b \u0434\u0432\u0430 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430: AudioRecorder \u0438 AudioPlayer.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0435\u043c \u0441 AudioRecorder.tsx. \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u043d\u0430\u0448\u0435\u043b, \u043d\u043e \u043e\u043d\u0430 \u043d\u0435 \u0443\u043c\u0435\u0435\u0442 \u043f\u043e \u0441\u0432\u0430\u0439\u043f\u0443 \u043e\u0442\u043c\u0435\u043d\u044f\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c. \u042d\u0442\u043e \u043d\u0430\u0434\u043e \u0431\u044b\u043b\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e.<\/p>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0434\u043e\u043b\u0433\u0438\u0445 \u043f\u043e\u0438\u0441\u043a\u043e\u0432 \u0438 \u0440\u0430\u0437\u0434\u0443\u043c\u0438\u0439 \u0432 \u0433\u043e\u043b\u043e\u0432\u0443 \u043f\u0440\u0438\u0448\u043b\u0430 \u0438\u0434\u0435\u044f: \u0430 \u0447\u0442\u043e, \u0435\u0441\u043b\u0438 \u044d\u0442\u043e\u0442 \u0441\u0432\u0430\u0439\u043f \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u043e\u043c? \u041f\u043e\u0447\u0435\u043c\u0443 \u0431\u044b \u0438 \u043d\u0435\u0442: \u0443 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430 \u0435\u0441\u0442\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043b\u0430\u0432\u043d\u043e \u0430\u043d\u0438\u043c\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0443\u0436\u043d\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0438 \u043f\u0440\u043e\u043f\u0438\u0441\u0430\u0442\u044c \u043e\u0442\u043c\u0435\u043d\u0443 \u043d\u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u0439 \u043f\u043e\u0437\u0438\u0446\u0438\u0438. \u041d\u043e \u0447\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u043a\u043d\u043e\u043f\u043a\u0443, \u043a\u0430\u043a\u0430\u044f \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u0430, \u0441\u043b\u0430\u0439\u0434\u0435\u0440 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 react-native-slider-custom. \u0421\u043a\u0440\u044b\u043b \u043f\u043e\u043b\u043e\u0441\u0443 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430, \u0441\u0434\u0435\u043b\u0430\u043b \u043a\u043d\u043e\u043f\u043a\u0443 \u00ab\u0437\u0430\u043f\u0438\u0441\u044c\/\u043e\u0442\u043c\u0435\u043d\u0430\u00bb \u0441 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u043d\u044b\u043c\u0438 \u0438\u043a\u043e\u043d\u043a\u0430\u043c\u0438, \u0441\u0434\u0435\u043b\u0430\u043b \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e\u0435 \u043f\u043e\u0437\u0438\u0446\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0432\u0435\u0440\u0445 \u0434\u0432\u0443\u0445 \u0431\u043b\u043e\u043a\u043e\u0432: \u0442\u0435\u043a\u0441\u0442\u0430 \u0438 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0437\u0430\u043f\u0438\u0441\u0438. \u0412\u0441\u0435 \u0433\u043e\u0442\u043e\u0432\u043e, \u0438\u0434\u0435\u044f \u0441\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0430 ?<\/p>\n<h2>AudioRecorder.tsx<\/h2>\n<pre><code class=\"javascript\">import React, { useEffect, useState } from 'react'; import { PermissionsAndroid, Platform, StyleSheet, Text, ToastAndroid, View } from 'react-native'; import { observer } from 'mobx-react-lite'; import Slider from 'react-native-slider-custom'; import {   AudioEncoderAndroidType,   AudioSet,   AudioSourceAndroidType,   AVEncoderAudioQualityIOSType,   AVEncodingOption, } from 'react-native-audio-recorder-player';   import { Colors } from '..\/..\/..\/styles\/Colors'; import MicroPhoneIcon from '..\/..\/svg\/MicroPhoneIcon'; import { TextStyle } from '..\/..\/..\/styles\/TextStyle'; import RecordingIcon from '..\/..\/svg\/RecordingIcon'; import TrashIcon from '..\/..\/svg\/TrashIcon'; import { playerStyles } from '.\/playerStyles'; import { useStores } from '..\/..\/..\/hooks\/use-stores';   const CANCEL_RECORDING_SLIDER_VALUE = 0.8; const MAX_AUDIO_DURATION = 180000; \/\/ ms = 180000=3m   interface IAudioRecorderProps {}   const AudioRecorder: React.FC&lt;IAudioRecorderProps> = observer(({}) => {   const { audioRecPlayStore: store } = useStores();   const [androidGranted, setAndroidGranted] = useState(false);   const [recordSlidingValue, setRecordSlidingValue] = useState(0);   const [recordTime, setRecordTime] = useState('00:00:00');     \/\/ Effects   useEffect(() => {     (async () => {       if (Platform.OS === 'android') {         const hasPermissionWrite = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);         const hasPermissionRecord = await PermissionsAndroid.check(PermissionsAndroid.PERMISSIONS.RECORD_AUDIO);           if (hasPermissionRecord &amp;&amp; hasPermissionWrite) {           setAndroidGranted(true);         } else {           setAndroidGranted(false);         }       } else {         setAndroidGranted(true);       }     })();   }, []);     \/\/ Handlers   const handleSlidingStart = () => {     onStartRecord();   };     const handleSlidingChange = (value: number) => {     setRecordSlidingValue(value);   };     const handleSlidingComplete = (value: number) => {     onStopRecord(value);   };     \/\/ Actions   const onStartRecord = async () => {     if (!androidGranted) {       if (Platform.OS === 'android') {         const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.RECORD_AUDIO);           if (granted !== PermissionsAndroid.RESULTS.GRANTED) {           ToastAndroid.show('\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043c\u0438\u043a\u0440\u043e\u0444\u043e\u043d\u0430 \u0437\u0430\u043f\u0440\u0435\u0449\u0435\u043d\u0430', ToastAndroid.LONG);           return false;         }       }         if (Platform.OS === 'android') {         const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE);           if (granted !== PermissionsAndroid.RESULTS.GRANTED) {           ToastAndroid.show('\u0417\u0430\u043f\u0438\u0441\u044c \u0432 \u0445\u0440\u0430\u043d\u0438\u043b\u0438\u0448\u0435 \u0437\u0430\u043f\u0440\u0435\u0449\u0435\u043d\u043e', ToastAndroid.LONG);           return false;         }       }         setAndroidGranted(true);     } else {       const path = Platform.select({         ios: 'akkerman_voice_message.m4a',         android: 'sdcard\/akkerman_voice_message.mp4',       });         const audioSet: AudioSet = {         AudioEncoderAndroid: AudioEncoderAndroidType.AAC,         AudioSourceAndroid: AudioSourceAndroidType.MIC,         AVEncoderAudioQualityKeyIOS: AVEncoderAudioQualityIOSType.high,         AVNumberOfChannelsKeyIOS: 2,         AVFormatIDKeyIOS: AVEncodingOption.aac,       };         await store.audioRecorderPlayer.startRecorder(path, audioSet);         store.setIsRecording(true);       store.audioRecorderPlayer.addRecordBackListener((e: any) => {         setRecordTime(store.audioRecorderPlayer.mmssss(Math.floor(e.current_position)));           \/\/ Stop and save recording         if (e.current_position > MAX_AUDIO_DURATION) {           onStopRecord(0);         }           return;       });     }       return;   };     const onStopRecord = async (value: number) => {     const result = await store.audioRecorderPlayer.stopRecorder();       setRecordSlidingValue(0);     store.setIsRecording(false);     store.audioRecorderPlayer.removeRecordBackListener();       if (value > CANCEL_RECORDING_SLIDER_VALUE) {       \/\/ Cancel recording       store.setAudio(null);     } else {       \/\/ Save recording       if (!result.includes('stop')) {         store.setAudio({ duration: recordTime, uri: result });       }     }   };     \/\/ Renders   return (     &lt;View>       &lt;Slider         value={recordSlidingValue}         minimumTrackTintColor=\"transparent\"         maximumTrackTintColor=\"transparent\"         onSlidingStart={handleSlidingStart}         onSlidingComplete={handleSlidingComplete}         onValueChange={handleSlidingChange}         customThumb={           store.isRecording ? (             &lt;View style={styles.animatedIconsContainer}>               &lt;View style={[styles.animatedIconsBG, { opacity: recordSlidingValue }]} \/>               &lt;View style={[styles.animatedIconsWrap]}>                 &lt;MicroPhoneIcon color={Colors.white} style={[styles.animatedIcon, { opacity: 1 - recordSlidingValue }]} \/>                 &lt;TrashIcon style={[styles.animatedIcon, { opacity: recordSlidingValue > CANCEL_RECORDING_SLIDER_VALUE ? 1 : recordSlidingValue }]} \/>               &lt;\/View>             &lt;\/View>           ) : (             &lt;MicroPhoneIcon \/>           )         }         thumbStyle={[           playerStyles.button,           store.isRecording &amp;&amp; playerStyles.buttonShadow,           { backgroundColor: recordSlidingValue > CANCEL_RECORDING_SLIDER_VALUE ? Colors.error : Colors.primary },         ]}         style={styles.swipeableButton}         animateTransitions       \/>         &lt;View style={[styles.recorder, { opacity: 1 - recordSlidingValue }]}>         &lt;View style={{ flexDirection: 'row', alignItems: 'center' }}>           &lt;Text style={{ ...TextStyle.caption, color: Colors.whiteMedium, marginLeft: 40 + 8 }}>             {store.isRecording ? '\u0412\u043f\u0440\u0430\u0432\u043e \u0434\u043b\u044f \u043e\u0442\u043c\u0435\u043d\u044b' : '\u0417\u0430\u0436\u043c\u0438\u0442\u0435 \u0434\u043b\u044f \u0437\u0430\u043f\u0438\u0441\u0438'}           &lt;\/Text>         &lt;\/View>           {store.isRecording &amp;&amp; (           &lt;View style={{ flexDirection: 'row', alignItems: 'center' }}>             &lt;Text style={{ ...TextStyle.body1, marginRight: 10 }}>{recordTime}&lt;\/Text>             &lt;RecordingIcon \/>           &lt;\/View>         )}       &lt;\/View>     &lt;\/View>   ); });   export default AudioRecorder;   const styles = StyleSheet.create({   recorder: {     height: 40,     flexDirection: 'row',     alignItems: 'center',     justifyContent: 'space-between',   },   swipeableButton: {     position: 'absolute',     top: 0,     left: 0,     width: '100%',     height: 40,     zIndex: 1,   },   animatedIconsContainer: {     width: '100%',     height: '100%',     alignItems: 'center',     justifyContent: 'center',   },   animatedIconsBG: {     position: 'absolute',     top: 0,     left: 0,     bottom: 0,     right: 0,     backgroundColor: Colors.error,   },   animatedIconsWrap: {     position: 'relative',     width: 24,     height: 24,   },   animatedIcon: {     position: 'absolute',     top: 0,     left: 0,     bottom: 0,     right: 0,   }, });<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0442\u043c\u0435\u043d\u044b \u0437\u0430\u043f\u0438\u0441\u0438 \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0441\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u043c onSlidingComplete \u0443 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0438\u0438 \u043f\u043e\u043b\u0437\u0443\u043d\u043a\u0430. \u041f\u043e\u0437\u0438\u0446\u0438\u044e \u043e\u0442\u043c\u0435\u043d\u044b \u0440\u0435\u0448\u0438\u043b \u0441\u0434\u0435\u043b\u0430\u0442\u044c 80%. \u0412\u044b\u043d\u0435\u0441 \u0432 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u0443 \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u0441\u0442\u0432\u0430 CANCEL_RECORDING_SLIDER_VALUE, \u0440\u0430\u0432\u043d\u044b\u0439 0.8, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0441\u043b\u0430\u0439\u0434\u0435\u0440 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043e\u0442 0 \u0434\u043e 1.<\/p>\n<p>\u041f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0438\u0438 \u043a\u043d\u043e\u043f\u043a\u0438 \u0437\u0430\u043f\u0438\u0441\u0438 (\u043f\u043e\u043b\u0437\u0443\u043d\u043a\u0430 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430) \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a handleSlidingComplete. \u041e\u043d, \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044e onStopRecord, \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u044f \u0442\u0443\u0434\u0430 \u043f\u043e\u0437\u0438\u0446\u0438\u044e \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430 \u043d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0438\u044f. \u0412 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 onStopRecord \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430 \u0434\u0435\u043b\u0430\u0435\u043c \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0443: \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c \u0438\u043b\u0438 \u043e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u0437\u0430\u043f\u0438\u0441\u044c. \u0427\u0442\u043e\u0431\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c, \u043a\u0430\u043a\u043e\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c, \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0443\u0441\u043b\u043e\u0432\u0438\u0435 value > CANCEL_RECORDING_SLIDER_VALUE. \u0415\u0441\u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u043b\u0430\u0439\u0434\u0435\u0440\u0430 \u043f\u0440\u0438 \u043e\u0442\u043f\u0443\u0441\u043a\u0430\u043d\u0438\u0438 \u043a\u043d\u043e\u043f\u043a\u0438 \u0437\u0430\u043f\u0438\u0441\u0438 \u0431\u043e\u043b\u044c\u0448\u0435 0.8 \u2014 \u043e\u0442\u043c\u0435\u043d\u044f\u0435\u043c \u0437\u0430\u043f\u0438\u0441\u044c, \u0435\u0441\u043b\u0438 \u043c\u0435\u043d\u044c\u0448\u0435 \u2014 \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c.<\/p>\n<p>\u0412\u043e\u0442 \u0438 \u0432\u0441\u0435!<\/p>\n<h2>AudioPlayer.tsx<\/h2>\n<pre><code class=\"javascript\">import React, { useCallback, useEffect, useState } from 'react'; import { ActivityIndicator, Platform, Pressable, StyleSheet, Text, View } from 'react-native'; import { observer } from 'mobx-react-lite'; import Slider from 'react-native-slider-custom';<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-382314","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/382314","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=382314"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/382314\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=382314"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=382314"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=382314"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}