Authored by chenl

Merge branch '5.4' of http://git.yoho.cn/mobile/YH_RNComponent into local

@@ -10,6 +10,9 @@ import Classify from './classify/Classify'; @@ -10,6 +10,9 @@ import Classify from './classify/Classify';
10 import Seckill from './seckill/Seckill'; 10 import Seckill from './seckill/Seckill';
11 import Guang from './guang/Guang'; 11 import Guang from './guang/Guang';
12 12
  13 +import Message from './message/Message';
  14 +import NewArrival from './newArrival/NewArrival';
  15 +
13 // import Search from './search/Search'; 16 // import Search from './search/Search';
14 17
15 export default function native(platform) { 18 export default function native(platform) {
@@ -19,7 +22,8 @@ export default function native(platform) { @@ -19,7 +22,8 @@ export default function native(platform) {
19 Seckill(platform); 22 Seckill(platform);
20 Guang(platform); 23 Guang(platform);
21 Plustar(platform); 24 Plustar(platform);
22 - 25 + Message(platform)
  26 + NewArrival(NewArrival)
23 27
24 if (Platform.OS === 'ios') { 28 if (Platform.OS === 'ios') {
25 Community(platform); 29 Community(platform);
@@ -123,7 +123,6 @@ export default function brandStoreReducer(state=initialState, action) { @@ -123,7 +123,6 @@ export default function brandStoreReducer(state=initialState, action) {
123 .set('is_show_shop_name', is_show_shop_name) 123 .set('is_show_shop_name', is_show_shop_name)
124 .set('shop_domain', shop_domain) 124 .set('shop_domain', shop_domain)
125 .set('shops_id', shops_id) 125 .set('shops_id', shops_id)
126 - .set('is_addFav',false);  
127 let nextState = state.set('info', info); 126 let nextState = state.set('info', info);
128 return nextState; 127 return nextState;
129 } 128 }
@@ -41,21 +41,17 @@ export default class BannerReourceList extends React.Component { @@ -41,21 +41,17 @@ export default class BannerReourceList extends React.Component {
41 <TouchableOpacity activeOpacity={0.5} onPress={() => { 41 <TouchableOpacity activeOpacity={0.5} onPress={() => {
42 this.props.onPressSlideItem && this.props.onPressSlideItem(rowData, rowID); 42 this.props.onPressSlideItem && this.props.onPressSlideItem(rowData, rowID);
43 }}> 43 }}>
44 - <View style={styles.row}>  
45 - <View style={styles.thumbnailV}>  
46 - <Image  
47 - source={{uri: newSrc}}  
48 - style={styles.thumbnail}  
49 - resizeMode={'contain'}  
50 - >  
51 - <View style={styles.textV}>  
52 - <Text style={styles.text} numberOfLines={1}>  
53 - {rowData.name}  
54 - </Text>  
55 - </View>  
56 - </Image>  
57 -  
58 - </View> 44 + <View style={styles.thumbnailV}>
  45 + <Image
  46 + source={{uri: newSrc}}
  47 + style={styles.thumbnail}
  48 + resizeMode={'contain'}
  49 + ></Image>
  50 + <View style={styles.textV}>
  51 + <Text style={styles.text} numberOfLines={1}>
  52 + {rowData.name}
  53 + </Text>
  54 + </View>
59 </View> 55 </View>
60 </TouchableOpacity> 56 </TouchableOpacity>
61 ); 57 );
@@ -113,14 +109,9 @@ let styles = StyleSheet.create({ @@ -113,14 +109,9 @@ let styles = StyleSheet.create({
113 width:Dimensions.get('window').width, 109 width:Dimensions.get('window').width,
114 backgroundColor: 'white', 110 backgroundColor: 'white',
115 }, 111 },
116 - row: {  
117 - marginLeft: 10,  
118 - width: itemWidth,  
119 - height: itemWidth,  
120 - backgroundColor: 'white',  
121 - },  
122 textV: { 112 textV: {
123 - marginTop: itemHeight - 20, 113 + position: 'absolute',
  114 + top: itemHeight - 20,
124 width: itemWidth, 115 width: itemWidth,
125 alignItems: 'center', 116 alignItems: 'center',
126 height: 20, 117 height: 20,
@@ -140,6 +131,7 @@ let styles = StyleSheet.create({ @@ -140,6 +131,7 @@ let styles = StyleSheet.create({
140 fontWeight: 'bold', 131 fontWeight: 'bold',
141 }, 132 },
142 thumbnailV: { 133 thumbnailV: {
  134 + marginLeft: 10,
143 width: itemWidth, 135 width: itemWidth,
144 height: itemHeight, 136 height: itemHeight,
145 backgroundColor: 'white', 137 backgroundColor: 'white',
  1 +'use strict';
  2 +
  3 +import React from 'react';
  4 +import ReactNative, {
  5 + AppRegistry,
  6 + Platform,
  7 +} from 'react-native';
  8 +
  9 +import {
  10 + Provider,
  11 + connect
  12 +} from 'react-redux';
  13 +
  14 +import configureStore from './store/configureStore';
  15 +import {Record, List, Map} from 'immutable';
  16 +
  17 +import appInitialState from './reducers/app/appInitialState';
  18 +import messageInitialState from './reducers/message/messageInitialState';
  19 +
  20 +import MessageContainer from './containers/MessageContainer';
  21 +
  22 +import {
  23 + setPlatform,
  24 + setHost,
  25 + setServiceHost,
  26 +} from './reducers/app/appActions';
  27 +
  28 +
  29 +function getInitialState() {
  30 + const _initState = {
  31 + app: (new appInitialState()),
  32 + message: (new messageInitialState()),
  33 + };
  34 + return _initState;
  35 +}
  36 +
  37 +export default function native(platform) {
  38 +
  39 + let YH_Message = React.createClass({
  40 +
  41 + render() {
  42 + const store = configureStore(getInitialState());
  43 + store.dispatch(setPlatform(platform));
  44 + store.dispatch(setHost(this.props.host));
  45 + store.dispatch(setServiceHost(this.props.serviceHost));
  46 +
  47 + return (
  48 + <Provider store={store}>
  49 + <MessageContainer />
  50 + </Provider>
  51 + );
  52 + }
  53 + });
  54 +
  55 + AppRegistry.registerComponent('YH_Message', () => YH_Message);
  56 +}
  1 +'use strict';
  2 +
  3 +import React, {Component} from 'react';
  4 +import ReactNative, {
  5 + View,
  6 + Text,
  7 + Image,
  8 + ListView,
  9 + StyleSheet,
  10 + Dimensions,
  11 + TouchableOpacity,
  12 + InteractionManager,
  13 + Platform,
  14 +} from 'react-native';
  15 +
  16 +
  17 +
  18 +export default class Message extends Component {
  19 +
  20 + constructor(props) {
  21 + super(props);
  22 +
  23 + this._renderRow = this._renderRow.bind(this);
  24 +
  25 + this.dataSource = new ListView.DataSource({
  26 + rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
  27 + });
  28 + }
  29 +
  30 + componentDidMount() {
  31 +
  32 + }
  33 +
  34 + _renderRow(rowData: object, sectionID: number, rowID: number) {
  35 +
  36 + return (
  37 + <TouchableOpacity
  38 + onPress={() => {
  39 + console.log(rowID)
  40 + }}
  41 + >
  42 + <View style={styles.rowContainer}>
  43 + <Text>{rowData.get('title')}</Text>
  44 + <Text>{rowData.get('detail')}</Text>
  45 + </View>
  46 + </TouchableOpacity>
  47 + );
  48 + }
  49 +
  50 +
  51 + render() {
  52 +
  53 + let dataSource = this.props.data.dataSource.toArray();
  54 +
  55 + return (
  56 + <View style={styles.container}>
  57 + <ListView
  58 + contentContainerStyle={styles.contentContainer}
  59 + dataSource={this.dataSource.cloneWithRows(dataSource)}
  60 + renderRow={this._renderRow}
  61 + enableEmptySections={true}
  62 + />
  63 + </View>
  64 + );
  65 + }
  66 +}
  67 +
  68 +let {width, height} = Dimensions.get('window');
  69 +
  70 +let styles = StyleSheet.create({
  71 + container: {
  72 + flex: 1,
  73 + backgroundColor: '#f0f0f0',
  74 + },
  75 + contentContainer: {
  76 +
  77 + },
  78 + rowContainer: {
  79 +
  80 + },
  81 +});
  1 +import keyMirror from 'key-mirror';
  2 +
  3 +export default keyMirror({
  4 +
  5 + SET_PLATFORM: null,
  6 + SET_HOST: null,
  7 + SET_SERVICE_HOST: null,
  8 + SET_CHANNEL: null,
  9 +
  10 +
  11 +
  12 +});
  1 +'use strict'
  2 +
  3 +import React, {Component} from 'react';
  4 +import ReactNative, {
  5 + StyleSheet,
  6 + Dimensions,
  7 + Platform,
  8 + View,
  9 + Text,
  10 + NativeModules,
  11 + InteractionManager,
  12 + NativeAppEventEmitter,
  13 +} from 'react-native'
  14 +
  15 +import {bindActionCreators} from 'redux';
  16 +import {connect} from 'react-redux';
  17 +import {Map} from 'immutable';
  18 +import * as messageActions from '../reducers/message/messageActions';
  19 +
  20 +import Message from '../components/message/Message';
  21 +
  22 +
  23 +const actions = [
  24 + messageActions,
  25 +];
  26 +
  27 +function mapStateToProps(state) {
  28 + return {
  29 + ...state
  30 + };
  31 +}
  32 +
  33 +function mapDispatchToProps(dispatch) {
  34 +
  35 + const creators = Map()
  36 + .merge(...actions)
  37 + .filter(value => typeof value === 'function')
  38 + .toObject();
  39 +
  40 + return {
  41 + actions: bindActionCreators(creators, dispatch),
  42 + dispatch
  43 + };
  44 +}
  45 +
  46 +class MessageContainer extends Component {
  47 + constructor(props) {
  48 + super(props);
  49 +
  50 + this._onRefresh = this._onRefresh.bind(this);
  51 +
  52 +
  53 + }
  54 +
  55 + componentDidMount() {
  56 +
  57 + }
  58 +
  59 + componentWillUnmount() {
  60 +
  61 + }
  62 +
  63 + _onRefresh() {
  64 +
  65 + }
  66 +
  67 + render() {
  68 + let {message} = this.props;
  69 + return (
  70 + <View style={styles.container}>
  71 + <Message
  72 + data={message}
  73 + onRefresh={this._onRefresh}
  74 + />
  75 + </View>
  76 + );
  77 + }
  78 +}
  79 +
  80 +let styles = StyleSheet.create({
  81 + container: {
  82 + flex: 1,
  83 + },
  84 +
  85 +});
  86 +
  87 +export default connect(mapStateToProps, mapDispatchToProps)(MessageContainer);
  1 +'use strict';
  2 +
  3 +import ReactNative from 'react-native';
  4 +
  5 +const {
  6 + SET_PLATFORM,
  7 + SET_CHANNEL,
  8 + SET_HOST,
  9 + SET_SERVICE_HOST,
  10 +} = require('../../constants/actionTypes').default;
  11 +
  12 +export function setPlatform(platform) {
  13 + return {
  14 + type: SET_PLATFORM,
  15 + payload: platform
  16 + };
  17 +}
  18 +
  19 +export function setChannel(channel) {
  20 + return {
  21 + type: SET_CHANNEL,
  22 + payload: channel
  23 + };
  24 +}
  25 +
  26 +export function setHost(host) {
  27 + return {
  28 + type: SET_HOST,
  29 + payload: host
  30 + };
  31 +}
  32 +
  33 +export function setServiceHost(host) {
  34 + return {
  35 + type: SET_SERVICE_HOST,
  36 + payload: host
  37 + };
  38 +}
  1 +'use strict';
  2 +
  3 +import {Record, List, Map} from 'immutable';
  4 +
  5 +let InitialState = Record({
  6 + platform: 'ios', // ios, android
  7 + channel: 1, // 1 - boy, 2 - girl, 3 - kid, 4 - lifestyle, 5 - yoho
  8 + host: 'http://api.yoho.cn',
  9 + serviceHost: 'http://service.yoho.cn',
  10 +});
  11 +
  12 +export default InitialState;
  1 +'use strict';
  2 +
  3 +import InitialState from './appInitialState';
  4 +
  5 +const {
  6 + SET_PLATFORM,
  7 + SET_CHANNEL,
  8 + SET_HOST,
  9 + SET_SERVICE_HOST,
  10 +} = require('../../constants/actionTypes').default;
  11 +
  12 +const initialState = new InitialState;
  13 +
  14 +export default function appReducer(state = initialState, action) {
  15 + if (!(state instanceof InitialState)) return initialState.merge(state);
  16 +
  17 + switch (action.type) {
  18 + case SET_PLATFORM:
  19 + return state.set('platform', action.payload);
  20 + case SET_CHANNEL:
  21 + return state.set('channel', action.payload);
  22 + case SET_HOST:
  23 + return state.set('host', action.payload);
  24 + case SET_SERVICE_HOST:
  25 + return state.set('serviceHost', action.payload);
  26 + }
  27 +
  28 + return state;
  29 +}
  1 +import {combineReducers} from 'redux';
  2 +import app from './app/appReducer';
  3 +import message from './message/messageReducer';
  4 +
  5 +const rootReducer = combineReducers({
  6 + app,
  7 + message,
  8 +});
  9 +
  10 +export default rootReducer;
  1 +'use strict';
  2 +
  3 +import ReactNative from 'react-native';
  4 +import MessageService from '../../services/MessageService';
  5 +
  6 +const {
  7 + SET_CHANNEL,
  8 +
  9 +} = require('../../constants/actionTypes').default;
  10 +
  11 +
  1 +'use strict';
  2 +
  3 +import Immutable, {Record, List, Map} from 'immutable';
  4 +
  5 +let dataSource = Immutable.fromJS([
  6 + {
  7 + id: 'member',
  8 + title: '会员信息',
  9 + detail: '会员等级变更提醒、会员生日福利',
  10 + },
  11 + {
  12 + id: 'asset',
  13 + title: '我的资产',
  14 + detail: '新注册用户提示新人专享优惠券信息',
  15 + },
  16 + {
  17 + id: 'customer',
  18 + title: '客户服务',
  19 + detail: '没有收到任何关于你的消息',
  20 + },
  21 + {
  22 + id: 'other',
  23 + title: '其他',
  24 + detail: '没有收到任何系统给消息',
  25 + },
  26 + ]);
  27 +
  28 +let InitialState = Record({
  29 + dataSource,
  30 + newMessage: new (Record({
  31 + isFetching: false,
  32 + error: null,
  33 + data: List(),
  34 + })),
  35 +});
  36 +
  37 +export default InitialState;
  1 +'use strict';
  2 +
  3 +import InitialState from './messageInitialState';
  4 +import Immutable, {Map} from 'immutable';
  5 +
  6 +const {
  7 + SET_CHANNEL,
  8 +
  9 +} = require('../../constants/actionTypes').default;
  10 +
  11 +const initialState = new InitialState;
  12 +
  13 +export default function messageReducer(state=initialState, action) {
  14 + switch(action.type) {
  15 +
  16 +
  17 + }
  18 +
  19 + return state;
  20 +}
  1 +'use strict';
  2 +
  3 +import Request from '../../common/services/Request';
  4 +
  5 +export default class MessageService {
  6 +
  7 + constructor (host) {
  8 + let baseURL = 'http://api.yoho.cn';
  9 + if(host){
  10 + baseURL = host;
  11 + }
  12 + this.api = new Request(baseURL);
  13 + }
  14 +
  15 +}
  1 +/**
  2 + * # configureStore.js
  3 + *
  4 + * A Redux boilerplate setup
  5 + *
  6 + */
  7 +'use strict';
  8 +
  9 +/**
  10 + * ## Imports
  11 + *
  12 + * redux functions
  13 + */
  14 +import { createStore, applyMiddleware } from 'redux';
  15 +import thunk from 'redux-thunk';
  16 +import createLogger from 'redux-logger';
  17 +
  18 +/**
  19 +* ## Reducer
  20 +* The reducer contains the 4 reducers from
  21 +* device, global, auth, profile
  22 +*/
  23 +import reducer from '../reducers';
  24 +
  25 +const logger = createLogger({
  26 + predicate: (getState, action) => process.env.NODE_ENV === `development`
  27 +});
  28 +
  29 +/**
  30 + * ## creatStoreWithMiddleware
  31 + * Like the name...
  32 + */
  33 +const createStoreWithMiddleware = applyMiddleware(
  34 + thunk,
  35 + logger
  36 +)(createStore);
  37 +
  38 +/**
  39 + * ## configureStore
  40 + * @param {Object} the state with for keys:
  41 + * device, global, auth, profile
  42 + *
  43 + */
  44 +export default function configureStore(initialState) {
  45 + return createStoreWithMiddleware(reducer, initialState);
  46 +};
  1 +'use strict';
  2 +
  3 +import React from 'react';
  4 +import ReactNative, {
  5 + AppRegistry,
  6 + Platform,
  7 +} from 'react-native';
  8 +
  9 +import {
  10 + Provider,
  11 + connect
  12 +} from 'react-redux';
  13 +
  14 +import configureStore from './store/configureStore';
  15 +import {Record, List, Map} from 'immutable';
  16 +
  17 +import appInitialState from './reducers/app/appInitialState';
  18 +import newArrivalInitialState from './reducers/newArrival/newArrivalInitialState';
  19 +
  20 +import NewArrivalContainer from './containers/NewArrivalContainer';
  21 +
  22 +import {
  23 + setPlatform,
  24 + setHost,
  25 + setServiceHost,
  26 +} from './reducers/app/appActions';
  27 +
  28 +
  29 +function getInitialState() {
  30 + const _initState = {
  31 + app: (new appInitialState()),
  32 + newArrival: (new newArrivalInitialState()),
  33 + };
  34 + return _initState;
  35 +}
  36 +
  37 +export default function native(platform) {
  38 +
  39 + let YH_NewArrival = React.createClass({
  40 +
  41 + render() {
  42 + const store = configureStore(getInitialState());
  43 + store.dispatch(setPlatform(platform));
  44 + store.dispatch(setHost(this.props.host));
  45 + store.dispatch(setServiceHost(this.props.serviceHost));
  46 +
  47 + return (
  48 + <Provider store={store}>
  49 + <NewArrivalContainer />
  50 + </Provider>
  51 + );
  52 + }
  53 + });
  54 +
  55 + AppRegistry.registerComponent('YH_NewArrival', () => YH_NewArrival);
  56 +}
  1 +'use strict';
  2 +
  3 +import React, {Component} from 'react';
  4 +import ReactNative, {
  5 + View,
  6 + Text,
  7 + Image,
  8 + ListView,
  9 + StyleSheet,
  10 + Dimensions,
  11 + TouchableOpacity,
  12 + InteractionManager,
  13 + Platform,
  14 +} from 'react-native';
  15 +
  16 +
  17 +
  18 +export default class NewArrival extends Component {
  19 +
  20 + constructor(props) {
  21 + super(props);
  22 +
  23 + this._renderRow = this._renderRow.bind(this);
  24 + this._onEndReached = this._onEndReached.bind(this);
  25 + this._renderFooter = this._renderFooter.bind(this);
  26 + this.trigggePullToRefresh = this.trigggePullToRefresh.bind(this);
  27 +
  28 + this.dataSource = new ListView.DataSource({
  29 + rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
  30 + });
  31 + }
  32 +
  33 + componentDidMount() {
  34 + this.trigggePullToRefresh();
  35 + }
  36 +
  37 + componentWillReceiveProps(nextProps) {
  38 + if (nextProps.data.ptr) {
  39 + this.listView && this.listView.scrollTo({x: 0, y: 0, animated: false, });
  40 + }
  41 + }
  42 +
  43 + trigggePullToRefresh() {
  44 + if (Platform.OS === 'ios') {
  45 + InteractionManager.runAfterInteractions(() => {
  46 + this.listView && this.listView.getScrollResponder().startPullToRefresh();
  47 + });
  48 + } else {
  49 + this.props.onRefresh && this.props.onRefresh();
  50 + }
  51 + }
  52 +
  53 + _renderRow(rowData: object, sectionID: number, rowID: number) {
  54 + return null;
  55 + }
  56 +
  57 + _onEndReached() {
  58 +
  59 + }
  60 +
  61 + _renderFooter() {
  62 + return null;
  63 + }
  64 +
  65 + render() {
  66 +
  67 + let {list, ptr, isFetching, endReached, showLoginTip, cachedList} = this.props.data;
  68 + let dataSource = list.size == 0 ? cachedList.toArray() : list.toArray();
  69 +
  70 + let isLoadingMore = list.size != 0 && !ptr && isFetching;
  71 +
  72 + return (
  73 + <View style={styles.container}>
  74 + <ListView
  75 + ref={(c) => {
  76 + this.listView = c;
  77 + }}
  78 + contentContainerStyle={styles.contentContainer}
  79 + dataSource={this.dataSource.cloneWithRows(dataSource)}
  80 + renderRow={this._renderRow}
  81 + enableEmptySections={true}
  82 + onEndReached={this._onEndReached}
  83 + renderFooter={this._renderFooter}
  84 + />
  85 + </View>
  86 + );
  87 + }
  88 +}
  89 +
  90 +let {width, height} = Dimensions.get('window');
  91 +
  92 +let styles = StyleSheet.create({
  93 + container: {
  94 + flex: 1,
  95 + backgroundColor: '#f0f0f0',
  96 + },
  97 + contentContainer: {
  98 +
  99 + },
  100 +});
  1 +import keyMirror from 'key-mirror';
  2 +
  3 +export default keyMirror({
  4 +
  5 + SET_PLATFORM: null,
  6 + SET_HOST: null,
  7 + SET_SERVICE_HOST: null,
  8 + SET_CHANNEL: null,
  9 +
  10 +
  11 +
  12 +});
  1 +'use strict'
  2 +
  3 +import React, {Component} from 'react';
  4 +import ReactNative, {
  5 + StyleSheet,
  6 + Dimensions,
  7 + Platform,
  8 + View,
  9 + Text,
  10 + NativeModules,
  11 + InteractionManager,
  12 + NativeAppEventEmitter,
  13 +} from 'react-native'
  14 +
  15 +import {bindActionCreators} from 'redux';
  16 +import {connect} from 'react-redux';
  17 +import {Map} from 'immutable';
  18 +import * as newArrivalActions from '../reducers/newArrival/newArrivalActions';
  19 +
  20 +import NewArrival from '../components/newArrival/NewArrival';
  21 +
  22 +
  23 +const actions = [
  24 + newArrivalActions,
  25 +];
  26 +
  27 +function mapStateToProps(state) {
  28 + return {
  29 + ...state
  30 + };
  31 +}
  32 +
  33 +function mapDispatchToProps(dispatch) {
  34 +
  35 + const creators = Map()
  36 + .merge(...actions)
  37 + .filter(value => typeof value === 'function')
  38 + .toObject();
  39 +
  40 + return {
  41 + actions: bindActionCreators(creators, dispatch),
  42 + dispatch
  43 + };
  44 +}
  45 +
  46 +class NewArrivalContainer extends Component {
  47 + constructor(props) {
  48 + super(props);
  49 +
  50 + this._onEndReached = this._onEndReached.bind(this);
  51 + }
  52 +
  53 + componentDidMount() {
  54 +
  55 + }
  56 +
  57 + componentWillUnmount() {
  58 +
  59 + }
  60 +
  61 + _onEndReached() {
  62 +
  63 + }
  64 +
  65 + render() {
  66 + let {newArrival} = this.props;
  67 + return (
  68 + <View style={styles.container}>
  69 + <NewArrival
  70 + data={newArrival}
  71 + onEndReached={this._onEndReached}
  72 + />
  73 + </View>
  74 + );
  75 + }
  76 +}
  77 +
  78 +let styles = StyleSheet.create({
  79 + container: {
  80 + flex: 1,
  81 + },
  82 +
  83 +});
  84 +
  85 +export default connect(mapStateToProps, mapDispatchToProps)(NewArrivalContainer);
  1 +'use strict';
  2 +
  3 +import ReactNative from 'react-native';
  4 +
  5 +const {
  6 + SET_PLATFORM,
  7 + SET_CHANNEL,
  8 + SET_HOST,
  9 + SET_SERVICE_HOST,
  10 +} = require('../../constants/actionTypes').default;
  11 +
  12 +export function setPlatform(platform) {
  13 + return {
  14 + type: SET_PLATFORM,
  15 + payload: platform
  16 + };
  17 +}
  18 +
  19 +export function setChannel(channel) {
  20 + return {
  21 + type: SET_CHANNEL,
  22 + payload: channel
  23 + };
  24 +}
  25 +
  26 +export function setHost(host) {
  27 + return {
  28 + type: SET_HOST,
  29 + payload: host
  30 + };
  31 +}
  32 +
  33 +export function setServiceHost(host) {
  34 + return {
  35 + type: SET_SERVICE_HOST,
  36 + payload: host
  37 + };
  38 +}
  1 +'use strict';
  2 +
  3 +import {Record, List, Map} from 'immutable';
  4 +
  5 +let InitialState = Record({
  6 + platform: 'ios', // ios, android
  7 + channel: 1, // 1 - boy, 2 - girl, 3 - kid, 4 - lifestyle, 5 - yoho
  8 + host: 'http://api.yoho.cn',
  9 + serviceHost: 'http://service.yoho.cn',
  10 +});
  11 +
  12 +export default InitialState;
  1 +'use strict';
  2 +
  3 +import InitialState from './appInitialState';
  4 +
  5 +const {
  6 + SET_PLATFORM,
  7 + SET_CHANNEL,
  8 + SET_HOST,
  9 + SET_SERVICE_HOST,
  10 +} = require('../../constants/actionTypes').default;
  11 +
  12 +const initialState = new InitialState;
  13 +
  14 +export default function appReducer(state = initialState, action) {
  15 + if (!(state instanceof InitialState)) return initialState.merge(state);
  16 +
  17 + switch (action.type) {
  18 + case SET_PLATFORM:
  19 + return state.set('platform', action.payload);
  20 + case SET_CHANNEL:
  21 + return state.set('channel', action.payload);
  22 + case SET_HOST:
  23 + return state.set('host', action.payload);
  24 + case SET_SERVICE_HOST:
  25 + return state.set('serviceHost', action.payload);
  26 + }
  27 +
  28 + return state;
  29 +}
  1 +import {combineReducers} from 'redux';
  2 +import app from './app/appReducer';
  3 +import newArrival from './newArrival/newArrivalReducer';
  4 +
  5 +const rootReducer = combineReducers({
  6 + app,
  7 + newArrival,
  8 +});
  9 +
  10 +export default rootReducer;
  1 +'use strict';
  2 +
  3 +import ReactNative from 'react-native';
  4 +import NewArrivalService from '../../services/NewArrivalService';
  5 +
  6 +const {
  7 + SET_CHANNEL,
  8 +
  9 +} = require('../../constants/actionTypes').default;
  10 +
  11 +
  1 +'use strict';
  2 +
  3 +import {Record, List, Map} from 'immutable';
  4 +
  5 +let InitialState = Record({
  6 +
  7 +});
  8 +
  9 +export default InitialState;
  1 +'use strict';
  2 +
  3 +import InitialState from './newArrivalInitialState';
  4 +import Immutable, {Map} from 'immutable';
  5 +
  6 +const {
  7 + SET_CHANNEL,
  8 +
  9 +} = require('../../constants/actionTypes').default;
  10 +
  11 +const initialState = new InitialState;
  12 +
  13 +export default function newArrivalReducer(state=initialState, action) {
  14 + switch(action.type) {
  15 +
  16 +
  17 + }
  18 +
  19 + return state;
  20 +}
  1 +'use strict';
  2 +
  3 +import Request from '../../common/services/Request';
  4 +
  5 +export default class HomeService {
  6 +
  7 + constructor (host) {
  8 + let baseURL = 'http://api.yoho.cn';
  9 + if(host){
  10 + baseURL = host;
  11 + }
  12 + this.api = new Request(baseURL);
  13 + }
  14 +
  15 +}
  1 +/**
  2 + * # configureStore.js
  3 + *
  4 + * A Redux boilerplate setup
  5 + *
  6 + */
  7 +'use strict';
  8 +
  9 +/**
  10 + * ## Imports
  11 + *
  12 + * redux functions
  13 + */
  14 +import { createStore, applyMiddleware } from 'redux';
  15 +import thunk from 'redux-thunk';
  16 +import createLogger from 'redux-logger';
  17 +
  18 +/**
  19 +* ## Reducer
  20 +* The reducer contains the 4 reducers from
  21 +* device, global, auth, profile
  22 +*/
  23 +import reducer from '../reducers';
  24 +
  25 +const logger = createLogger({
  26 + predicate: (getState, action) => process.env.NODE_ENV === `development`
  27 +});
  28 +
  29 +/**
  30 + * ## creatStoreWithMiddleware
  31 + * Like the name...
  32 + */
  33 +const createStoreWithMiddleware = applyMiddleware(
  34 + thunk,
  35 + logger
  36 +)(createStore);
  37 +
  38 +/**
  39 + * ## configureStore
  40 + * @param {Object} the state with for keys:
  41 + * device, global, auth, profile
  42 + *
  43 + */
  44 +export default function configureStore(initialState) {
  45 + return createStoreWithMiddleware(reducer, initialState);
  46 +};
@@ -94,19 +94,19 @@ export default function native(platform) { @@ -94,19 +94,19 @@ export default function native(platform) {
94 } 94 }
95 95
96 let type = this.props.type; 96 let type = this.props.type;
97 - if (type == 'list') { 97 + if (type == 'detail') {
  98 + let id=this.props.id;
  99 + let udid=this.props.udid;
  100 + store.dispatch(setId(id));
98 return ( 101 return (
99 <Provider store={store}> 102 <Provider store={store}>
100 - <PlustarContainer /> 103 + <DetailContainer />
101 </Provider> 104 </Provider>
102 ); 105 );
103 } else { 106 } else {
104 - let id=this.props.id;  
105 - let udid=this.props.udid;  
106 - store.dispatch(setId(id));  
107 return ( 107 return (
108 <Provider store={store}> 108 <Provider store={store}>
109 - <DetailContainer /> 109 + <PlustarContainer />
110 </Provider> 110 </Provider>
111 ); 111 );
112 } 112 }
1 -'use strict';  
2 -  
3 -import React from 'react';  
4 -import ReactNative from 'react-native';  
5 -  
6 -const {  
7 - View,  
8 - Image,  
9 - Text,  
10 - TouchableOpacity,  
11 - Dimensions,  
12 - StyleSheet,  
13 -} = ReactNative;  
14 -  
15 -export default class GPTags extends React.Component {  
16 -  
17 - constructor(props) {  
18 - super (props);  
19 -  
20 - }  
21 -  
22 - render() {  
23 - let {title, isLimit} = this.props;  
24 -  
25 - let leftWidth = tagMaxWidth;  
26 - if (isLimit) {  
27 - leftWidth = tagMaxWidth - limitWidth - 2;  
28 - }  
29 -  
30 - let textMaxWidth = leftWidth - iconWidth - 5 * 2;  
31 -  
32 - return (  
33 - <View style={styles.container}>  
34 - <View style={[styles.leftContainer, {width: leftWidth}]}>  
35 - <Image  
36 - style={styles.icon}  
37 - source={require('../../images/tag/globalpurchase_fly_smallicon.png')}  
38 - />  
39 - <Text numberOfLines={1} style={[styles.text, {maxWidth: textMaxWidth}]}>{title}</Text>  
40 - </View>  
41 -  
42 - {isLimit ? <Image  
43 - style={styles.limit}  
44 - source={require('../../images/tag/gp_tip_xl_product.png')}  
45 - /> : null}  
46 -  
47 - </View>  
48 - );  
49 - }  
50 -}  
51 -  
52 -let {width, height} = Dimensions.get('window');  
53 -  
54 -const DEVICE_WIDTH_RATIO = width / 320;  
55 -let tagMaxWidth = Math.ceil(137.5 * DEVICE_WIDTH_RATIO);  
56 -let tagHeight = Math.ceil(14 * DEVICE_WIDTH_RATIO);  
57 -let iconWidth = Math.ceil(10 * DEVICE_WIDTH_RATIO);  
58 -  
59 -let limitWidth = Math.ceil(30 * DEVICE_WIDTH_RATIO);  
60 -let limitHeight = Math.ceil(14 * DEVICE_WIDTH_RATIO);  
61 -  
62 -let styles = StyleSheet.create({  
63 - container: {  
64 - flexDirection: 'row',  
65 - alignItems: 'center',  
66 - width: width,  
67 - height: tagHeight,  
68 - },  
69 - leftContainer: {  
70 - flexDirection: 'row',  
71 - alignItems: 'center',  
72 - backgroundColor: '#462e3d',  
73 - height: tagHeight,  
74 - },  
75 - icon: {  
76 - marginLeft: 5,  
77 - width: iconWidth,  
78 - height: iconWidth,  
79 - resizeMode: 'contain',  
80 - },  
81 - text: {  
82 - marginLeft: 5,  
83 - marginRight: 5,  
84 - color: 'white',  
85 - fontSize: 10,  
86 - fontFamily: 'STHeitiSC-Light',  
87 - },  
88 - limit: {  
89 - marginLeft: 2,  
90 - width: limitWidth,  
91 - height: limitHeight,  
92 - resizeMode: 'contain',  
93 - },  
94 -});  
@@ -13,7 +13,7 @@ import ReactNative, { @@ -13,7 +13,7 @@ import ReactNative, {
13 13
14 import SlicedImage from '../../../common/components/SlicedImage'; 14 import SlicedImage from '../../../common/components/SlicedImage';
15 import LoadMoreIndicator from '../../../common/components/LoadMoreIndicator'; 15 import LoadMoreIndicator from '../../../common/components/LoadMoreIndicator';
16 -import ProductListCell from './ProductListCell'; 16 +import ProductListCell from '../../../common/components/ListCell/ProductListCell';
17 import ProductFilter from './ProductFilter'; 17 import ProductFilter from './ProductFilter';
18 import ProductShopCell from './ProductShopCell'; 18 import ProductShopCell from './ProductShopCell';
19 19
1 -'use strict';  
2 -  
3 -import React, {Component} from 'react';  
4 -import ReactNative, {  
5 - View,  
6 - Text,  
7 - Image,  
8 - ListView,  
9 - StyleSheet,  
10 - Dimensions,  
11 - TouchableOpacity,  
12 -} from 'react-native';  
13 -  
14 -import SlicedImage from '../../../common/components/SlicedImage';  
15 -import Tags from './Tags';  
16 -import GPTags from './GPTags';  
17 -  
18 -  
19 -export default class Search extends Component {  
20 -  
21 - constructor(props) {  
22 - super(props);  
23 -  
24 - this._renderTags = this._renderTags.bind(this);  
25 - this._renderImages = this._renderImages.bind(this);  
26 - this._renderPrice = this._renderPrice.bind(this);  
27 - }  
28 -  
29 - _renderTags() {  
30 - let {data, sourceType} = this.props;  
31 - let isGlobalProduct = data.get('is_global') && data.get('is_global') == 'Y'; // 是否全球购商品  
32 -  
33 - let tags = data.get('tags'); // 商品标签  
34 - // 数据源是全球购  
35 - if (sourceType == 1) {  
36 - if (isGlobalProduct) {  
37 -  
38 - } else {  
39 - tags = ['is_in_stock', ...tags];  
40 - }  
41 - }  
42 -  
43 - let countryName = isGlobalProduct && data.get('country_name'); // 全球购国家名称  
44 -  
45 - if (isGlobalProduct) {  
46 - return <GPTags title={countryName} limit={showGPLimitTag}/>;  
47 - } else {  
48 - return <Tags items={tags}/>;  
49 - }  
50 - }  
51 -  
52 - _renderImages() {  
53 - let {data, sourceType} = this.props;  
54 - let url = data.get('default_images').replace('{width}', rowWidth).replace('{height}', imageHeight); // 商品缩略图  
55 - url = SlicedImage.getSlicedUrl(data.get('default_images'), 290, 386, 2);  
56 -  
57 - let isGlobalProduct = data.get('is_global') && data.get('is_global') == 'Y'; // 是否全球购商品  
58 -  
59 -  
60 - let showGPLimitTag = isGlobalProduct && data.get('is_limited') && data.get('is_limited') == 'Y'; // 全球购限量商品  
61 - let showGPSoldOut = isGlobalProduct && data.get('is_stock') && data.get('is_stock') == 'Y'; // 全球购售罄  
62 -  
63 - let showAlmostSoldOut = !isGlobalProduct && data.get('tags', []).indexOf('is_soon_sold_out') !== -1; // 非全球购的即将售罄  
64 - let showSoldOut = sourceType == 2 && data.get('storage_num') && data.get('storage_num') == 0; // 数据源是奥莱才显示  
65 -  
66 - return (  
67 - <View style={styles.imageContainer}>  
68 - <Image style={styles.image} source={{uri: url}}>  
69 - {showAlmostSoldOut ? <Image style={styles.almostSoldOutImage} source={require('../../images/tag/tip_jjsq.png')}/> : null}  
70 - {showSoldOut ? <Image style={styles.soldOutImage} source={require('../../images/tag/outlet_sellout_bg.png')}/> : null}  
71 - {showGPSoldOut ? <Image style={styles.gpSoldOutImage} source={require('../../images/tag/gp_tip_SQ.png')}/> : null}  
72 - </Image>  
73 - </View>  
74 - );  
75 - }  
76 -  
77 - _renderPrice() {  
78 - let {data, sourceType} = this.props;  
79 - let isGlobalProduct = data.get('is_global') && data.get('is_global') == 'Y'; // 是否全球购商品  
80 -  
81 - let salePrice = 0; // 售卖价  
82 - let originPrice = 0; // 原价  
83 - let salePriceStr = ''; // 拼接的售卖价  
84 - let originPriceStr = ''; // 拼接的原价  
85 - let showOriginPrice = true; // 是否显示原价  
86 - let salePriceColor = '#d0021b'; // 不显示原价时,售卖价颜色  
87 -  
88 - if (isGlobalProduct) {  
89 - salePrice = parseFloat(data.get('final_price'));  
90 - originPrice = parseFloat(data.get('orign_price'));  
91 - salePriceStr = data.get('formart_final_price');  
92 - originPriceStr = data.get('formart_orign_price');  
93 - } else {  
94 - salePrice = parseFloat(data.get('sales_price'));  
95 - originPrice = parseFloat(data.get('market_price'));  
96 - salePriceStr = '¥' + salePrice.toFixed(2);  
97 - originPriceStr = '¥' + originPrice.toFixed(2);  
98 - }  
99 -  
100 - if (!originPrice || (salePrice == originPrice)) {  
101 - showOriginPrice = false;  
102 - salePriceColor = '#444444';  
103 - }  
104 -  
105 - if (showOriginPrice) {  
106 - return (  
107 - <View style={styles.priceContainer}>  
108 - <Text style={[styles.nowPrice, {color: salePriceColor}]} numberOfLines={1}>{salePriceStr}</Text>  
109 - <View style={styles.oldPriceContainer}>  
110 - <Text style={styles.oldPrice} numberOfLines={1}>{originPriceStr}</Text>  
111 - <View style={styles.deleteLine}/>  
112 - </View>  
113 - </View>  
114 -  
115 - );  
116 - } else {  
117 - return (  
118 - <View style={styles.priceContainer}>  
119 - <Text style={[styles.nowPrice, {color: salePriceColor}]} numberOfLines={1}>{salePriceStr}</Text>  
120 - </View>  
121 - );  
122 - }  
123 - }  
124 -  
125 -  
126 - render() {  
127 - let {data, sourceType, rowID, style} = this.props;  
128 - let name = data.get('product_name') ? data.get('product_name') : '';  
129 -  
130 - return (  
131 - <TouchableOpacity  
132 - style={[styles.container, style]}  
133 - activeOpacity={1}  
134 - onPress={() => {  
135 - this.props.onPressProduct && this.props.onPressProduct(data, rowID);  
136 - }}  
137 - >  
138 - <View>  
139 -  
140 - {this._renderTags()}  
141 -  
142 - {this._renderImages()}  
143 -  
144 - <View style={styles.nameContainer}>  
145 - <Text style={styles.name} numberOfLines={2}>{name}</Text>  
146 - </View>  
147 -  
148 - {this._renderPrice()}  
149 -  
150 - </View>  
151 - </TouchableOpacity>  
152 - );  
153 - }  
154 -}  
155 -  
156 -let {width, height} = Dimensions.get('window');  
157 -  
158 -const DEVICE_WIDTH_RATIO = width / 320;  
159 -let rowWidth = Math.ceil(137.5 * DEVICE_WIDTH_RATIO);  
160 -let rowHeight = Math.ceil(254 * DEVICE_WIDTH_RATIO);  
161 -let rowMarginTop = Math.ceil(10 * DEVICE_WIDTH_RATIO);  
162 -  
163 -const IMAGE_WIDTH = 145;  
164 -const IMAGE_HEIGHT = 193;  
165 -const IMAGE_RATIO = IMAGE_HEIGHT / IMAGE_WIDTH;  
166 -let imageTop = 14 * DEVICE_WIDTH_RATIO;  
167 -let imageHeight = rowWidth * IMAGE_RATIO;  
168 -  
169 -let almostSoldOutImageHeight = Math.ceil(14 * DEVICE_WIDTH_RATIO);  
170 -let almostSoldOutImageTop = imageHeight - almostSoldOutImageHeight;  
171 -  
172 -let nameMarginTop = Math.ceil(7 * DEVICE_WIDTH_RATIO);  
173 -let nameHeight = Math.ceil(36 * DEVICE_WIDTH_RATIO);  
174 -  
175 -let gpSoldOutImageHeight = Math.ceil(25 * DEVICE_WIDTH_RATIO);  
176 -  
177 -let styles = StyleSheet.create({  
178 - container: {  
179 - width: rowWidth,  
180 - height: rowHeight,  
181 - marginTop: rowMarginTop,  
182 - },  
183 - rowContainer: {  
184 - width: rowWidth,  
185 - height: rowHeight,  
186 -  
187 - },  
188 - imageContainer: {  
189 - width: rowWidth,  
190 - height: imageHeight,  
191 - backgroundColor: '#f0f0f0',  
192 - },  
193 - image: {  
194 - width: rowWidth,  
195 - height: imageHeight,  
196 - backgroundColor: '#f0f0f0',  
197 - },  
198 - soldOutImage: {  
199 - position: 'absolute',  
200 - top: 0,  
201 - left: 0,  
202 - width: rowWidth,  
203 - height: imageHeight,  
204 - },  
205 - almostSoldOutImage: {  
206 - top: almostSoldOutImageTop,  
207 - width: rowWidth,  
208 - height: almostSoldOutImageHeight,  
209 - backgroundColor: '#ff9e0d',  
210 - },  
211 - nameContainer: {  
212 - // justifyContent: 'center',  
213 - marginTop: nameMarginTop,  
214 - width: rowWidth,  
215 - height: nameHeight,  
216 - },  
217 - name: {  
218 - fontFamily: 'STHeitiSC-Light',  
219 - fontSize: 12,  
220 - color: '#444444',  
221 - },  
222 - priceContainer: {  
223 - flexDirection: 'row',  
224 - },  
225 - nowPrice: {  
226 - fontSize: 12,  
227 - color: '#d0021b',  
228 - },  
229 - oldPriceContainer: {  
230 - flexDirection: 'row',  
231 - marginLeft: 5,  
232 - },  
233 - oldPrice: {  
234 - fontSize: 12,  
235 - color: '#b0b0b0',  
236 - height: 16,  
237 - },  
238 - deleteLine: {  
239 - position: 'absolute',  
240 - top: (16 / 2) - 0.8,  
241 - left: 0,  
242 - right: 0,  
243 - height: 1,  
244 - backgroundColor: '#b0b0b0',  
245 - },  
246 - gpSoldOutImage: {  
247 - position: 'absolute',  
248 - top: 5,  
249 - right: 5,  
250 - width: gpSoldOutImageHeight,  
251 - height: gpSoldOutImageHeight,  
252 - },  
253 -});  
1 -'use strict';  
2 -  
3 -import React from 'react';  
4 -import ReactNative from 'react-native';  
5 -  
6 -const {  
7 - View,  
8 - Image,  
9 - Text,  
10 - ListView,  
11 - Dimensions,  
12 - StyleSheet,  
13 -} = ReactNative;  
14 -  
15 -export default class Tags extends React.Component {  
16 -  
17 - constructor(props) {  
18 - super (props);  
19 -  
20 - this.config = {  
21 - is_discount: {  
22 - image: require('../../images/tag/tip_sale.png'),  
23 - width: 30,  
24 - }, // YH_ProductTagTypeSale  
25 - resale: {  
26 - image: require('../../images/tag/tip_zdz.png'),  
27 - width: 45,  
28 - }, // YH_ProductTagTypeReSale  
29 - 'mid-year': {  
30 - image: require('../../images/tag/tip_nzrc.png'),  
31 - width: 45,  
32 - }, // YH_ProductTagTypeSaleMiddle  
33 - 'year-end': {  
34 - image: require('../../images/tag/tip_nzdc.png'),  
35 - width: 45,  
36 - }, // YH_ProductTagTypeSaleHot  
37 - is_new: {  
38 - image: require('../../images/tag/tip_new.png'),  
39 - width: 30,  
40 - }, // YH_ProductTagTypeNew  
41 - is_yohood: {  
42 - image: require('../../images/tag/tip_xpj.png'),  
43 - width: 45,  
44 - }, // YH_ProductTagTypeYohood  
45 - is_limited: {  
46 - image: require('../../images/tag/tip_xl_product.png'),  
47 - width: 30,  
48 - }, // YH_ProductTagTypeLimited  
49 - is_in_stock: {  
50 - image: require('../../images/tag/tip_gnzf.png'),  
51 - width: 45,  
52 - }, // YH_ProductTagTypeInland  
53 - is_deposit_advance: {  
54 - image: require('../../images/tag/tip_advance.png'),  
55 - width: 30,  
56 - }, // YH_ProductTagTypeDeposit  
57 - default: {  
58 - image: '',  
59 - width: 45,  
60 - },  
61 - };  
62 -  
63 - this._renderRow = this._renderRow.bind(this);  
64 -  
65 - this.dataSource = new ListView.DataSource({  
66 - rowHasChanged: (r1, r2) => r1 != r2,  
67 - });  
68 - }  
69 -  
70 - _renderRow(rowData, sectionID, rowID) {  
71 - let item = this.config[rowData];  
72 - if (!item) {  
73 - return null;  
74 - }  
75 -  
76 - let width = Math.ceil(item.width * DEVICE_WIDTH_RATIO);  
77 - let marginLeft = rowID == 0 ? 0 : 2;  
78 - let iconStyle = {width, height: tagHeight, marginLeft};  
79 -  
80 - return (  
81 - <Image style={[styles.icon, iconStyle]} source={item.image}/>  
82 - );  
83 - }  
84 -  
85 - render() {  
86 -  
87 - let {style, items} = this.props;  
88 - // let tags = ['is_discount', 'resale', ];  
89 -  
90 - return (  
91 - <View style={[styles.container]}>  
92 - <ListView  
93 - style={[styles.container]}  
94 - contentContainerStyle={[styles.contentContainer]}  
95 - enableEmptySections={true}  
96 - dataSource={this.dataSource.cloneWithRows(items.toArray())}  
97 - renderRow={this._renderRow}  
98 - scrollEnabled={false}  
99 - scrollsToTop={false}  
100 - horizontal={true}  
101 - showsHorizontalScrollIndicator={false}  
102 - />  
103 - </View>  
104 - );  
105 - }  
106 -}  
107 -  
108 -let {width, height} = Dimensions.get('window');  
109 -  
110 -const DEVICE_WIDTH_RATIO = width / 320;  
111 -let tagHeight = Math.ceil(14 * DEVICE_WIDTH_RATIO);  
112 -  
113 -let styles = StyleSheet.create({  
114 - container: {  
115 -  
116 - },  
117 - contentContainer: {  
118 - height: tagHeight,  
119 - },  
120 - icon: {  
121 - resizeMode: 'contain',  
122 - },  
123 -});