├── .babelrc ├── .buckconfig ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .watchmanconfig ├── App.js ├── App ├── Component │ ├── Button.android.js │ ├── Button.ios.js │ ├── HomeCustomTabBar.js │ ├── Logo.js │ ├── MainView.js │ ├── PhoneModal.js │ ├── SongBox.js │ ├── SongCustomTabBar.js │ ├── Style │ │ ├── LogoStyle.js │ │ ├── MainViewStyle.js │ │ ├── PhoneModalStyle.js │ │ └── SongBoxStyle.js │ └── VideoComponent.js ├── Config │ ├── ReactotronConfig.js │ └── index.js ├── Container │ └── AppContainer.js ├── Img │ ├── Image.js │ ├── back.jpeg │ └── logo.png ├── Lib │ └── lyric-parser │ │ ├── .babelrc │ │ ├── .editorconfig │ │ ├── .eslintignore │ │ ├── .eslintrc.js │ │ ├── .npmignore │ │ ├── LICENSE │ │ ├── README.md │ │ ├── demo │ │ └── index.html │ │ ├── dist │ │ └── lyric.js │ │ ├── package.json │ │ └── src │ │ └── index.js ├── Service │ ├── ApiUtil.js │ ├── action.js │ └── index.js ├── Store │ ├── actionCreators.js │ ├── actionTypes.js │ ├── index.js │ └── reducer │ │ ├── themeReducer.js │ │ └── userReducer.js ├── Util │ ├── CountryCode.js │ ├── StatusBar.js │ └── Tool.js └── View │ ├── FindView.js │ ├── Home.js │ ├── LeftView.js │ ├── LiveView.js │ ├── LoginView.js │ ├── MyView.js │ ├── PhoneLogin.js │ ├── PlaySongView.js │ ├── RadioStation.js │ ├── RankingListView.js │ ├── SongList.js │ ├── SongPlayList.js │ ├── SongSquare.js │ ├── Style │ ├── FindViewStyle.js │ ├── HomeStyle.js │ ├── LeftViewStyle.js │ ├── LiveStyle.js │ ├── LoginViewStyle.js │ ├── MyViewStyle.js │ ├── PhoneLoginStyle.js │ ├── PlaySongStyle.js │ ├── RadioStationStyle.js │ ├── RankingListStyle.js │ ├── SongListStyle.js │ ├── SongPlayListStyle.js │ ├── SongSquareStyle.js │ ├── VideoListStyle.js │ ├── VideoViewStyle.js │ └── YunCunStyle.js │ ├── VideoList.js │ ├── VideoView.js │ └── YunCun.js ├── README.md ├── android ├── app │ ├── BUCK │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ └── fonts │ │ │ ├── AntDesign.ttf │ │ │ ├── Entypo.ttf │ │ │ ├── EvilIcons.ttf │ │ │ ├── Feather.ttf │ │ │ ├── FontAwesome.ttf │ │ │ ├── FontAwesome5_Brands.ttf │ │ │ ├── FontAwesome5_Regular.ttf │ │ │ ├── FontAwesome5_Solid.ttf │ │ │ ├── Fontisto.ttf │ │ │ ├── Foundation.ttf │ │ │ ├── Ionicons.ttf │ │ │ ├── MaterialCommunityIcons.ttf │ │ │ ├── MaterialIcons.ttf │ │ │ ├── Octicons.ttf │ │ │ ├── SimpleLineIcons.ttf │ │ │ └── Zocial.ttf │ │ ├── java │ │ └── com │ │ │ └── react_native_music │ │ │ ├── MainActivity.java │ │ │ └── MainApplication.java │ │ └── res │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── keystores │ ├── BUCK │ └── debug.keystore.properties └── settings.gradle ├── app.json ├── index.js ├── ios ├── react_native_music-tvOS │ └── Info.plist ├── react_native_music-tvOSTests │ └── Info.plist ├── react_native_music.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── react_native_music-tvOS.xcscheme │ │ └── react_native_music.xcscheme ├── react_native_music │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ └── LaunchScreen.xib │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Info.plist │ └── main.m └── react_native_musicTests │ ├── Info.plist │ └── react_native_musicTests.m ├── package.json └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["module:metro-react-native-babel-preset"] 3 | } 4 | -------------------------------------------------------------------------------- /.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | 16 | ; Ignore polyfills 17 | .*/Libraries/polyfills/.* 18 | 19 | ; Ignore metro 20 | .*/node_modules/metro/.* 21 | 22 | [include] 23 | 24 | [libs] 25 | node_modules/react-native/Libraries/react-native/react-native-interface.js 26 | node_modules/react-native/flow/ 27 | node_modules/react-native/flow-github/ 28 | 29 | [options] 30 | emoji=true 31 | 32 | esproposal.optional_chaining=enable 33 | esproposal.nullish_coalescing=enable 34 | 35 | module.system=haste 36 | module.system.haste.use_name_reducers=true 37 | # get basename 38 | module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1' 39 | # strip .js or .js.flow suffix 40 | module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1' 41 | # strip .ios suffix 42 | module.system.haste.name_reducers='^\(.*\)\.ios$' -> '\1' 43 | module.system.haste.name_reducers='^\(.*\)\.android$' -> '\1' 44 | module.system.haste.name_reducers='^\(.*\)\.native$' -> '\1' 45 | module.system.haste.paths.blacklist=.*/__tests__/.* 46 | module.system.haste.paths.blacklist=.*/__mocks__/.* 47 | module.system.haste.paths.blacklist=/node_modules/react-native/Libraries/Animated/src/polyfills/.* 48 | module.system.haste.paths.whitelist=/node_modules/react-native/Libraries/.* 49 | 50 | munge_underscores=true 51 | 52 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 53 | 54 | module.file_ext=.js 55 | module.file_ext=.jsx 56 | module.file_ext=.json 57 | module.file_ext=.native.js 58 | 59 | suppress_type=$FlowIssue 60 | suppress_type=$FlowFixMe 61 | suppress_type=$FlowFixMeProps 62 | suppress_type=$FlowFixMeState 63 | 64 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 65 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 66 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 67 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 68 | 69 | [version] 70 | ^0.78.0 71 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | * @flow 7 | */ 8 | 9 | import React from 'react'; 10 | import { Provider } from 'react-redux' 11 | import configureStore from './App/Store' 12 | import {PersistGate} from 'redux-persist/integration/react' 13 | import AppNav from './App/Container/AppContainer' 14 | 15 | 16 | const {store, persistor} = configureStore(); 17 | 18 | export default class App extends React.Component { 19 | render() { 20 | return ( 21 | 22 | 23 | 24 | 25 | 26 | ); 27 | } 28 | } 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /App/Component/Button.android.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | const ReactNative = require('react-native'); 3 | const { 4 | TouchableNativeFeedback, 5 | View, 6 | } = ReactNative; 7 | 8 | const Button = (props) => { 9 | return 14 | {props.children} 15 | ; 16 | }; 17 | 18 | module.exports = Button; 19 | -------------------------------------------------------------------------------- /App/Component/Button.ios.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | const ReactNative = require('react-native'); 3 | const { 4 | TouchableOpacity, 5 | View, 6 | } = ReactNative; 7 | 8 | const Button = (props) => { 9 | return 10 | {props.children} 11 | ; 12 | }; 13 | 14 | module.exports = Button; 15 | -------------------------------------------------------------------------------- /App/Component/HomeCustomTabBar.js: -------------------------------------------------------------------------------- 1 | import {Icon} from "react-native-elements"; 2 | 3 | const React = require('react'); 4 | const { ViewPropTypes, TouchableOpacity} = ReactNative = require('react-native'); 5 | const PropTypes = require('prop-types'); 6 | const createReactClass = require('create-react-class'); 7 | const { 8 | StyleSheet, 9 | Text, 10 | View, 11 | Animated, 12 | } = ReactNative; 13 | 14 | const Button = require('./Button'); 15 | 16 | const HomeCustomTabBar = createReactClass({ 17 | propTypes: { 18 | goToPage: PropTypes.func, 19 | activeTab: PropTypes.number, 20 | tabs: PropTypes.array, 21 | backgroundColor: PropTypes.string, 22 | activeTextColor: PropTypes.string, 23 | inactiveTextColor: PropTypes.string, 24 | textStyle: Text.propTypes.style, 25 | tabStyle: ViewPropTypes.style, 26 | renderTab: PropTypes.func, 27 | underlineStyle: ViewPropTypes.style, 28 | activeSize: PropTypes.number, 29 | inactiveSize: PropTypes.number, 30 | rightClick: PropTypes.func, 31 | leftClick: PropTypes.func 32 | }, 33 | 34 | getDefaultProps() { 35 | return { 36 | activeTextColor: 'navy', 37 | inactiveTextColor: 'black', 38 | backgroundColor: null, 39 | }; 40 | }, 41 | 42 | renderTabOption(name, page) { 43 | }, 44 | 45 | renderTab(name, page, isTabActive, onPressHandler) { 46 | const { activeTextColor, inactiveTextColor, textStyle, activeSize, inactiveSize } = this.props; 47 | const textColor = isTabActive ? activeTextColor : inactiveTextColor; 48 | const fontWeight = isTabActive ? 'bold' : 'normal'; 49 | const fontSize = isTabActive ? activeSize : inactiveSize; 50 | 51 | return ; 65 | }, 66 | 67 | render() { 68 | 69 | return ( 70 | 71 | { 72 | this.props.leftClick && this.props.leftClick() 73 | }}> 74 | 79 | 80 | 81 | {this.props.tabs.map((name, page) => { 82 | const isTabActive = this.props.activeTab === page; 83 | const renderTab = this.props.renderTab || this.renderTab; 84 | return renderTab(name, page, isTabActive, this.props.goToPage); 85 | })} 86 | { 87 | this.props.rightClick&&this.props.rightClick() 88 | }}> 89 | 93 | 94 | 95 | ); 96 | }, 97 | }); 98 | 99 | const styles = StyleSheet.create({ 100 | tab: { 101 | flex: 1, 102 | alignItems: 'center', 103 | justifyContent: 'center', 104 | }, 105 | tabs: { 106 | height: 50, 107 | flexDirection: 'row', 108 | justifyContent: 'space-around', 109 | alignItems: 'center', 110 | }, 111 | }); 112 | 113 | module.exports = HomeCustomTabBar; 114 | -------------------------------------------------------------------------------- /App/Component/Logo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Animated, Easing, Image, View, Button} from "react-native"; 3 | import getStyle from './Style/LogoStyle' 4 | import Img from "../Img/Image"; 5 | 6 | let Styles = {} 7 | class Logo extends React.Component{ 8 | 9 | constructor(props) { 10 | super(props); 11 | this.state={ 12 | circle_length1: new Animated.Value(10), 13 | circle_length2: new Animated.Value(10), 14 | circle_border1: new Animated.Value(5), 15 | circle_border2: new Animated.Value(5), 16 | duration: 6000 17 | } 18 | 19 | this.circle_length1 = Animated.timing( 20 | this.state.circle_length1, 21 | { 22 | toValue: 300, 23 | duration: this.state.duration, 24 | easing: Easing.ease, 25 | } 26 | ); 27 | 28 | this.circle_border1 = Animated.timing( 29 | this.state.circle_border1, 30 | { 31 | toValue: 150, 32 | duration: this.state.duration, 33 | easing: Easing.ease, 34 | } 35 | ); 36 | 37 | this.circle_length2 = Animated.timing( 38 | this.state.circle_length2, 39 | { 40 | toValue: 300, 41 | duration: this.state.duration, 42 | easing: Easing.ease, 43 | } 44 | ); 45 | 46 | this.circle_border2 = Animated.timing( 47 | this.state.circle_border2, 48 | { 49 | toValue: 150, 50 | duration: this.state.duration, 51 | easing: Easing.ease, 52 | } 53 | ); 54 | 55 | this.animated1 = Animated.parallel([this.circle_length1, this.circle_border1]) 56 | this.animated2 = Animated.parallel([this.circle_length2, this.circle_border2]) 57 | } 58 | 59 | componentDidMount(): void { 60 | setTimeout(()=>{ 61 | Animated.loop(this.animated2).start() 62 | }, 3000) 63 | 64 | Animated.loop(this.animated1).start() 65 | } 66 | 67 | componentWillUnmount(): void { 68 | Animated.loop(this.animated1).stop() 69 | Animated.loop(this.animated2).stop() 70 | } 71 | 72 | 73 | render(){ 74 | Styles = getStyle() 75 | return( 76 | 77 | 87 | 97 | 98 | 99 | ) 100 | } 101 | } 102 | 103 | export default Logo 104 | -------------------------------------------------------------------------------- /App/Component/MainView.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | import React from 'react' 4 | import {View} from 'react-native' 5 | import {getStatusBarHeight} from "../Util/StatusBar"; 6 | import getStyle from './Style/MainViewStyle' 7 | 8 | let Styles = {} 9 | class MainView extends React.Component{ 10 | 11 | constructor(props){ 12 | super(props) 13 | 14 | this.state={ 15 | statusBar: 0, //状态栏高度 16 | } 17 | } 18 | 19 | async componentWillMount(): void { 20 | getStatusBarHeight().then((result) => { 21 | this.setState({ 22 | statusBar: result.height 23 | }) 24 | }) 25 | } 26 | 27 | render(){ 28 | Styles = getStyle(); 29 | return( 30 | 31 | {this.props.children} 32 | 33 | ) 34 | } 35 | } 36 | 37 | export default MainView; 38 | 39 | -------------------------------------------------------------------------------- /App/Component/PhoneModal.js: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react' 3 | import {Modal, PanResponder, View, Text, SectionList, TouchableOpacity} from "react-native"; 4 | import getStyle from './Style/PhoneModalStyle' 5 | import {Dimensions} from 'react-native' 6 | import {Icon, Divider} from "react-native-elements"; 7 | import CountryCode from '../Util/CountryCode' 8 | 9 | 10 | const winW = Dimensions.get('window').width 11 | const winH = Dimensions.get('window').height 12 | 13 | let Styles = {} 14 | 15 | export default class PhoneModal extends React.Component{ 16 | constructor(props) { 17 | super(props); 18 | this.state={ 19 | modalVisible: false, 20 | constTop: 60, 21 | top: 60, 22 | dataSource: CountryCode 23 | } 24 | 25 | } 26 | 27 | componentWillMount(): void { 28 | this._panResponder = PanResponder.create({ 29 | // 要求成为响应者: 30 | onStartShouldSetPanResponder: (evt, gestureState) => true, 31 | onStartShouldSetPanResponderCapture: (evt, gestureState) => true, 32 | onMoveShouldSetPanResponder: (evt, gestureState) => true, 33 | onMoveShouldSetPanResponderCapture: (evt, gestureState) => true, 34 | 35 | onPanResponderGrant: (evt, gestureState) => { 36 | // 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情! 37 | // gestureState.{x,y} 现在会被设置为0 38 | }, 39 | onPanResponderMove: (evt, gestureState) => { 40 | // 最近一次的移动距离为gestureState.move{X,Y} 41 | let top = this.state.top 42 | if((top + gestureState.dy)>=this.state.constTop){ 43 | this.setState({ 44 | top: gestureState.dy+this.state.constTop 45 | }) 46 | } 47 | 48 | // 从成为响应者开始时的累计手势移动距离为gestureState.d{x,y} 49 | }, 50 | onPanResponderTerminationRequest: (evt, gestureState) => true, 51 | onPanResponderRelease: (evt, gestureState) => { 52 | // 用户放开了所有的触摸点,且此时视图已经成为了响应者。 53 | 54 | if(this.state.top > winH/2){ 55 | this.setState({ 56 | modalVisible: false, 57 | }) 58 | }else{ 59 | this.setState({ 60 | top: this.state.constTop 61 | }) 62 | } 63 | // 一般来说这意味着一个手势操作已经成功完成。 64 | }, 65 | onPanResponderTerminate: (evt, gestureState) => { 66 | // 另一个组件已经成为了新的响应者,所以当前手势将被取消。 67 | }, 68 | onShouldBlockNativeResponder: (evt, gestureState) => { 69 | // 返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者 70 | // 默认返回true。目前暂时只支持android。 71 | return true; 72 | }, 73 | }); 74 | } 75 | 76 | componentDidMount(): void { 77 | 78 | } 79 | 80 | 81 | //渲染 item 82 | renderItem=({ item, index, section: { title, data }})=>{ 83 | return( 84 | { 85 | this.setModalVisible(false) 86 | this.props.getCode&&this.props.getCode(item.dial_code) 87 | }}> 88 | {item.name} 89 | {item.dial_code} 90 | 91 | ) 92 | } 93 | 94 | //渲染头部 95 | renderHeader=({ section: { title }})=>{ 96 | return( 97 | 98 | {title} 99 | 100 | ) 101 | } 102 | 103 | setModalVisible=(visible)=>{ 104 | this.setState({ modalVisible: visible }); 105 | } 106 | 107 | render(){ 108 | Styles = getStyle() 109 | return( 110 | { 115 | this.setState({ 116 | top: this.state.constTop 117 | }) 118 | }} 119 | > 120 | 121 | 122 | 123 | 124 | { 130 | this.setModalVisible(false) 131 | }} 132 | /> 133 | 134 | 选择国家和地区 135 | 136 | 137 | 138 | item + index} 144 | ItemSeparatorComponent={()=> } 145 | canCancelContentTouches={true} 146 | /> 147 | 148 | 149 | 150 | ) 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /App/Component/SongBox.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Image, Text, View} from "react-native"; 3 | import getStyle from './Style/SongBoxStyle' 4 | 5 | 6 | let Styles = {} 7 | class SongBox extends React.Component{ 8 | constructor(props) { 9 | super(props); 10 | this.state={ 11 | 12 | } 13 | } 14 | 15 | render(){ 16 | Styles = getStyle() 17 | let item = this.props.item 18 | return( 19 | 20 | 21 | {item.name} 22 | 23 | 24 | ) 25 | } 26 | 27 | } 28 | 29 | export default SongBox 30 | -------------------------------------------------------------------------------- /App/Component/SongCustomTabBar.js: -------------------------------------------------------------------------------- 1 | const React = require('react'); 2 | const { ViewPropTypes } = ReactNative = require('react-native'); 3 | const PropTypes = require('prop-types'); 4 | const createReactClass = require('create-react-class'); 5 | const { 6 | View, 7 | Animated, 8 | StyleSheet, 9 | ScrollView, 10 | Text, 11 | Platform, 12 | Dimensions, 13 | } = ReactNative; 14 | const Button = require('./Button'); 15 | 16 | const WINDOW_WIDTH = Dimensions.get('window').width; 17 | 18 | let beforePageOffset = 0 19 | 20 | const ScrollableTabBar = createReactClass({ 21 | propTypes: { 22 | goToPage: PropTypes.func, 23 | activeTab: PropTypes.number, 24 | tabs: PropTypes.array, 25 | backgroundColor: PropTypes.string, 26 | activeTextColor: PropTypes.string, 27 | inactiveTextColor: PropTypes.string, 28 | scrollOffset: PropTypes.number, 29 | style: ViewPropTypes.style, 30 | tabStyle: ViewPropTypes.style, 31 | tabsContainerStyle: ViewPropTypes.style, 32 | textStyle: Text.propTypes.style, 33 | renderTab: PropTypes.func, 34 | underlineStyle: ViewPropTypes.style, 35 | onScroll: PropTypes.func, 36 | }, 37 | 38 | getDefaultProps() { 39 | return { 40 | scrollOffset: 52, 41 | activeTextColor: 'navy', 42 | inactiveTextColor: 'black', 43 | backgroundColor: null, 44 | style: {}, 45 | tabStyle: {}, 46 | tabsContainerStyle: {}, 47 | underlineStyle: {}, 48 | }; 49 | }, 50 | 51 | 52 | 53 | getInitialState() { 54 | this._tabsMeasurements = []; 55 | return { 56 | _leftTabUnderline: new Animated.Value(0), 57 | _widthTabUnderline: new Animated.Value(0), 58 | _containerWidth: null, 59 | }; 60 | }, 61 | 62 | componentDidMount() { 63 | this.props.scrollValue.addListener(this.updateView); 64 | }, 65 | 66 | updateView(offset) { 67 | const position = Math.floor(offset.value); 68 | const pageOffset = offset.value % 1; 69 | const tabCount = this.props.tabs.length; 70 | const lastTabPosition = tabCount - 1; 71 | 72 | if (tabCount === 0 || offset.value < 0 || offset.value > lastTabPosition) { 73 | return; 74 | } 75 | 76 | if (this.necessarilyMeasurementsCompleted(position, position === lastTabPosition)) { 77 | this.updateTabPanel(position, pageOffset); 78 | this.updateTabUnderline(position, pageOffset, tabCount); 79 | } 80 | }, 81 | 82 | necessarilyMeasurementsCompleted(position, isLastTab) { 83 | return this._tabsMeasurements[position] && 84 | (isLastTab || this._tabsMeasurements[position + 1]) && 85 | this._tabContainerMeasurements && 86 | this._containerMeasurements; 87 | }, 88 | 89 | updateTabPanel(position, pageOffset) { 90 | const containerWidth = this._containerMeasurements.width; 91 | const tabWidth = this._tabsMeasurements[position].width; 92 | const nextTabMeasurements = this._tabsMeasurements[position + 1]; 93 | const nextTabWidth = nextTabMeasurements && nextTabMeasurements.width || 0; 94 | const tabOffset = this._tabsMeasurements[position].left; 95 | const absolutePageOffset = pageOffset * tabWidth; 96 | let newScrollX = tabOffset + absolutePageOffset; 97 | 98 | // center tab and smooth tab change (for when tabWidth changes a lot between two tabs) 99 | newScrollX -= (containerWidth - (1 - pageOffset) * tabWidth - pageOffset * nextTabWidth) / 2; 100 | newScrollX = newScrollX >= 0 ? newScrollX : 0; 101 | 102 | if (Platform.OS === 'android') { 103 | this._scrollView.scrollTo({x: newScrollX, y: 0, animated: false, }); 104 | } else { 105 | const rightBoundScroll = this._tabContainerMeasurements.width - (this._containerMeasurements.width); 106 | newScrollX = newScrollX > rightBoundScroll ? rightBoundScroll : newScrollX; 107 | this._scrollView.scrollTo({x: newScrollX, y: 0, animated: false, }); 108 | } 109 | 110 | }, 111 | 112 | updateTabUnderline(position, pageOffset, tabCount) { 113 | 114 | 115 | 116 | const lineLeft = this._tabsMeasurements[position].left; 117 | const lineRight = this._tabsMeasurements[position].right; 118 | 119 | 120 | if (position < tabCount - 1) { 121 | const nextTabLeft = this._tabsMeasurements[position + 1].left; 122 | const nextTabRight = this._tabsMeasurements[position + 1].right; 123 | 124 | const newLineLeft = (pageOffset * nextTabLeft + (1 - pageOffset) * lineLeft); 125 | const newLineRight = (pageOffset * nextTabRight + (1 - pageOffset) * lineRight); 126 | 127 | let width = nextTabLeft-lineLeft 128 | let rate = pageOffset/1 129 | let addW = 0 130 | 131 | if(width*rate < width*(1-rate)){ 132 | addW = width*rate 133 | }else{ 134 | addW =width*(1-rate) 135 | } 136 | 137 | if(pageOffset onPressHandler(page)} 163 | onLayout={onLayoutHandler} 164 | > 165 | 166 | 167 | {name} 168 | 169 | 170 | ; 171 | }, 172 | 173 | measureTab(page, event) { 174 | const { x, width, height, } = event.nativeEvent.layout; 175 | this._tabsMeasurements[page] = {left: x, right: x + width, width, height, }; 176 | this.updateView({value: this.props.scrollValue.__getValue(), }); 177 | }, 178 | 179 | render() { 180 | const tabUnderlineStyle = { 181 | position: 'absolute', 182 | height: 4, 183 | backgroundColor: 'navy', 184 | bottom: 0, 185 | }; 186 | 187 | const dynamicTabUnderline = { 188 | left: this.state._leftTabUnderline, 189 | width: this.state._widthTabUnderline, 190 | }; 191 | 192 | return 196 | { this._scrollView = scrollView; }} 198 | horizontal={true} 199 | showsHorizontalScrollIndicator={false} 200 | showsVerticalScrollIndicator={false} 201 | directionalLockEnabled={true} 202 | bounces={false} 203 | scrollsToTop={false} 204 | > 205 | 210 | {this.props.tabs.map((name, page) => { 211 | const isTabActive = this.props.activeTab === page; 212 | const renderTab = this.props.renderTab || this.renderTab; 213 | return renderTab(name, page, isTabActive, this.props.goToPage, this.measureTab.bind(this, page)); 214 | })} 215 | 216 | 217 | 218 | ; 219 | }, 220 | 221 | componentWillReceiveProps(nextProps) { 222 | // If the tabs change, force the width of the tabs container to be recalculated 223 | if (JSON.stringify(this.props.tabs) !== JSON.stringify(nextProps.tabs) && this.state._containerWidth) { 224 | this.setState({ _containerWidth: null, }); 225 | } 226 | }, 227 | 228 | onTabContainerLayout(e) { 229 | this._tabContainerMeasurements = e.nativeEvent.layout; 230 | let width = this._tabContainerMeasurements.width; 231 | if (width < WINDOW_WIDTH) { 232 | width = WINDOW_WIDTH; 233 | } 234 | this.setState({ _containerWidth: width, }); 235 | this.updateView({value: this.props.scrollValue.__getValue(), }); 236 | }, 237 | 238 | onContainerLayout(e) { 239 | this._containerMeasurements = e.nativeEvent.layout; 240 | this.updateView({value: this.props.scrollValue.__getValue(), }); 241 | }, 242 | }); 243 | 244 | module.exports = ScrollableTabBar; 245 | 246 | const styles = StyleSheet.create({ 247 | tab: { 248 | height: 49, 249 | alignItems: 'center', 250 | justifyContent: 'center', 251 | paddingLeft: 20, 252 | paddingRight: 20, 253 | }, 254 | container: { 255 | height: 50, 256 | borderWidth: 1, 257 | borderTopWidth: 0, 258 | borderLeftWidth: 0, 259 | borderRightWidth: 0, 260 | borderColor: '#ccc', 261 | }, 262 | tabs: { 263 | flexDirection: 'row', 264 | justifyContent: 'space-around', 265 | }, 266 | }); 267 | -------------------------------------------------------------------------------- /App/Component/Style/LogoStyle.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | export default getStyle = function () { 4 | return{ 5 | container:{ 6 | alignItems: 'center', 7 | flexDirection: 'column', 8 | justifyContent: 'center', 9 | }, 10 | circle:{ 11 | width: 50, 12 | height: 50, 13 | borderWidth: 1, 14 | borderColor: 'white', 15 | opacity: 0.2, 16 | position: 'absolute', 17 | }, 18 | logo:{ 19 | width: 50, 20 | height: 50, 21 | borderRadius: 25, 22 | }, 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /App/Component/Style/MainViewStyle.js: -------------------------------------------------------------------------------- 1 | 2 | export default getStyle = function () { 3 | 4 | return { 5 | container:{ 6 | flex: 1, 7 | backgroundColor: 'white' 8 | }, 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /App/Component/Style/PhoneModalStyle.js: -------------------------------------------------------------------------------- 1 | import {Dimensions} from 'react-native' 2 | 3 | const winW = Dimensions.get('window').width 4 | const winH = Dimensions.get('window').height 5 | 6 | export default getStyle = function () { 7 | 8 | return{ 9 | container: { 10 | backgroundColor: 'gray', 11 | flex: 1, 12 | opacity: 0.5 13 | }, 14 | 15 | contentContainer: { 16 | backgroundColor: 'white', 17 | width: winW, 18 | height: winH, 19 | position: 'absolute', 20 | borderRadius: 20, 21 | paddingHorizontal: 10 22 | }, 23 | 24 | list: { 25 | marginRight: 20, 26 | marginTop: 20, 27 | marginBottom: 60 28 | }, 29 | 30 | itemContainer: { 31 | flexDirection: 'row', 32 | justifyContent: 'space-between', 33 | paddingVertical: 10 34 | }, 35 | 36 | itemHeader: { 37 | backgroundColor: 'white' 38 | }, 39 | 40 | text: { 41 | fontSize: 16, 42 | marginLeft: 10 43 | }, 44 | 45 | top: { 46 | flexDirection: 'row', 47 | marginTop: 20 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /App/Component/Style/SongBoxStyle.js: -------------------------------------------------------------------------------- 1 | import {Dimensions} from 'react-native' 2 | 3 | const winW = Dimensions.get('window').width 4 | const winH = Dimensions.get('window').height 5 | 6 | export default getStyle = function () { 7 | 8 | return{ 9 | container: { 10 | width: (winW-10)/3, 11 | alignItems: 'flex-start', 12 | marginTop: 10, 13 | }, 14 | 15 | imgBox: { 16 | width: (winW-10)/3-10, 17 | height: (winW-10)/3-10, 18 | }, 19 | 20 | text: { 21 | marginVertical: 10, 22 | fontSize: 12, 23 | marginRight: 10 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /App/Component/VideoComponent.js: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react' 3 | import {View} from "react-native"; 4 | import ApiUtil from '../Service/ApiUtil' 5 | import Video from 'react-native-video'; 6 | import Img from '../Img/Image' 7 | import {Button} from "react-native-elements"; 8 | 9 | class VideoComponent extends React.Component{ 10 | constructor(props) { 11 | super(props); 12 | this.state={ 13 | url: '' 14 | } 15 | } 16 | 17 | async componentWillMount(): void { 18 | try { 19 | const result = await ApiUtil.request('getVideoUrl',{id: this.props.id}) 20 | if(result.data.code === 200){ 21 | this.setState({ 22 | url: (result.data.urls[0]).url 23 | }) 24 | }else{ 25 | 26 | } 27 | } catch { 28 | 29 | } 30 | } 31 | 32 | render(){ 33 | return( 34 | 35 | {/*{*/} 36 | {/* this.state.url !== ""&&*/} 37 | {/* */} 47 | 48 | {/* */} 49 | {/*}*/} 50 | 51 | 52 | ) 53 | } 54 | 55 | } 56 | 57 | export default VideoComponent 58 | -------------------------------------------------------------------------------- /App/Config/ReactotronConfig.js: -------------------------------------------------------------------------------- 1 | import Reactotron, {asyncStorage}from 'reactotron-react-native' 2 | import {AsyncStorage} from 'react-native' 3 | import { reactotronRedux } from 'reactotron-redux' 4 | 5 | const reactotron = Reactotron 6 | .setAsyncStorageHandler(AsyncStorage) // AsyncStorage would either come from `react-native` or `@react-native-community/async-storage` depending on where you get it from 7 | .configure() // controls connection & communication settings 8 | .useReactNative() // add all built-in react native plugins 9 | .use(reactotronRedux()) 10 | .use(asyncStorage()) 11 | .connect() // let's connect! 12 | 13 | console.tron = Reactotron 14 | 15 | export default reactotron 16 | -------------------------------------------------------------------------------- /App/Config/index.js: -------------------------------------------------------------------------------- 1 | import {Platform} from 'react-native' 2 | 3 | const config = { 4 | baseURL: Platform.OS === "ios"?'http://localhost:3000':'http://10.0.2.2:3000' 5 | } 6 | 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /App/Container/AppContainer.js: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react'; 3 | import { 4 | createAppContainer, createDrawerNavigator, 5 | createStackNavigator, 6 | createSwitchNavigator, 7 | } from 'react-navigation'; 8 | 9 | import LoginView from '../View/LoginView' 10 | import PhoneLogin from '../View/PhoneLogin' 11 | import Home from '../View/Home' 12 | import SongList from '../View/SongList' 13 | import PlaySongView from '../View/PlaySongView' 14 | import LeftView from '../View/LeftView' 15 | import SongSquare from '../View/SongSquare' 16 | import VideoList from '../View/VideoList' 17 | import RankingListView from '../View/RankingListView' 18 | import {Dimensions} from "react-native"; 19 | import RadioStation from "../View/RadioStation"; 20 | import LiveView from "../View/LiveView"; 21 | 22 | const winW = Dimensions.get('window').width 23 | const winH = Dimensions.get('window').height 24 | //抽屉 25 | const leftNavigator = createDrawerNavigator( 26 | { 27 | Home: Home, 28 | }, 29 | { 30 | drawerWidth: winW*0.8, 31 | hideStatusBar: true, 32 | drawerBackgroundColor: 'white', 33 | overlayColor: 'black', 34 | contentComponent:(()=>{ 35 | return 36 | }) 37 | } 38 | ); 39 | 40 | 41 | 42 | // App 主页面 43 | const MainNavigator = createStackNavigator({ 44 | Home: { 45 | screen: leftNavigator, 46 | navigationOptions: { 47 | header: null 48 | } 49 | }, 50 | SongList:{ 51 | screen: SongList, 52 | navigationOptions: { 53 | header: null 54 | } 55 | }, 56 | PlaySongView:{ 57 | screen: PlaySongView, 58 | navigationOptions: { 59 | header: null 60 | } 61 | }, 62 | SongSquare:{ 63 | screen: SongSquare, 64 | navigationOptions: { 65 | header: null 66 | } 67 | }, 68 | VideoList:{ 69 | screen: VideoList, 70 | navigationOptions: { 71 | header: null 72 | } 73 | }, 74 | RankingListView:{ 75 | screen: RankingListView, 76 | navigationOptions: { 77 | header: null 78 | } 79 | }, 80 | RadioStation:{ 81 | screen: RadioStation, 82 | navigationOptions: { 83 | header: null 84 | } 85 | }, 86 | LiveView:{ 87 | screen: LiveView, 88 | navigationOptions: { 89 | header: null 90 | } 91 | } 92 | },{ 93 | initialRouteName: 'Home', 94 | }) 95 | 96 | 97 | //使用 createSwitchNavigator 创建分组导航 98 | const RootNavigator = createSwitchNavigator({ 99 | Main: MainNavigator, 100 | LoginView: LoginView, 101 | PhoneLogin: PhoneLogin 102 | }, { 103 | navigationOptions: { 104 | header: null, 105 | }, 106 | initialRouteName: 'LoginView', 107 | }); 108 | 109 | 110 | export default createAppContainer(RootNavigator); 111 | -------------------------------------------------------------------------------- /App/Img/Image.js: -------------------------------------------------------------------------------- 1 | 2 | const imgs = { 3 | logo: require('./logo.png'), 4 | back: require('./back.jpeg') 5 | } 6 | 7 | export default imgs 8 | -------------------------------------------------------------------------------- /App/Img/back.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Onedayago/React-Native-Music/a499df51fddbca0fa8f8b370b5b9b53ee85f8c2a/App/Img/back.jpeg -------------------------------------------------------------------------------- /App/Img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Onedayago/React-Native-Music/a499df51fddbca0fa8f8b370b5b9b53ee85f8c2a/App/Img/logo.png -------------------------------------------------------------------------------- /App/Lib/lyric-parser/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", { "modules": "umd" }], 4 | "stage-2" 5 | ], 6 | "plugins": [ 7 | "transform-runtime", 8 | "add-module-exports" 9 | ], 10 | "comments": false, 11 | "env": { 12 | "test": { 13 | "plugins": [ "istanbul" ] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /App/Lib/lyric-parser/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /App/Lib/lyric-parser/.eslintignore: -------------------------------------------------------------------------------- 1 | dist/*.js 2 | webpack.*.js 3 | didi.bridge.js -------------------------------------------------------------------------------- /App/Lib/lyric-parser/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | sourceType: 'module' 5 | }, 6 | // https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style 7 | extends: 'standard', 8 | // required to lint *.vue files 9 | plugins: [ 10 | 'html' 11 | ], 12 | // add your custom rules here 13 | 'rules': { 14 | // allow paren-less arrow functions 15 | 'arrow-parens': 0, 16 | // allow debugger during development 17 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, 18 | 'indent': 0, 19 | 'space-before-function-paren': 0, 20 | 'eol-last': 0 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /App/Lib/lyric-parser/.npmignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .DS_Store 3 | node_modules/ 4 | *.zip 5 | npm-debug.log -------------------------------------------------------------------------------- /App/Lib/lyric-parser/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 HuangYi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /App/Lib/lyric-parser/README.md: -------------------------------------------------------------------------------- 1 | # lyric-parser 2 | lyric-parser base on javascript 3 | -------------------------------------------------------------------------------- /App/Lib/lyric-parser/demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | lyric-parser test 8 | 9 | 10 |
11 |

