Skip to content

Commit c34b0f7

Browse files
feat(UNT-T27088): external url support
1 parent cb3cb67 commit c34b0f7

File tree

9 files changed

+274
-61
lines changed

9 files changed

+274
-61
lines changed

README.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,34 @@ const ref = useRef<IWaveformRef>(null);
9090
<Waveform
9191
mode="static"
9292
ref={ref}
93-
path={item}
93+
path={path}
94+
candleSpace={2}
95+
candleWidth={4}
96+
scrubColor="white"
97+
onPlayerStateChange={playerState => console.log(playerState)}
98+
onPanStateChange={isMoving => console.log(isMoving)}
99+
/>;
100+
```
101+
102+
When you want to show a waveform for a external audio URL, you need to use `static` mode for the waveform and set isExternalUrl to true.
103+
104+
Check the example below for more information.
105+
106+
```tsx
107+
import {
108+
Waveform,
109+
type IWaveformRef,
110+
} from '@simform_solutions/react-native-audio-waveform';
111+
112+
const url = 'https://dl.espressif.com/dl/audio/gs-16b-2c-44100hz.mp3'; // URL to the audio file for which you want to show waveform
113+
const ref = useRef<IWaveformRef>(null);
114+
<Waveform
115+
mode="static"
116+
ref={ref}
117+
path={url}
118+
isExternalUrl={true}
119+
onDownloadStateChange={state => console.log(state)}
120+
onDownloadProgressChange={progress => console.log(progress)}
94121
candleSpace={2}
95122
candleWidth={4}
96123
scrubColor="white"
@@ -133,6 +160,9 @@ You can check out the full example at [Example](./example/src/App.tsx).
133160
| ref\* | - ||| IWaveformRef | Type of ref provided to waveform component. If waveform mode is `static`, some methods from ref will throw error and same for `live`.<br> Check [IWaveformRef](#iwaveformref-methods) for more details about which methods these refs provides. |
134161
| path\* | - ||| string | Used for `static` type. It is the resource path of an audio source file. |
135162
| playbackSpeed | 1.0 ||| 1.0 / 1.5 / 2.0 | The playback speed of the audio player. Note: Currently playback speed only supports, Normal (1x) Faster(1.5x) and Fastest(2.0x), any value passed to playback speed greater than 2.0 will be automatically adjusted to normal playback speed |
163+
| volume | 3 ||| number | Used for `static` type. It is a volume level for the media player, ranging from 1 to 10. |
164+
| isExternalUrl | false ||| boolean | Used for `static` type. If the resource path of an audio file is a URL, then pass true; otherwise, pass false. |
165+
| downloadExternalAudio | true ||| boolean | Used for `static` type. Indicates whether the external media should be downloaded. |
136166
| candleSpace | 2 ||| number | Space between two candlesticks of waveform |
137167
| candleWidth | 5 ||| number | Width of single candlestick of waveform |
138168
| candleHeightScale | 3 ||| number | Scaling height of candlestick of waveform |
@@ -145,6 +175,8 @@ You can check out the full example at [Example](./example/src/App.tsx).
145175
| onRecorderStateChange | - ||| ( recorderState : RecorderState ) => void | callback function which returns the recorder state whenever the recorder state changes. Check RecorderState for more details |
146176
| onCurrentProgressChange | - ||| ( currentProgress : number, songDuration: number ) => void | callback function, which returns current progress of audio and total song duration. |
147177
| onChangeWaveformLoadState | - ||| ( state : boolean ) => void | callback function which returns the loading state of waveform candlestick. |
178+
| onDownloadStateChange | - ||| ( state : boolean ) => void | A callback function that returns the loading state of a file download from an external URL. |
179+
| onDownloadProgressChange | - ||| ( currentProgress : number ) => void | Used when isExternalUrl is true; a callback function that returns the current progress of a file download from an external URL |
148180
| onError | - ||| ( error : Error ) => void | callback function which returns the error for static audio waveform |
149181

150182
##### Know more about [ViewStyle](https://reactnative.dev/docs/view-style-props), [PlayerState](#playerstate), and [RecorderState](#recorderstate)

example/src/App.tsx

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
ScrollView,
2626
StatusBar,
2727
Text,
28+
TouchableOpacity,
2829
View,
2930
} from 'react-native';
3031
import { GestureHandlerRootView } from 'react-native-gesture-handler';
@@ -49,18 +50,22 @@ const RenderListItem = React.memo(
4950
onPanStateChange,
5051
currentPlaybackSpeed,
5152
changeSpeed,
53+
isExternalUrl = false,
5254
}: {
5355
item: ListItem;
5456
currentPlaying: string;
5557
setCurrentPlaying: Dispatch<SetStateAction<string>>;
5658
onPanStateChange: (value: boolean) => void;
5759
currentPlaybackSpeed: PlaybackSpeedType;
5860
changeSpeed: () => void;
61+
isExternalUrl?: boolean;
5962
}) => {
6063
const ref = useRef<IWaveformRef>(null);
6164
const [playerState, setPlayerState] = useState(PlayerState.stopped);
6265
const styles = stylesheet({ currentUser: item.fromCurrentUser });
63-
const [isLoading, setIsLoading] = useState(true);
66+
const [isLoading, setIsLoading] = useState(isExternalUrl ? false : true);
67+
const [downloadExternalAudio, setDownloadExternalAudio] = useState(false);
68+
const [isAudioDownloaded, setIsAudioDownloaded] = useState(false);
6469

6570
const handleButtonAction = () => {
6671
if (playerState === PlayerState.stopped) {
@@ -70,6 +75,11 @@ const RenderListItem = React.memo(
7075
}
7176
};
7277

78+
const handleDownloadPress = (): void => {
79+
setDownloadExternalAudio(true);
80+
setIsLoading(true);
81+
};
82+
7383
useEffect(() => {
7484
if (currentPlaying !== item.path) {
7585
ref.current?.stopPlayer();
@@ -79,7 +89,15 @@ const RenderListItem = React.memo(
7989
}, [currentPlaying]);
8090

8191
return (
82-
<View key={item.path} style={[styles.listItemContainer]}>
92+
<View
93+
key={item.path}
94+
style={[
95+
styles.listItemContainer,
96+
item.fromCurrentUser &&
97+
isExternalUrl &&
98+
!isAudioDownloaded &&
99+
styles.listItemReverseContainer,
100+
]}>
83101
<View style={styles.listItemWidth}>
84102
<ImageBackground
85103
source={
@@ -118,6 +136,7 @@ const RenderListItem = React.memo(
118136
scrubColor={Colors.white}
119137
waveColor={Colors.gray}
120138
candleHeightScale={4}
139+
downloadExternalAudio={downloadExternalAudio}
121140
onPlayerStateChange={state => {
122141
setPlayerState(state);
123142
if (
@@ -127,10 +146,20 @@ const RenderListItem = React.memo(
127146
setCurrentPlaying('');
128147
}
129148
}}
149+
isExternalUrl={isExternalUrl}
130150
onPanStateChange={onPanStateChange}
131151
onError={error => {
132152
console.log(error, 'we are in example');
133153
}}
154+
onDownloadStateChange={state => {
155+
console.log('Download State', state);
156+
}}
157+
onDownloadProgressChange={progress => {
158+
console.log('Download Progress', `${progress}%`);
159+
if (progress === 100) {
160+
setIsAudioDownloaded(true);
161+
}
162+
}}
134163
onCurrentProgressChange={(currentProgress, songDuration) => {
135164
console.log(
136165
'currentProgress ',
@@ -154,6 +183,15 @@ const RenderListItem = React.memo(
154183
)}
155184
</ImageBackground>
156185
</View>
186+
{isExternalUrl && !downloadExternalAudio && !isAudioDownloaded ? (
187+
<TouchableOpacity onPress={handleDownloadPress}>
188+
<Image
189+
source={Icons.download}
190+
style={styles.downloadIcon}
191+
resizeMode="contain"
192+
/>
193+
</TouchableOpacity>
194+
) : null}
157195
</View>
158196
);
159197
}
@@ -281,6 +319,7 @@ const AppContainer = () => {
281319
currentPlaying={currentPlaying}
282320
setCurrentPlaying={setCurrentPlaying}
283321
item={item}
322+
isExternalUrl={item.isExternalUrl}
284323
onPanStateChange={value => setShouldScroll(!value)}
285324
{...{ currentPlaybackSpeed, changeSpeed }}
286325
/>
8.4 KB
Loading

example/src/assets/icons/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ export const Icons = {
44
simform: require('./simform.png'),
55
mic: require('./mic.png'),
66
logo: require('./logo.png'),
7+
download: require('./download.png'),
78
};

example/src/constants/Audios.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { globalMetrics } from '../../src/theme';
55
export interface ListItem {
66
fromCurrentUser: boolean;
77
path: string;
8+
isExternalUrl?: boolean;
89
}
910

1011
/**
@@ -69,6 +70,11 @@ const audioAssetArray = [
6970
'file_example_mp3_15s.mp3',
7071
];
7172

73+
const externalAudioAssetArray = [
74+
'https://codeskulptor-demos.commondatastorage.googleapis.com/GalaxyInvaders/theme_01.mp3',
75+
'https://codeskulptor-demos.commondatastorage.googleapis.com/pang/paza-moduless.mp3',
76+
];
77+
7278
/**
7379
* Generate a list of file objects with information about successfully copied files (Android)
7480
* or all files (iOS).
@@ -78,8 +84,18 @@ export const generateAudioList = async (): Promise<ListItem[]> => {
7884
const audioAssets = await copyFilesToNativeResources();
7985

8086
// Generate the final list based on the copied or available files
81-
return audioAssets?.map?.((value, index) => ({
87+
const localAssetList = audioAssets?.map?.((value, index) => ({
8288
fromCurrentUser: index % 2 !== 0,
8389
path: `${filePath}/${value}`,
8490
}));
91+
92+
const externalAudioList: ListItem[] = externalAudioAssetArray.map(
93+
(value, index) => ({
94+
fromCurrentUser: index % 2 !== 0,
95+
path: value,
96+
isExternalUrl: true,
97+
})
98+
);
99+
100+
return [...localAssetList, ...externalAudioList];
85101
};

example/src/styles.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,16 @@ const styles = (params: StyleSheetParams = {}) =>
3939
},
4040
listItemContainer: {
4141
marginTop: scale(16),
42-
alignItems: params.currentUser ? 'flex-end' : 'flex-start',
42+
flexDirection: 'row',
43+
justifyContent: params.currentUser ? 'flex-end' : 'flex-start',
44+
alignItems: 'center',
45+
},
46+
listItemReverseContainer: {
47+
flexDirection: 'row-reverse',
48+
alignSelf: 'flex-end',
4349
},
4450
listItemWidth: {
45-
width: '90%',
51+
width: '88%',
4652
},
4753
buttonImage: {
4854
height: scale(22),
@@ -107,6 +113,13 @@ const styles = (params: StyleSheetParams = {}) =>
107113
textAlign: 'center',
108114
fontWeight: '600',
109115
},
116+
downloadIcon: {
117+
width: 20,
118+
height: 20,
119+
tintColor: Colors.pink,
120+
marginLeft: 10,
121+
marginRight: 10,
122+
},
110123
});
111124

112125
export default styles;

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
]
111111
},
112112
"dependencies": {
113-
"lodash": "^4.17.21"
113+
"lodash": "^4.17.21",
114+
"rn-fetch-blob": "^0.12.0"
114115
}
115-
}
116+
}

0 commit comments

Comments
 (0)