123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612 |
- import React, { useState, useEffect, useRef, useMemo, useContext } from 'react';
- import {
- View,
- Text,
- StyleSheet,
- Dimensions,
- PanResponder,
- ScrollView,
- Image,
- Platform,
- StatusBar,
- TouchableOpacity,
- Share
- } from 'react-native';
- import Modal from 'react-native-modal';
- import { Slider, Icon } from 'react-native-elements';
- import Animated, { Easing } from 'react-native-reanimated';
- import { State, PanGestureHandler } from 'react-native-gesture-handler';
- import { getStatusBarHeight } from '../utils/StatusBarHeight';
- import C from 'rn-class';
- import PlayerControls from './PlayerControls';
- import PlayerContents from './PlayerContents';
- import PlayerHeader from './PlayerHeader';
- import { getInset } from 'react-native-safe-area-view';
- import Video from 'react-native-video';
- import { PlayerDispatchContext, PlayerStateContext } from './PlayerReducer';
- const { width, height } = Dimensions.get('window');
- const statusBarHeight = getStatusBarHeight(true);
- const minHeight = height * 0.1;
- const midBound = height - height / 2.5;
- const upperBound = midBound + minHeight;
- //const destination = height - height * 0.2
- //const destination = height - height*0.16 -2 - getStatusBarHeight(true);
- //getInset('bottom')
- // height > 560
- // ?height * 0.75 - getInset('bottom')
- // :height * 0.70;
- const {
- Value,
- event,
- Extrapolate,
- cond,
- Clock,
- eq,
- set,
- add,
- sub,
- multiply,
- lessThan,
- clockRunning,
- startClock,
- spring,
- stopClock,
- interpolate,
- timing,
- neq,
- or,
- and,
- greaterThan,
- call
- } = Animated;
- const shadow = {
- alignItems: 'center',
- shadowColor: 'black',
- shadowOffset: { width: 0, height: 0 },
- shadowOpacity: 0.18,
- shadowRadius: 2
- };
- const shadow2 = {
- alignItems: 'center',
- shadowColor: '#000',
- shadowOffset: {
- width: 0,
- height: 7
- },
- shadowOpacity: 0.41,
- shadowRadius: 9.11
- //elevation: 14
- };
- const shadowShare = {
- shadowColor: '#000',
- shadowOffset: {
- width: 0,
- height: 7
- },
- shadowOpacity: 0.41,
- shadowRadius: 9.11,
- elevation: 14
- };
- const shadowImage = {
- shadowColor: '#000',
- shadowOffset: {
- width: 0,
- height: 2
- },
- shadowOpacity: 0.25,
- shadowRadius: 3.84,
- elevation: 5
- };
- function runSpring(clock, value, dest) {
- const state = {
- finished: new Value(0),
- velocity: new Value(0),
- position: new Value(0),
- time: new Value(0)
- };
- const config = {
- damping: 20,
- mass: 1,
- stiffness: 100,
- overshootClamping: false,
- restSpeedThreshold: 1,
- restDisplacementThreshold: 0.5,
- toValue: new Value(0)
- };
- return [
- cond(clockRunning(clock), 0, [
- set(state.finished, 0),
- set(state.velocity, 0),
- set(state.position, value),
- set(config.toValue, dest),
- startClock(clock)
- ]),
- spring(clock, state, config),
- cond(state.finished, stopClock(clock)),
- state.position
- ];
- }
- const onShare = async () => {
- try {
- const result = await Share.share({
- message: '페이스북 | https://facebook.com'
- });
- if (result.action === Share.sharedAction) {
- if (result.activityType) {
- // shared with activity type of result.activityType
- } else {
- // shared
- }
- } else if (result.action === Share.dismissedAction) {
- // dismissed
- }
- } catch (error) {
- alert(error.message);
- }
- };
- export default props => {
- const [modalVisible, setModalVisible] = useState(null);
- const { y: tabY } = props.tabPosition;
- const destination = tabY - minHeight - statusBarHeight;
- const [currentPlayerValue, setCurrentPlayerValue] = useState(0);
- const [isPlay, setPlay] = useState(false);
- const { playerData } = props;
- const {
- onGestureEvent,
- translateY,
- opacity,
- playContainerWidth,
- playerContainerHeight,
- videoWidth,
- videoHeight,
- playerControlOpaciy,
- playerBoderRadius,
- mainPlayerContainerHeight,
- PlayerHeadWidth,
- PlayerHeadHeight,
- PlayerHeadOpacity
- } = useMemo(() => {
- const translationY = new Value(0);
- const velocityY = new Value(0);
- const offsetY = new Value(0);
- const offsetY2 = new Value(0);
- const gestureState = new Value(State.UNDETERMINED);
- const clockY = new Clock();
- const finalTranslateY = add(translationY, multiply(0.2, velocityY));
- /**
- * snapPoint 제스처가 끝날 때 위치 지정을 해주는 값
- * TabBar 의 높이가 해상도와 스크린에서 그려지는 값이 틀려 TabBar 에서
- * onLayout 이벤트를 이용하여 TabBar의 y 값을 가져와 처리
- * destination 변수가 플레이어 축소 시 y 값 높이
- */
- const snapPoint = cond(
- lessThan(finalTranslateY, sub(offsetY, height / 4)),
- 0,
- destination
- );
- const ty = cond(
- and(eq(gestureState, State.END), neq(translationY, 0)),
- [
- set(
- translationY,
- runSpring(clockY, add(offsetY, translationY), snapPoint)
- ),
- set(offsetY, translationY),
- translationY
- ],
- [
- cond(eq(gestureState, State.BEGAN), stopClock(clockY)),
- add(offsetY, translationY)
- ]
- );
- const translateY = add(ty, offsetY2);
- // const onGestureEvent = event(
- // [
- // {
- // nativeEvent: {
- // translationY,
- // velocityY,
- // state: gestureState
- // }
- // }
- // ],
- // { useNativeDriver: true }
- // );
- return {
- translateY,
- onGestureEvent: event(
- [
- {
- nativeEvent: {
- translationY,
- velocityY,
- state: gestureState
- }
- }
- ],
- { useNativeDriver: true }
- ),
- opacity: interpolate(translateY, {
- inputRange: [0, midBound],
- outputRange: [1, 0],
- extrapolate: Extrapolate.CLAMP
- }),
- playContainerWidth: interpolate(translateY, {
- inputRange: [0, midBound],
- outputRange: [width, width - 16],
- extrapolate: Extrapolate.CLAMP
- }),
- playerContainerHeight: interpolate(translateY, {
- inputRange: [0, midBound],
- outputRange: [height, 0],
- extrapolate: Extrapolate.CLAMP
- }),
- videoWidth: interpolate(translateY, {
- inputRange: [0, midBound, upperBound],
- outputRange: [width, width - 16, width / 4],
- extrapolate: Extrapolate.CLAMP
- }),
- videoHeight: interpolate(translateY, {
- inputRange: [0, midBound, upperBound],
- outputRange: [width / 1.78, minHeight * 1.3, minHeight],
- extrapolate: Extrapolate.CLAMP
- }),
- playerControlOpaciy: interpolate(translateY, {
- inputRange: [midBound, upperBound],
- outputRange: [0, 1],
- extrapolate: Extrapolate.CLAMP
- }),
- playerBoderRadius: interpolate(translateY, {
- inputRange: [midBound, upperBound],
- outputRange: [0, 15],
- extrapolate: Extrapolate.CLAMP
- }),
- mainPlayerContainerHeight: interpolate(translateY, {
- inputRange: [0, midBound, upperBound],
- outputRange: [height, minHeight * 1.3, minHeight],
- extrapolate: Extrapolate.CLAMP
- }),
- PlayerHeadWidth: interpolate(translateY, {
- inputRange: [0, width],
- outputRange: [width, 0],
- extrapolate: Extrapolate.CLAMP
- }),
- PlayerHeadHeight: interpolate(translateY, {
- inputRange: [0, 50],
- outputRange: [50, 0],
- extrapolate: Extrapolate.CLAMP
- }),
- PlayerHeadOpacity: interpolate(translateY, {
- inputRange: [0, minHeight],
- outputRange: [1, 0],
- extrapolate: Extrapolate.CLAMP
- })
- };
- }, []);
- useEffect(() => {
- playerDispatch({type:'currentTime', setCurrentTime: 0});
- return () => {
- playerDispatch({type:'currentTime', setCurrentTime: 0});
- };
- },[playerData.soundURI]);
- const slideUp = () => {};
- const player = useRef(null);
-
- const playerDispatch = useContext(PlayerDispatchContext);
- const { uri, paused, muted } = useContext(PlayerStateContext);
- const playerOnBuffer = (data) => {
- // console.log('bufffer::: ' + data)
- }
- const playerOnError = (e) => {
- console.log(e);
- }
- const playerOnLoad = (data) => {
- // console.log(data.duration)
- let duration=0;
- data.duration < 0 ? duration=1 : duration=data.duration
- playerDispatch({type:'paused', setPaused:false});
- playerDispatch({type:'duration', setDuraton: Math.floor(duration)});
- }
- const playerOnProgress = (data) => {
- // console.log(data.currentTime)
- playerDispatch({type:'currentTime', setCurrentTime: data.currentTime});
- }
- const onLoadStart = () => {
- // console.log('onload start');
- playerDispatch({type:'player', player: player});
- }
- const playerOnEnd = () => {
- //console.log('end');
- }
- const onSeek = (data) => {
- //console.log(data)
- if(data.seekTime===0 && data.currentTime===0) {
- playerDispatch({type:'paused', setPaused: true});
- playerDispatch({type:'currentTime', setCurrentTime: 0});
- }
- }
- return (
- <>
- {/* <C.View
- style={{ ...StyleSheet.absoluteFillObject, zIndex: 1000, marginTop:getStatusBarHeight(true) }}
- cls="h20 bgc-rgba-white-0_5"
- >
- <C.Text cls="f5 ta-c bgc-rgba-red-0_5">테스트용 Component</C.Text>
- <C.View cls="h10 ai-c bgc-rgba-blue-0_5">
- <Slider
- style={{ width: '95%' }}
- trackStyle={{ height: 2 }}
- thumbTintColor="#333"
- thumbStyle={{ width: 10, height: 5 }}
- value={currentPlayerValue}
- onValueChange={value => setCurrentPlayerValue(value)}
- />
- <C.View cls="w95 jc-sb flx-row">
- <C.Text>Time : {currentPlayerValue}</C.Text>
- </C.View>
- </C.View>
- <C.TouchableOpacity
- cls="h5 bgc-rgba-black-0_5"
- onPress={() => {
- setPlay(!isPlay);
- }}
- >
- <C.Text cls="f5 ta-c rgba-white-0_5">Click Test</C.Text>
- </C.TouchableOpacity>
- </C.View> */}
- <PanGestureHandler
- onHandlerStateChange={onGestureEvent}
- activeOffsetY={[-10, 10]}
- onGestureEvent={onGestureEvent}
- >
- <Animated.View
- style={{
- ...StyleSheet.absoluteFillObject,
- zIndex: 10,
- marginTop: statusBarHeight,
- transform: [{ translateY }],
- alignItems: 'center',
- height: mainPlayerContainerHeight
- }}
- >
- <Animated.View
- style={{
- padding: 0,
- backgroundColor: 'white',
- width: playContainerWidth,
- borderRadius: playerBoderRadius
- }}
- >
- <Animated.View
- style={[
- Platform.OS === 'android'
- ? { borderColor: '#eee' }
- : { borderColor: 'white' },
- {
- ...StyleSheet.absoluteFillObject,
- borderWidth: 1,
- borderRadius: 15,
- opacity: playerControlOpaciy,
- backgroundColor: 'white'
- },
- shadow2
- ]}
- >
- <PlayerControls
- {...props}
- />
- </Animated.View>
- <Animated.View
- style={{
- opacity: PlayerHeadOpacity,
- backgroundColor: 'rgba(255,255,255,0)',
- width: PlayerHeadWidth,
- height: PlayerHeadHeight
- }}
- >
- <PlayerHeader {...props} visibleMenu={setModalVisible} />
- </Animated.View>
- <Animated.View style={{ width: videoWidth, height: videoHeight }}>
- <Animated.Image
- resizeMode="contain"
- style={{ flex: 1, margin: 10, width: null, height: null }}
- source={{uri: playerData.img}}
- />
- <Video
- source={{uri:playerData.soundURI}}
- style={StyleSheet.absoluteFill}
- ref={player}
- //rate={this.state.rate}
- paused={paused}
- //volume={this.state.volume}
- muted={muted}
- //ignoreSilentSwitch={this.state.ignoreSilentSwitch}
- resizeMode='contain'
- onLoad={playerOnLoad}
- onLoadStart={onLoadStart}
- onBuffer={playerOnBuffer}
- onProgress={playerOnProgress}
- onSeek={onSeek}
- onError={playerOnError}
- onEnd={playerOnEnd}
- //poster='https://baconmockup.com/300/200/'
- repeat={true}
- //filter={this.state.filter}
- //filterEnabled={this.state.filterEnabled}
- audioOnly={true}
- />
- </Animated.View>
- </Animated.View>
- <Animated.View
- style={{
- backgroundColor: 'white',
- width: playContainerWidth,
- height: playerContainerHeight
- }}
- >
- <Animated.View style={{ opacity }}>
- <PlayerContents
- {...props}
- />
- </Animated.View>
- </Animated.View>
- </Animated.View>
- </PanGestureHandler>
- <Modal
- isVisible={modalVisible === 'detail'}
- onSwipeComplete={() => {
- setModalVisible(null);
- }}
- swipeDirection="left"
- //deviceHeight={height}
- deviceWidth={width}
- style={{ margin: 0 }}
- backdropColor="white"
- backdropOpacity={1}
- animationIn="slideInLeft"
- animationOut="slideOutLeft"
- >
- <C.View cls="w90 h100 bgc-rgba-black-0_5">
- <ScrollView style={{ flex: 1, marginTop: '10%' }}>
- <C.Text cls="flx1 fw-b ta-c">{playerData.title}</C.Text>
- <C.View cls="flx2 ph5 mt5">
- <C.Text cls="fw-b">{playerData.title}</C.Text>
- <C.Text cls="mt2">{playerData.content}</C.Text>
- <C.Text cls="mt3" numberOfLines={1}>
- {/* by {playerData.author.replace(/\r\n|\n|\r/gm, ' ')} */}
- </C.Text>
- <C.Text cls="mt5 as-e" numberOfLines={1}>
- {playerData.date}
- </C.Text>
- </C.View>
- </ScrollView>
- </C.View>
- <TouchableOpacity
- style={[
- StyleSheet.absoluteFillObject,
- {
- width: '90%',
- justifyContent: 'center',
- paddingRight: '5%',
- alignItems: 'flex-end'
- }
- ]}
- onPress={() => {
- setModalVisible(null);
- }}
- >
- <C.EL.Icon
- color="rgb(10,132,255)"
- {...C.n2cls('size10')}
- type="ionicon"
- name="md-arrow-dropleft"
- />
- </TouchableOpacity>
- </Modal>
- <Modal isVisible={modalVisible === 'share'} style={{ margin: 0 }}>
- <View
- style={{
- flex: 1,
- justifyContent: 'center',
- alignItems: 'center',
- backgroundColor: 'rgb(242,242,247)'
- }}
- >
- <ScrollView>
- <View style={{ height: statusBarHeight }} />
- <View style={{ marginHorizontal: '5%', alignItems: 'flex-end' }}>
- <C.EL.Icon
- color="black"
- {...C.n2cls('size10')}
- type="ionicon"
- name="md-close"
- onPress={() => {
- setModalVisible(null);
- }}
- />
- </View>
- <View
- style={{
- //...shadowImage,
- //backgroundColor: 'white',
- marginHorizontal: '5%',
- width: width * 0.8,
- height: (width * 0.8) / 1.78
- }}
- >
- <Image
- resizeMode="contain"
- style={{
- flex: 1,
- marginVertical: 10,
- width: null,
- height: null
- }}
- source={{uri:playerData.img}}
- />
- </View>
- <C.View cls="flx-row jc-sa ai-t mt2">
- <C.Text cls="fw-b">{playerData.title}</C.Text>
- <C.Text cls="fw-b">{playerData.duration}</C.Text>
- </C.View>
- <View style={{ height: minHeight }} />
- <TouchableOpacity
- onPress={onShare}
- style={{ marginHorizontal: '5%', alignItems: 'center' }}
- >
- <View style={[s.sharContainer]}>
- <C.EL.Icon
- color="black"
- {...C.n2cls('size10')}
- type="ionicon"
- name="md-share"
- />
- <C.Text cls="f2.5">공유하기, 메세지 보내기</C.Text>
- </View>
- </TouchableOpacity>
- <View style={{ height: minHeight }} />
- </ScrollView>
- </View>
- </Modal>
- </>
- );
- };
- const s = StyleSheet.create({
- sharContainer: {
- backgroundColor: 'white',
- flexDirection: 'row',
- justifyContent: 'space-around',
- alignItems: 'center',
- borderColor: '#ddd',
- borderRadius: 15,
- flex: 1,
- width: width * 0.8,
- height: minHeight,
- ...shadowShare
- }
- });
|