12 |

13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 61 | 62 | -------------------------------------------------------------------------------- /App/Lib/lyric-parser/dist/lyric.js: -------------------------------------------------------------------------------- 1 | (function webpackUniversalModuleDefinition(root, factory) { 2 | if(typeof exports === 'object' && typeof module === 'object') 3 | module.exports = factory(); 4 | else if(typeof define === 'function' && define.amd) 5 | define([], factory); 6 | else if(typeof exports === 'object') 7 | exports["Lyric"] = factory(); 8 | else 9 | root["Lyric"] = factory(); 10 | })(this, function() { 11 | return /******/ (function(modules) { // webpackBootstrap 12 | /******/ // The module cache 13 | /******/ var installedModules = {}; 14 | /******/ 15 | /******/ // The require function 16 | /******/ function __webpack_require__(moduleId) { 17 | /******/ 18 | /******/ // Check if module is in cache 19 | /******/ if(installedModules[moduleId]) 20 | /******/ return installedModules[moduleId].exports; 21 | /******/ 22 | /******/ // Create a new module (and put it into the cache) 23 | /******/ var module = installedModules[moduleId] = { 24 | /******/ i: moduleId, 25 | /******/ l: false, 26 | /******/ exports: {} 27 | /******/ }; 28 | /******/ 29 | /******/ // Execute the module function 30 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 31 | /******/ 32 | /******/ // Flag the module as loaded 33 | /******/ module.l = true; 34 | /******/ 35 | /******/ // Return the exports of the module 36 | /******/ return module.exports; 37 | /******/ } 38 | /******/ 39 | /******/ 40 | /******/ // expose the modules object (__webpack_modules__) 41 | /******/ __webpack_require__.m = modules; 42 | /******/ 43 | /******/ // expose the module cache 44 | /******/ __webpack_require__.c = installedModules; 45 | /******/ 46 | /******/ // identity function for calling harmony imports with the correct context 47 | /******/ __webpack_require__.i = function(value) { return value; }; 48 | /******/ 49 | /******/ // define getter function for harmony exports 50 | /******/ __webpack_require__.d = function(exports, name, getter) { 51 | /******/ if(!__webpack_require__.o(exports, name)) { 52 | /******/ Object.defineProperty(exports, name, { 53 | /******/ configurable: false, 54 | /******/ enumerable: true, 55 | /******/ get: getter 56 | /******/ }); 57 | /******/ } 58 | /******/ }; 59 | /******/ 60 | /******/ // getDefaultExport function for compatibility with non-harmony modules 61 | /******/ __webpack_require__.n = function(module) { 62 | /******/ var getter = module && module.__esModule ? 63 | /******/ function getDefault() { return module['default']; } : 64 | /******/ function getModuleExports() { return module; }; 65 | /******/ __webpack_require__.d(getter, 'a', getter); 66 | /******/ return getter; 67 | /******/ }; 68 | /******/ 69 | /******/ // Object.prototype.hasOwnProperty.call 70 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 71 | /******/ 72 | /******/ // __webpack_public_path__ 73 | /******/ __webpack_require__.p = ""; 74 | /******/ 75 | /******/ // Load entry module and return exports 76 | /******/ return __webpack_require__(__webpack_require__.s = 20); 77 | /******/ }) 78 | /************************************************************************/ 79 | /******/ ([ 80 | /* 0 */ 81 | /***/ (function(module, exports, __webpack_require__) { 82 | 83 | // Thank's IE8 for his funny defineProperty 84 | module.exports = !__webpack_require__(3)(function(){ 85 | return Object.defineProperty({}, 'a', {get: function(){ return 7; }}).a != 7; 86 | }); 87 | 88 | /***/ }), 89 | /* 1 */ 90 | /***/ (function(module, exports) { 91 | 92 | module.exports = function(it){ 93 | return typeof it === 'object' ? it !== null : typeof it === 'function'; 94 | }; 95 | 96 | /***/ }), 97 | /* 2 */ 98 | /***/ (function(module, exports) { 99 | 100 | var core = module.exports = {version: '2.4.0'}; 101 | if(typeof __e == 'number')__e = core; // eslint-disable-line no-undef 102 | 103 | /***/ }), 104 | /* 3 */ 105 | /***/ (function(module, exports) { 106 | 107 | module.exports = function(exec){ 108 | try { 109 | return !!exec(); 110 | } catch(e){ 111 | return true; 112 | } 113 | }; 114 | 115 | /***/ }), 116 | /* 4 */ 117 | /***/ (function(module, exports) { 118 | 119 | // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 120 | var global = module.exports = typeof window != 'undefined' && window.Math == Math 121 | ? window : typeof self != 'undefined' && self.Math == Math ? self : Function('return this')(); 122 | if(typeof __g == 'number')__g = global; // eslint-disable-line no-undef 123 | 124 | /***/ }), 125 | /* 5 */ 126 | /***/ (function(module, exports, __webpack_require__) { 127 | 128 | var anObject = __webpack_require__(11) 129 | , IE8_DOM_DEFINE = __webpack_require__(16) 130 | , toPrimitive = __webpack_require__(18) 131 | , dP = Object.defineProperty; 132 | 133 | exports.f = __webpack_require__(0) ? Object.defineProperty : function defineProperty(O, P, Attributes){ 134 | anObject(O); 135 | P = toPrimitive(P, true); 136 | anObject(Attributes); 137 | if(IE8_DOM_DEFINE)try { 138 | return dP(O, P, Attributes); 139 | } catch(e){ /* empty */ } 140 | if('get' in Attributes || 'set' in Attributes)throw TypeError('Accessors not supported!'); 141 | if('value' in Attributes)O[P] = Attributes.value; 142 | return O; 143 | }; 144 | 145 | /***/ }), 146 | /* 6 */ 147 | /***/ (function(module, exports, __webpack_require__) { 148 | 149 | "use strict"; 150 | 151 | 152 | exports.__esModule = true; 153 | 154 | exports.default = function (instance, Constructor) { 155 | if (!(instance instanceof Constructor)) { 156 | throw new TypeError("Cannot call a class as a function"); 157 | } 158 | }; 159 | 160 | /***/ }), 161 | /* 7 */ 162 | /***/ (function(module, exports, __webpack_require__) { 163 | 164 | "use strict"; 165 | 166 | 167 | exports.__esModule = true; 168 | 169 | var _defineProperty = __webpack_require__(8); 170 | 171 | var _defineProperty2 = _interopRequireDefault(_defineProperty); 172 | 173 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 174 | 175 | exports.default = function () { 176 | function defineProperties(target, props) { 177 | for (var i = 0; i < props.length; i++) { 178 | var descriptor = props[i]; 179 | descriptor.enumerable = descriptor.enumerable || false; 180 | descriptor.configurable = true; 181 | if ("value" in descriptor) descriptor.writable = true; 182 | (0, _defineProperty2.default)(target, descriptor.key, descriptor); 183 | } 184 | } 185 | 186 | return function (Constructor, protoProps, staticProps) { 187 | if (protoProps) defineProperties(Constructor.prototype, protoProps); 188 | if (staticProps) defineProperties(Constructor, staticProps); 189 | return Constructor; 190 | }; 191 | }(); 192 | 193 | /***/ }), 194 | /* 8 */ 195 | /***/ (function(module, exports, __webpack_require__) { 196 | 197 | module.exports = { "default": __webpack_require__(9), __esModule: true }; 198 | 199 | /***/ }), 200 | /* 9 */ 201 | /***/ (function(module, exports, __webpack_require__) { 202 | 203 | __webpack_require__(19); 204 | var $Object = __webpack_require__(2).Object; 205 | module.exports = function defineProperty(it, key, desc){ 206 | return $Object.defineProperty(it, key, desc); 207 | }; 208 | 209 | /***/ }), 210 | /* 10 */ 211 | /***/ (function(module, exports) { 212 | 213 | module.exports = function(it){ 214 | if(typeof it != 'function')throw TypeError(it + ' is not a function!'); 215 | return it; 216 | }; 217 | 218 | /***/ }), 219 | /* 11 */ 220 | /***/ (function(module, exports, __webpack_require__) { 221 | 222 | var isObject = __webpack_require__(1); 223 | module.exports = function(it){ 224 | if(!isObject(it))throw TypeError(it + ' is not an object!'); 225 | return it; 226 | }; 227 | 228 | /***/ }), 229 | /* 12 */ 230 | /***/ (function(module, exports, __webpack_require__) { 231 | 232 | // optional / simple context binding 233 | var aFunction = __webpack_require__(10); 234 | module.exports = function(fn, that, length){ 235 | aFunction(fn); 236 | if(that === undefined)return fn; 237 | switch(length){ 238 | case 1: return function(a){ 239 | return fn.call(that, a); 240 | }; 241 | case 2: return function(a, b){ 242 | return fn.call(that, a, b); 243 | }; 244 | case 3: return function(a, b, c){ 245 | return fn.call(that, a, b, c); 246 | }; 247 | } 248 | return function(/* ...args */){ 249 | return fn.apply(that, arguments); 250 | }; 251 | }; 252 | 253 | /***/ }), 254 | /* 13 */ 255 | /***/ (function(module, exports, __webpack_require__) { 256 | 257 | var isObject = __webpack_require__(1) 258 | , document = __webpack_require__(4).document 259 | // in old IE typeof document.createElement is 'object' 260 | , is = isObject(document) && isObject(document.createElement); 261 | module.exports = function(it){ 262 | return is ? document.createElement(it) : {}; 263 | }; 264 | 265 | /***/ }), 266 | /* 14 */ 267 | /***/ (function(module, exports, __webpack_require__) { 268 | 269 | var global = __webpack_require__(4) 270 | , core = __webpack_require__(2) 271 | , ctx = __webpack_require__(12) 272 | , hide = __webpack_require__(15) 273 | , PROTOTYPE = 'prototype'; 274 | 275 | var $export = function(type, name, source){ 276 | var IS_FORCED = type & $export.F 277 | , IS_GLOBAL = type & $export.G 278 | , IS_STATIC = type & $export.S 279 | , IS_PROTO = type & $export.P 280 | , IS_BIND = type & $export.B 281 | , IS_WRAP = type & $export.W 282 | , exports = IS_GLOBAL ? core : core[name] || (core[name] = {}) 283 | , expProto = exports[PROTOTYPE] 284 | , target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE] 285 | , key, own, out; 286 | if(IS_GLOBAL)source = name; 287 | for(key in source){ 288 | // contains in native 289 | own = !IS_FORCED && target && target[key] !== undefined; 290 | if(own && key in exports)continue; 291 | // export native or passed 292 | out = own ? target[key] : source[key]; 293 | // prevent global pollution for namespaces 294 | exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] 295 | // bind timers to global for call from export context 296 | : IS_BIND && own ? ctx(out, global) 297 | // wrap global constructors for prevent change them in library 298 | : IS_WRAP && target[key] == out ? (function(C){ 299 | var F = function(a, b, c){ 300 | if(this instanceof C){ 301 | switch(arguments.length){ 302 | case 0: return new C; 303 | case 1: return new C(a); 304 | case 2: return new C(a, b); 305 | } return new C(a, b, c); 306 | } return C.apply(this, arguments); 307 | }; 308 | F[PROTOTYPE] = C[PROTOTYPE]; 309 | return F; 310 | // make static versions for prototype methods 311 | })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; 312 | // export proto methods to core.%CONSTRUCTOR%.methods.%NAME% 313 | if(IS_PROTO){ 314 | (exports.virtual || (exports.virtual = {}))[key] = out; 315 | // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME% 316 | if(type & $export.R && expProto && !expProto[key])hide(expProto, key, out); 317 | } 318 | } 319 | }; 320 | // type bitmap 321 | $export.F = 1; // forced 322 | $export.G = 2; // global 323 | $export.S = 4; // static 324 | $export.P = 8; // proto 325 | $export.B = 16; // bind 326 | $export.W = 32; // wrap 327 | $export.U = 64; // safe 328 | $export.R = 128; // real proto method for `library` 329 | module.exports = $export; 330 | 331 | /***/ }), 332 | /* 15 */ 333 | /***/ (function(module, exports, __webpack_require__) { 334 | 335 | var dP = __webpack_require__(5) 336 | , createDesc = __webpack_require__(17); 337 | module.exports = __webpack_require__(0) ? function(object, key, value){ 338 | return dP.f(object, key, createDesc(1, value)); 339 | } : function(object, key, value){ 340 | object[key] = value; 341 | return object; 342 | }; 343 | 344 | /***/ }), 345 | /* 16 */ 346 | /***/ (function(module, exports, __webpack_require__) { 347 | 348 | module.exports = !__webpack_require__(0) && !__webpack_require__(3)(function(){ 349 | return Object.defineProperty(__webpack_require__(13)('div'), 'a', {get: function(){ return 7; }}).a != 7; 350 | }); 351 | 352 | /***/ }), 353 | /* 17 */ 354 | /***/ (function(module, exports) { 355 | 356 | module.exports = function(bitmap, value){ 357 | return { 358 | enumerable : !(bitmap & 1), 359 | configurable: !(bitmap & 2), 360 | writable : !(bitmap & 4), 361 | value : value 362 | }; 363 | }; 364 | 365 | /***/ }), 366 | /* 18 */ 367 | /***/ (function(module, exports, __webpack_require__) { 368 | 369 | // 7.1.1 ToPrimitive(input [, PreferredType]) 370 | var isObject = __webpack_require__(1); 371 | // instead of the ES6 spec version, we didn't implement @@toPrimitive case 372 | // and the second argument - flag - preferred type is a string 373 | module.exports = function(it, S){ 374 | if(!isObject(it))return it; 375 | var fn, val; 376 | if(S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it)))return val; 377 | if(typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it)))return val; 378 | if(!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it)))return val; 379 | throw TypeError("Can't convert object to primitive value"); 380 | }; 381 | 382 | /***/ }), 383 | /* 19 */ 384 | /***/ (function(module, exports, __webpack_require__) { 385 | 386 | var $export = __webpack_require__(14); 387 | // 19.1.2.4 / 15.2.3.6 Object.defineProperty(O, P, Attributes) 388 | $export($export.S + $export.F * !__webpack_require__(0), 'Object', {defineProperty: __webpack_require__(5).f}); 389 | 390 | /***/ }), 391 | /* 20 */ 392 | /***/ (function(module, exports, __webpack_require__) { 393 | 394 | var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) { 395 | if (true) { 396 | !(__WEBPACK_AMD_DEFINE_ARRAY__ = [module, exports, __webpack_require__(6), __webpack_require__(7)], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), 397 | __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? 398 | (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), 399 | __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); 400 | } else if (typeof exports !== "undefined") { 401 | factory(module, exports, require('babel-runtime/helpers/classCallCheck'), require('babel-runtime/helpers/createClass')); 402 | } else { 403 | var mod = { 404 | exports: {} 405 | }; 406 | factory(mod, mod.exports, global.classCallCheck, global.createClass); 407 | global.index = mod.exports; 408 | } 409 | })(this, function (module, exports, _classCallCheck2, _createClass2) { 410 | 'use strict'; 411 | 412 | Object.defineProperty(exports, "__esModule", { 413 | value: true 414 | }); 415 | 416 | var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); 417 | 418 | var _createClass3 = _interopRequireDefault(_createClass2); 419 | 420 | function _interopRequireDefault(obj) { 421 | return obj && obj.__esModule ? obj : { 422 | default: obj 423 | }; 424 | } 425 | 426 | var timeExp = /\[(\d{2,}):(\d{2})(?:\.(\d{2,3}))?]/g; 427 | 428 | var STATE_PAUSE = 0; 429 | var STATE_PLAYING = 1; 430 | 431 | var tagRegMap = { 432 | title: 'ti', 433 | artist: 'ar', 434 | album: 'al', 435 | offset: 'offset', 436 | by: 'by' 437 | }; 438 | 439 | function noop() {} 440 | 441 | var Lyric = function () { 442 | function Lyric(lrc) { 443 | var hanlder = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : noop; 444 | (0, _classCallCheck3.default)(this, Lyric); 445 | 446 | this.lrc = lrc; 447 | this.tags = {}; 448 | this.lines = []; 449 | this.handler = hanlder; 450 | this.state = STATE_PAUSE; 451 | this.curLine = 0; 452 | 453 | this._init(); 454 | } 455 | 456 | (0, _createClass3.default)(Lyric, [{ 457 | key: '_init', 458 | value: function _init() { 459 | this._initTag(); 460 | 461 | this._initLines(); 462 | } 463 | }, { 464 | key: '_initTag', 465 | value: function _initTag() { 466 | for (var tag in tagRegMap) { 467 | var matches = this.lrc.match(new RegExp('\\[' + tagRegMap[tag] + ':([^\\]]*)]', 'i')); 468 | this.tags[tag] = matches && matches[1] || ''; 469 | } 470 | } 471 | }, { 472 | key: '_initLines', 473 | value: function _initLines() { 474 | var lines = this.lrc.split('\n'); 475 | for (var i = 0; i < lines.length; i++) { 476 | var line = lines[i]; 477 | var result = timeExp.exec(line); 478 | if (result) { 479 | var txt = line.replace(timeExp, '').trim(); 480 | if (txt) { 481 | this.lines.push({ 482 | time: result[1] * 60 * 1000 + result[2] * 1000 + (result[3] || 0) * 10, 483 | txt: txt 484 | }); 485 | } 486 | } 487 | } 488 | 489 | this.lines.sort(function (a, b) { 490 | return a.time - b.time; 491 | }); 492 | } 493 | }, { 494 | key: '_findCurNum', 495 | value: function _findCurNum(time) { 496 | for (var i = 0; i < this.lines.length; i++) { 497 | if (time <= this.lines[i].time) { 498 | return i; 499 | } 500 | } 501 | return this.lines.length - 1; 502 | } 503 | }, { 504 | key: '_callHandler', 505 | value: function _callHandler(i) { 506 | if (i < 0) { 507 | return; 508 | } 509 | this.handler({ 510 | txt: this.lines[i].txt, 511 | lineNum: i 512 | }); 513 | } 514 | }, { 515 | key: '_playRest', 516 | value: function _playRest() { 517 | var _this = this; 518 | 519 | var line = this.lines[this.curNum]; 520 | var delay = line.time - (+new Date() - this.startStamp); 521 | 522 | this.timer = setTimeout(function () { 523 | _this._callHandler(_this.curNum++); 524 | if (_this.curNum < _this.lines.length && _this.state === STATE_PLAYING) { 525 | _this._playRest(); 526 | } 527 | }, delay); 528 | } 529 | }, { 530 | key: 'play', 531 | value: function play() { 532 | var startTime = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; 533 | var skipLast = arguments[1]; 534 | 535 | if (!this.lines.length) { 536 | return; 537 | } 538 | this.state = STATE_PLAYING; 539 | 540 | this.curNum = this._findCurNum(startTime); 541 | this.startStamp = +new Date() - startTime; 542 | 543 | if (!skipLast) { 544 | this._callHandler(this.curNum - 1); 545 | } 546 | 547 | if (this.curNum < this.lines.length) { 548 | clearTimeout(this.timer); 549 | this._playRest(); 550 | } 551 | } 552 | }, { 553 | key: 'togglePlay', 554 | value: function togglePlay() { 555 | var now = +new Date(); 556 | if (this.state === STATE_PLAYING) { 557 | this.stop(); 558 | this.pauseStamp = now; 559 | } else { 560 | this.state = STATE_PLAYING; 561 | this.play((this.pauseStamp || now) - (this.startStamp || now), true); 562 | this.pauseStamp = 0; 563 | } 564 | } 565 | }, { 566 | key: 'stop', 567 | value: function stop() { 568 | this.state = STATE_PAUSE; 569 | clearTimeout(this.timer); 570 | } 571 | }, { 572 | key: 'seek', 573 | value: function seek(offset) { 574 | this.play(offset); 575 | } 576 | }]); 577 | return Lyric; 578 | }(); 579 | 580 | exports.default = Lyric; 581 | module.exports = exports['default']; 582 | }); 583 | 584 | /***/ }) 585 | /******/ ]); 586 | }); -------------------------------------------------------------------------------- /App/Lib/lyric-parser/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lyric-parser", 3 | "version": "1.0.1", 4 | "description": "lyric-parser base on javascript", 5 | "main": "dist/lyric.js", 6 | "scripts": { 7 | "dev": "webpack-dev-server --config build/webpack.dev.js --port 9090 --inline --hot --content-base ./demo --host 0.0.0.0", 8 | "build": "webpack --config build/webpack.prod.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/ustbhuangyi/lyric-parser.git" 13 | }, 14 | "keywords": [ 15 | "lyric", 16 | "parser" 17 | ], 18 | "author": "ustbhuangyi", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/ustbhuangyi/lyric-parser/issues" 22 | }, 23 | "homepage": "https://github.com/ustbhuangyi/lyric-parser#readme", 24 | "devDependencies": { 25 | "babel-core": "^6.22.1", 26 | "babel-eslint": "^7.1.1", 27 | "babel-loader": "^6.2.10", 28 | "babel-plugin-add-module-exports": "^0.2.1", 29 | "babel-plugin-transform-runtime": "^6.22.0", 30 | "babel-preset-es2015": "^6.22.0", 31 | "babel-preset-stage-2": "^6.22.0", 32 | "babel-register": "^6.22.0", 33 | "eslint": "^3.14.1", 34 | "eslint-friendly-formatter": "^2.0.7", 35 | "eslint-loader": "^1.6.1", 36 | "eslint-plugin-html": "^2.0.0", 37 | "eslint-config-standard": "^6.2.1", 38 | "eslint-plugin-promise": "^3.4.0", 39 | "eslint-plugin-standard": "^2.0.1", 40 | "webpack": "^2.2.1", 41 | "webpack-dev-server": "^1.14.1", 42 | "html-webpack-plugin": "^2.28.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /App/Lib/lyric-parser/src/index.js: -------------------------------------------------------------------------------- 1 | const timeExp = /\[(\d{2,}):(\d{2})(?:\.(\d{2,3}))?]/g 2 | 3 | const STATE_PAUSE = 0 4 | const STATE_PLAYING = 1 5 | 6 | const tagRegMap = { 7 | title: 'ti', 8 | artist: 'ar', 9 | album: 'al', 10 | offset: 'offset', 11 | by: 'by' 12 | } 13 | 14 | function noop() { 15 | } 16 | 17 | export default class Lyric { 18 | constructor(lrc, hanlder = noop) { 19 | this.lrc = lrc 20 | this.tags = {} 21 | this.lines = [] 22 | this.handler = hanlder 23 | this.state = STATE_PAUSE 24 | this.curLine = 0 25 | 26 | this._init() 27 | } 28 | 29 | _init() { 30 | this._initTag() 31 | 32 | this._initLines() 33 | } 34 | 35 | _initTag() { 36 | for (let tag in tagRegMap) { 37 | const matches = this.lrc.match(new RegExp(`\\[${tagRegMap[tag]}:([^\\]]*)]`, 'i')) 38 | this.tags[tag] = matches && matches[1] || '' 39 | } 40 | } 41 | 42 | _initLines() { 43 | const lines = this.lrc.split('\n') 44 | for (let i = 0; i < lines.length; i++) { 45 | const line = lines[i] 46 | let result = timeExp.exec(line) 47 | if (result) { 48 | const txt = line.replace(timeExp, '').trim() 49 | if (txt) { 50 | this.lines.push({ 51 | time: result[1] * 60 * 1000 + result[2] * 1000 + (result[3] || 0) * 10, 52 | txt 53 | }) 54 | } 55 | } 56 | } 57 | 58 | this.lines.sort((a, b) => { 59 | return a.time - b.time 60 | }) 61 | } 62 | 63 | _findCurNum(time) { 64 | for (let i = 0; i < this.lines.length; i++) { 65 | if (time <= this.lines[i].time) { 66 | return i 67 | } 68 | } 69 | return this.lines.length - 1 70 | } 71 | 72 | _callHandler(i) { 73 | if (i < 0) { 74 | return 75 | } 76 | this.handler({ 77 | txt: this.lines[i].txt, 78 | lineNum: i 79 | }) 80 | } 81 | 82 | _playRest() { 83 | let line = this.lines[this.curNum] 84 | let delay = line.time - (+new Date() - this.startStamp) 85 | 86 | this.timer = setTimeout(() => { 87 | this._callHandler(this.curNum++) 88 | if (this.curNum < this.lines.length && this.state === STATE_PLAYING) { 89 | this._playRest() 90 | } 91 | }, delay) 92 | } 93 | 94 | play(startTime = 0, skipLast) { 95 | if (!this.lines.length) { 96 | return 97 | } 98 | this.state = STATE_PLAYING 99 | 100 | this.curNum = this._findCurNum(startTime) 101 | this.startStamp = +new Date() - startTime 102 | 103 | if (!skipLast) { 104 | this._callHandler(this.curNum - 1) 105 | } 106 | 107 | if (this.curNum < this.lines.length) { 108 | clearTimeout(this.timer) 109 | this._playRest() 110 | } 111 | } 112 | 113 | togglePlay() { 114 | var now = +new Date() 115 | if (this.state === STATE_PLAYING) { 116 | this.stop() 117 | this.pauseStamp = now 118 | } else { 119 | this.state = STATE_PLAYING 120 | this.play((this.pauseStamp || now) - (this.startStamp || now), true) 121 | this.pauseStamp = 0 122 | } 123 | } 124 | 125 | stop() { 126 | this.state = STATE_PAUSE 127 | clearTimeout(this.timer) 128 | } 129 | 130 | seek(offset) { 131 | this.play(offset) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /App/Service/ApiUtil.js: -------------------------------------------------------------------------------- 1 | import Api from './index' 2 | 3 | class ApiUtil { 4 | static api(){ 5 | if(!ApiUtil.serviceApi){ 6 | ApiUtil.serviceApi = Api.create() 7 | } 8 | return ApiUtil.serviceApi; 9 | } 10 | 11 | 12 | static async request(route,params){ 13 | 14 | const Api = ApiUtil.api(); 15 | 16 | 17 | if(!params) params=[]; 18 | return await eval("Api."+route)(params) 19 | } 20 | 21 | } 22 | 23 | export default ApiUtil 24 | -------------------------------------------------------------------------------- /App/Service/action.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Onedayago/React-Native-Music/a499df51fddbca0fa8f8b370b5b9b53ee85f8c2a/App/Service/action.js -------------------------------------------------------------------------------- /App/Service/index.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import config from '../Config' 3 | 4 | 5 | const create = ()=>{ 6 | 7 | const instance = axios.create({ 8 | baseURL: config.baseURL, 9 | timeout: 3000, 10 | headers: { 11 | 'Cache-Control': 'no-cache' 12 | } 13 | }); 14 | 15 | //请求拦截处理 16 | instance.interceptors.request.use(async function (config) { 17 | // 在发送请求之前做些什么 18 | return config; 19 | }, function (error) { 20 | // 对请求错误做些什么 21 | return Promise.reject(error); 22 | }); 23 | 24 | //返回拦截处理 25 | instance.interceptors.response.use(function (response) { 26 | // 对响应数据做点什么 27 | return response; 28 | }, function (error) { 29 | // 对响应错误做点什么 30 | return Promise.reject(error); 31 | }); 32 | 33 | 34 | //检测手机号是否注册 35 | const checkPhone =({phone})=> instance.get(`/cellphone/existence/check?phone=${phone}`); 36 | 37 | //手机号密码登录 38 | const login =({phone, password})=> instance.get(`/login/cellphone?phone=${phone}&password=${password}`); 39 | 40 | //轮播图 41 | const getBanner =({type})=> instance.get(`/banner?type=${type}`); 42 | 43 | //每日推荐 44 | const getCommend=()=>instance.get('/recommend/songs'); 45 | 46 | //获取音乐 url 47 | const getUrl=({id})=>instance.get(`/song/url?id=${id}`); 48 | 49 | //获取音乐歌词 50 | const getLyric=({id})=>instance.get(`/lyric?id=${id}`); 51 | 52 | //获取歌单分类 53 | const getCatList=()=>instance.get('/playlist/catlist?uid=107380882'); 54 | 55 | //根据歌单分类获取歌单列表 56 | const getPlayList=({before, cat, limit})=>instance.get(`/top/playlist/highquality?before=${before}&limit=${limit}&cat=${cat}`) 57 | 58 | //获取视频分类 59 | const getVideoTag=()=>instance.get('/video/group/list') 60 | 61 | //根据视频分类获取视频 62 | const getVideoList=({id})=>instance.get(`/video/group?id=${id}`) 63 | 64 | //获取视频播放地址 65 | const getVideoUrl=({id})=>instance.get(`/video/url?id=${id}`) 66 | 67 | return{ 68 | checkPhone, 69 | login, 70 | getBanner, 71 | getCommend, 72 | getUrl, 73 | getLyric, 74 | getCatList, 75 | getPlayList, 76 | getVideoTag, 77 | getVideoList, 78 | getVideoUrl 79 | } 80 | } 81 | 82 | export default { 83 | create 84 | }; 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /App/Store/actionCreators.js: -------------------------------------------------------------------------------- 1 | import actionTypes from './actionTypes' 2 | 3 | export function CheckPhone(data){ 4 | return { 5 | type: actionTypes.CheckPhone, 6 | data: data 7 | } 8 | } 9 | 10 | export function Login(data){ 11 | return { 12 | type: actionTypes.Login, 13 | data: data 14 | } 15 | } 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /App/Store/actionTypes.js: -------------------------------------------------------------------------------- 1 | export default { 2 | CheckPhone: 'CheckPhone', 3 | Login: 'Login' 4 | } 5 | -------------------------------------------------------------------------------- /App/Store/index.js: -------------------------------------------------------------------------------- 1 | import {applyMiddleware, createStore, compose} from 'redux' 2 | import { persistStore, persistReducer } from 'redux-persist' 3 | import { combineReducers } from 'redux' 4 | import thunkMiddleware from 'redux-thunk' 5 | import Reactotron from '../Config/ReactotronConfig' 6 | import UserReducer from './reducer/userReducer' 7 | import ThemeReducer from './reducer/themeReducer' 8 | import AsyncStorage from '@react-native-community/async-storage'; 9 | import immutableTransform from "redux-persist-transform-immutable"; 10 | 11 | 12 | const persistConfig = { 13 | transforms: [ 14 | immutableTransform() 15 | ], 16 | key: 'root', 17 | storage: AsyncStorage, 18 | } 19 | 20 | const reducer = combineReducers ({ 21 | UserReducer: UserReducer, 22 | ThemeReducer: ThemeReducer, 23 | }) 24 | 25 | const persistedReducer = persistReducer(persistConfig, reducer) 26 | 27 | 28 | export default function configureStore() { 29 | const enhancers = compose( 30 | applyMiddleware(thunkMiddleware), Reactotron.createEnhancer() 31 | ); 32 | const store = createStore(persistedReducer, enhancers) 33 | 34 | let persistor = persistStore(store) 35 | 36 | return {store, persistor} 37 | } 38 | -------------------------------------------------------------------------------- /App/Store/reducer/themeReducer.js: -------------------------------------------------------------------------------- 1 | import actionTypes from '../actionTypes' 2 | import {fromJS} from 'immutable' 3 | 4 | const defaultState = fromJS({ // 将对象转成immutable对象 5 | theme:'', 6 | }) 7 | 8 | export default (state = defaultState, action) => { 9 | return state 10 | } 11 | -------------------------------------------------------------------------------- /App/Store/reducer/userReducer.js: -------------------------------------------------------------------------------- 1 | import actionTypes from '../actionTypes' 2 | import {fromJS} from 'immutable' 3 | 4 | const defaultState = fromJS({ // 将对象转成immutable对象 5 | user: {} 6 | }) 7 | 8 | export default (state = defaultState, action) => { 9 | switch (action.type) { 10 | case actionTypes.Login: 11 | return state.merge({ 12 | user: action.data 13 | }) 14 | 15 | } 16 | return state 17 | } 18 | -------------------------------------------------------------------------------- /App/Util/StatusBar.js: -------------------------------------------------------------------------------- 1 | import { NativeModules, Platform } from 'react-native'; 2 | 3 | const { StatusBarManager } = NativeModules; 4 | 5 | function getIosStatusBarHeight() { 6 | return new Promise((resolve => { 7 | StatusBarManager.getHeight((height)=>{ 8 | resolve(height) 9 | }) 10 | })) 11 | } 12 | 13 | 14 | 15 | //获取状态栏高度 16 | export async function getStatusBarHeight() { 17 | if(Platform.OS === 'ios'){ 18 | return await getIosStatusBarHeight(); 19 | }else{ 20 | return {'height': 0} 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /App/Util/Tool.js: -------------------------------------------------------------------------------- 1 | 2 | export function checkPhone (phone) { 3 | let pattern = /^1[34578]\d{9}$/; 4 | if (pattern.test(phone)) { 5 | return true; 6 | } 7 | return false; 8 | } 9 | 10 | export function checkEmpty (str) { 11 | 12 | if(str !== ""){ 13 | return true 14 | } 15 | return false; 16 | } 17 | 18 | /* 19 | * 数字秒转时间格式 20 | * @param sec 数字秒 21 | * return 时间格式字符串 22 | * */ 23 | export function secondsFormat(sec) { 24 | let hour = Math.floor(sec / 3600); 25 | let minute = Math.floor((sec - hour * 3600) / 60); 26 | let second = Math.floor(sec - hour * 3600 - minute * 60); 27 | if (hour < 10) { 28 | hour = "0" + hour; 29 | } 30 | if (minute < 10) { 31 | minute = "0" + minute; 32 | } 33 | if (second < 10) { 34 | second = "0" + second; 35 | } 36 | 37 | if(hour !== "00"){ 38 | return hour + ":" + minute + ":" + second; 39 | }else{ 40 | return minute + ":" + second; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /App/View/FindView.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Text, View, Image, Dimensions, TouchableOpacity} from "react-native"; 3 | import Swiper from 'react-native-swiper' 4 | import getStyle from './Style/FindViewStyle' 5 | import Img from "../Img/Image"; 6 | import ApiUtil from '../Service/ApiUtil' 7 | import {Icon} from "react-native-elements"; 8 | import {withNavigation} from 'react-navigation' 9 | 10 | const winW = Dimensions.get('window').width 11 | const winH = Dimensions.get('window').height 12 | 13 | let Styles = {} 14 | class FindView extends React.Component{ 15 | constructor(props) { 16 | super(props); 17 | this.state={ 18 | banners: [] 19 | } 20 | } 21 | 22 | async componentWillMount(): void { 23 | try{ 24 | const result = await ApiUtil.request('getBanner', {type: '1'}) 25 | if(result.data.code === 200){ 26 | this.setState({ 27 | banners: result.data.banners 28 | }) 29 | }else{ 30 | 31 | } 32 | }catch { 33 | 34 | } 35 | 36 | } 37 | 38 | render(){ 39 | Styles = getStyle() 40 | const {navigate} = this.props.navigation 41 | return( 42 | 43 | 44 | {/*轮播图*/} 45 | { 46 | this.state.banners.length !==0 && 47 | 48 | 51 | { 52 | this.state.banners.map((item,index)=>{ 53 | return( 54 | 55 | ) 56 | }) 57 | } 58 | 59 | 60 | } 61 | 62 | {/* 项目列表*/} 63 | 64 | 65 | 66 | { 67 | navigate('SongList') 68 | }}> 69 | 75 | 每日推荐 76 | 77 | { 78 | navigate('SongSquare') 79 | }}> 80 | 86 | 歌单 87 | 88 | { 89 | navigate('RankingListView') 90 | }}> 91 | 97 | 排行榜 98 | 99 | { 100 | navigate('RadioStation') 101 | }}> 102 | 108 | 电台 109 | 110 | { 111 | navigate('LiveView') 112 | }}> 113 | 119 | 直播 120 | 121 | 122 | 123 | ) 124 | } 125 | } 126 | 127 | export default withNavigation(FindView) 128 | -------------------------------------------------------------------------------- /App/View/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {View} from "react-native"; 3 | import {Dimensions} from 'react-native' 4 | import getStyle from './Style/HomeStyle' 5 | import FindView from '../View/FindView' 6 | import VideoList from './VideoList' 7 | import ScrollableTabView from "react-native-scrollable-tab-view"; 8 | import CustomTabBar from '../Component/HomeCustomTabBar' 9 | import MainView from '../Component/MainView' 10 | import YunCun from "./YunCun"; 11 | import MyView from "./MyView"; 12 | 13 | const winW = Dimensions.get('window').width 14 | const winH = Dimensions.get('window').height 15 | 16 | let Styles = {} 17 | class Home extends React.Component{ 18 | constructor(props) { 19 | super(props); 20 | this.state={ 21 | 22 | } 23 | } 24 | 25 | componentDidMount(): void { 26 | 27 | } 28 | 29 | render(){ 30 | Styles = getStyle() 31 | return( 32 | 33 | { 36 | return this.props.navigation.openDrawer()} 42 | rightClick={()=>{}} 43 | /> 44 | } 45 | }> 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | ) 61 | } 62 | 63 | 64 | } 65 | 66 | export default Home 67 | -------------------------------------------------------------------------------- /App/View/LeftView.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {View, Image, Text, ScrollView} from "react-native"; 3 | import Img from '../Img/Image' 4 | import getStyle from './Style/LeftViewStyle' 5 | import {Divider, Icon, ListItem} from "react-native-elements"; 6 | 7 | 8 | let Styles = {} 9 | 10 | class LeftView extends React.Component{ 11 | constructor(props) { 12 | super(props); 13 | } 14 | 15 | render(){ 16 | Styles = getStyle() 17 | return( 18 | 19 | 20 | 21 | 22 | 27 | 28 | 38 | } 39 | /> 40 | 41 | 50 | } 51 | /> 52 | 53 | 62 | } 63 | /> 64 | 65 | 74 | } 75 | /> 76 | 77 | 86 | } 87 | /> 88 | 89 | 98 | } 99 | bottomDivider 100 | /> 101 | 102 | 111 | } 112 | /> 113 | 114 | 123 | } 124 | /> 125 | 126 | 135 | } 136 | /> 137 | 138 | 147 | } 148 | /> 149 | 150 | 159 | } 160 | /> 161 | 162 | 171 | } 172 | /> 173 | 174 | 183 | } 184 | /> 185 | 186 | 195 | } 196 | /> 197 | 198 | 199 | 200 | 201 | 202 | 203 | 208 | 夜间模式 209 | 210 | 211 | 216 | 设置 217 | 218 | 219 | 224 | 退出 225 | 226 | 227 | 228 | ) 229 | } 230 | 231 | } 232 | 233 | export default LeftView 234 | -------------------------------------------------------------------------------- /App/View/LiveView.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {View} from "react-native"; 3 | import {Header, Icon} from "react-native-elements"; 4 | import getStyle from './Style/LiveStyle' 5 | 6 | 7 | 8 | let Styles = {} 9 | class LiveView extends React.Component{ 10 | constructor(props) { 11 | super(props); 12 | this.state={ 13 | 14 | } 15 | } 16 | 17 | async componentWillMount(): void { 18 | try { 19 | 20 | } catch { 21 | 22 | } 23 | } 24 | 25 | render(){ 26 | Styles = getStyle() 27 | return( 28 | 29 |
{ 38 | this.props.navigation.goBack() 39 | }} 40 | /> 41 | } 42 | centerComponent={{ text: '直播', style: { color: 'black', fontSize: 16 } }} 43 | /> 44 | 45 | 46 | ) 47 | } 48 | 49 | } 50 | 51 | export default LiveView 52 | -------------------------------------------------------------------------------- /App/View/LoginView.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import MainView from '../Component/MainView' 3 | import getStyle from './Style/LoginViewStyle' 4 | import {View} from 'react-native' 5 | import { Button} from 'react-native-elements'; 6 | import Logo from '../Component/Logo' 7 | 8 | let Styles = {} 9 | 10 | class LoginView extends React.Component{ 11 | constructor(props) { 12 | super(props); 13 | this.state={ 14 | 15 | } 16 | } 17 | 18 | componentDidMount(): void { 19 | 20 | } 21 | 22 | render(){ 23 | Styles = getStyle() 24 | 25 | const {navigate} = this.props.navigation 26 | 27 | return( 28 | 29 | 30 | 31 | 32 | 33 | 34 |