Authored by shangjf

首页推荐弹框 优化review by 于良

... ... @@ -9,7 +9,7 @@ import ReactNative, {
StyleSheet,
Dimensions,
} from 'react-native';
import Immutable, {Map} from 'immutable';
import Immutable, {Map, List} from 'immutable';
import SlicedImage from '../../../common/components/SlicedImage';
... ... @@ -41,17 +41,17 @@ export default class PopularSingleProduct extends Component{
let goodsLookNum = rowData.get('sales_num') + "人";
return (
<TouchableOpacity
<TouchableOpacity
activeOpacity={1}
onPress={() => this.props.onPressImageItem && this.props.onPressImageItem(rowData, rowID)}
>
<View style={styles.goodsContainer}>
<YH_Image
style={styles.goodsImage}
<YH_Image
style={styles.goodsImage}
masksToBounds={true}
radius={{'topLeft':'2','topRight':'2','bottomRight':'2','bottomLeft':'2'}}
url={goodsImageUrl}
url={goodsImageUrl}
/>
<Text style={styles.goodsPrice} numberOfLines={1}>{goodsPrice}</Text>
<Text style={styles.goodsLookNum} numberOfLines={1}>{goodsLookNum}</Text>
... ... @@ -72,7 +72,8 @@ export default class PopularSingleProduct extends Component{
let banner = data.get("banner_image");
let title = data.get("title");
let imglst = data.get("list");
let imglst = data.get("list", List());
// let backgroundStyle = background.get("color") ? {backgroundColor: background.get("color")} : null;
... ... @@ -92,6 +93,7 @@ export default class PopularSingleProduct extends Component{
dataSource={this.dataSource.cloneWithRows(imglst.toArray())}
horizontal={true}
renderRow={this.renderRow}
enableEmptySections={true}
/>
</View>
);
... ...
... ... @@ -47,6 +47,8 @@ import VipUserFloor from '../floor/VipUserFloor';
import ActivityProductFloor from '../floor/ActivityProductFloor';
import HotCategoryIndividualization from '../floor/HotCategoryIndividualization';
import ProductListCell from '../../../common/components/ListCell/ProductListCell';
import RecommendPopView from './RecommendPopView';
export default class Home extends Component {
... ... @@ -59,6 +61,7 @@ export default class Home extends Component {
this._floorCellRender = this._floorCellRender.bind(this);
this._currentChannelData = this._currentChannelData.bind(this);
this._renderSectionHeader = this._renderSectionHeader.bind(this);
this._autoScrollToProductList = this._autoScrollToProductList.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
... ... @@ -389,11 +392,16 @@ export default class Home extends Component {
}
_autoScrollToProductList() {
this.props.onClickRecommendProduct&&this.props.onClickRecommendProduct()
}
_renderFooter() {
let data = this._currentChannelData();
let {isFetching, endReached, list, cached} = data;
let floorList = list.size > 0 ? list.toArray() : cached.get('list').toArray();
let isLoadingMore = !endReached && floorList.size != 0;
let isLoadingMore = !endReached && floorList.length != 0;
return (
<LoadMoreIndicator
... ... @@ -423,66 +431,81 @@ export default class Home extends Component {
render() {
let channel = this.props.channel;
let data = this._currentChannelData();
let {isFetching} = data;
let {isFetching, recommendProduct} = data;
let dataSource = floorParser.homeDataParse(data, channel);
let isPullToRefresh = isFetching;
let recommendData = recommendProduct.get('list').toArray();
let recommendVisible = recommendProduct.get('isVisible');
if (recommendData.length > 0) {
recommendData = recommendData[0];
} else {
recommendVisible = false;
}
return (
<View style={styles.container}>
{
Platform.OS === 'ios' ?
<ListView
ref={(c) => {
this.listView = c;
}}
contentContainerStyle={styles.contentContainer}
dataSource={this.dataSource.cloneWithRowsAndSections(dataSource)}
renderRow={this._renderRow}
renderSectionHeader={this._renderSectionHeader}
removeClippedSubviews={true}
enableEmptySections={true}
enablePullToRefresh={true}
isOnPullToRefresh={isPullToRefresh}
onRefreshData={() => {
this.props.onRefresh && this.props.onRefresh();
}}
onEndReached={() => {
this.props.onEndReached && this.props.onEndReached();
}}
renderFooter={this._renderFooter}
/>
:
<ListView
ref={(c) => {
this.listView = c;
}}
contentContainerStyle={styles.contentContainer}
dataSource={this.dataSource.cloneWithRowsAndSections(dataSource)}
renderRow={this._renderRow}
renderSectionHeader={this._renderSectionHeader}
removeClippedSubviews={true}
enableEmptySections={true}
enablePullToRefresh={true}
isOnPullToRefresh={isPullToRefresh}
refreshControl={
<RefreshControl
refreshing={false}
onRefresh={() => {
this.props.onRefresh && this.props.onRefresh();
}}
colors={['#000000', '#ff0000']}
progressBackgroundColor="#ffffff"
/>
}
onEndReached={() => {
this.props.onEndReached && this.props.onEndReached();
}}
renderFooter={this._renderFooter}
/>
}
{
Platform.OS === 'ios' ?
<ListView
ref={(c) => {
this.listView = c;
}}
contentContainerStyle={styles.contentContainer}
dataSource={this.dataSource.cloneWithRowsAndSections(dataSource)}
renderRow={this._renderRow}
renderSectionHeader={this._renderSectionHeader}
removeClippedSubviews={true}
enableEmptySections={true}
enablePullToRefresh={true}
isOnPullToRefresh={isPullToRefresh}
onRefreshData={() => {
this.props.onRefresh && this.props.onRefresh();
}}
onEndReached={() => {
this.props.onEndReached && this.props.onEndReached();
<RecommendPopView
data={recommendData}
isVisible={recommendVisible}
onPress={()=>{
this._autoScrollToProductList();
}}
renderFooter={this._renderFooter}
/>
:
<ListView
ref={(c) => {
this.listView = c;
}}
contentContainerStyle={styles.contentContainer}
dataSource={this.dataSource.cloneWithRowsAndSections(dataSource)}
renderRow={this._renderRow}
renderSectionHeader={this._renderSectionHeader}
removeClippedSubviews={true}
enableEmptySections={true}
enablePullToRefresh={true}
isOnPullToRefresh={isPullToRefresh}
refreshControl={
<RefreshControl
refreshing={false}
onRefresh={() => {
this.props.onRefresh && this.props.onRefresh();
}}
colors={['#000000', '#ff0000']}
progressBackgroundColor="#ffffff"
/>
}
onEndReached={() => {
this.props.onEndReached && this.props.onEndReached();
}}
renderFooter={this._renderFooter}
/>
}
</View>
);
}
... ... @@ -529,5 +552,5 @@ let styles = StyleSheet.create({
height: 44,
marginTop: 15,
marginBottom: 15
}
},
});
... ...
'use strict';
import React, {Component} from 'react';
import ReactNative, {
View,
Text,
Image,
StyleSheet,
Dimensions,
TouchableOpacity,
Platform,
} from 'react-native';
import YH_Image from '../../../common/components/YH_Image';
export default class RecommendPopView extends Component {
constructor(props) {
super(props);
}
render() {
let {data, isVisible} = this.props;
if (!isVisible) {
return null
}
let url = YH_Image.getSlicedUrl(data.get('default_images', ''), 290, 386, 2); // 商品缩略图
return (
<TouchableOpacity
activeOpacity={1}
onPress={()=>{
this.props.onPress&&this.props.onPress();
}}
>
<View style={styles.container}>
<Image
source={require('../../images/recommendBgIcon.png')}
style={styles.bgImage}
/>
<YH_Image style={styles.image} url={url} />
<View style={styles.titleContainer}>
<Text style={styles.title}>
心仪还不赶紧拿下,就是TA了!
</Text>
<Text style={styles.subTitle}>
猜你喜欢
</Text>
</View>
<Image
source={require('../../images/arowTanIcon.png')}
style={styles.downIcon}
/>
</View>
</TouchableOpacity>
)
};
}
let {width, height} = Dimensions.get('window');
const DEVICE_WIDTH_RATIO = width / 375;
let styles = StyleSheet.create({
container: {
position: 'absolute',
left: 0,
bottom: 15*DEVICE_WIDTH_RATIO,
width,
height: 58*DEVICE_WIDTH_RATIO,
flex: 1,
flexDirection: 'row',
backgroundColor: 'transparent',
},
bgImage: {
position: 'absolute',
width,
height: 58*DEVICE_WIDTH_RATIO,
backgroundColor: 'transparent',
},
image: {
marginLeft: 6*DEVICE_WIDTH_RATIO,
width: 34*DEVICE_WIDTH_RATIO,
height: 44*DEVICE_WIDTH_RATIO,
alignSelf: 'center',
backgroundColor: 'red'
},
titleContainer: {
alignSelf: 'center',
backgroundColor: 'transparent',
marginLeft: 6*DEVICE_WIDTH_RATIO,
},
title: {
color: 'white',
fontSize: 16,
},
subTitle: {
color: 'white',
fontSize: 13,
marginTop: 5*DEVICE_WIDTH_RATIO,
},
downIcon: {
alignSelf: 'center',
marginLeft: 80*DEVICE_WIDTH_RATIO,
},
})
... ...
... ... @@ -45,4 +45,10 @@ export default keyMirror({
SHOP_FAV_CANCEL_REQUEST: null,
SHOP_FAV_CANCEL_SUCCESS: null,
SHOP_FAV_CANCEL_FAILURE: null,
HOME_RECOMMEND_PRODUCT_REQUEST: null,
HOME_RECOMMEND_PRODUCT_SUCCESS: null,
HOME_RECOMMEND_PRODUCT_FAILURE: null,
HOME_RECOMMEND_PRODUCT_SELECTED: null,
});
... ...
... ... @@ -69,6 +69,7 @@ class HomeContainer extends Component {
this.onPressShopFavorite = this.onPressShopFavorite.bind(this);
this.onPressTitleMore = this.onPressTitleMore.bind(this);
this.onClickSort = this.onClickSort.bind(this);
this.onClickRecommendProduct = this.onClickRecommendProduct.bind(this);
this.subscription = NativeAppEventEmitter.addListener(
'ChannelDidChangeEvent',
... ... @@ -77,6 +78,35 @@ class HomeContainer extends Component {
this.home && this.home.trigggePullToRefresh();
}
);
this.subscription2 = NativeAppEventEmitter.addListener(
'UserDidLoginEvent',
(reminder) => {
this.home && this.home.trigggePullToRefresh();
}
);
this.subscription3 = NativeAppEventEmitter.addListener(
'UserDidLogoutEvent',
(reminder) => {
this.home && this.home.trigggePullToRefresh();
}
);
this.subscription4 = NativeAppEventEmitter.addListener(
'ABTestDidChangeEvent',
(reminder) => {
this.home && this.home.trigggePullToRefresh();
}
);
this.subscription5 = NativeAppEventEmitter.addListener(
'UserDidRegisterEvent',
(reminder) => {
this.home && this.home.trigggePullToRefresh();
}
);
}
componentDidMount() {
... ... @@ -86,6 +116,11 @@ class HomeContainer extends Component {
componentWillUnmount() {
this.subscription && this.subscription.remove();
this.subscription2 && this.subscription2.remove();
this.subscription3 && this.subscription3.remove();
this.subscription4 && this.subscription4.remove();
this.subscription5 && this.subscription5.remove();
}
onRefresh() {
... ... @@ -191,6 +226,10 @@ class HomeContainer extends Component {
this.props.actions.selecLifeStyleProductIndex(index);
}
onClickRecommendProduct() {
this.props.actions.selectedRecommendProduct();
}
render() {
let {app, home} = this.props;
return (
... ... @@ -222,6 +261,7 @@ class HomeContainer extends Component {
onPressShopFavorite={this.onPressShopFavorite}
onPressTitleMore={this.onPressTitleMore}
onClickSort={this.onClickSort}
onClickRecommendProduct={this.onClickRecommendProduct}
/>
</View>
);
... ...
... ... @@ -45,6 +45,12 @@ const {
SHOP_FAV_CANCEL_SUCCESS,
SHOP_FAV_CANCEL_FAILURE,
HOME_RECOMMEND_PRODUCT_REQUEST,
HOME_RECOMMEND_PRODUCT_SUCCESS,
HOME_RECOMMEND_PRODUCT_FAILURE,
HOME_RECOMMEND_PRODUCT_SELECTED,
} = require('../../constants/actionTypes').default;
/**
... ... @@ -111,6 +117,10 @@ export function fetchFloor() {
}
dispatch(fetchShopInfo(payload, channel));
if (channel == '1' || channel == '2') {
dispatch(getRecommendPop());
}
})
.catch(error => {
dispatch(fetchFloorFailure(error, channelStr));
... ... @@ -540,7 +550,7 @@ export function cancelFavorite(shopId, resultNumber) {
dispatch(cancelFavoritRequest(channelStr));
dispatch(cancelFavoritSuccess(channelStr, resultNumber, shopId, false));
ReactNative.NativeModules.YH_CommonHelper.uid()
.then(uid => {
return new HomeService(app.host).cancelFavorite(shopId, uid)
... ... @@ -557,6 +567,90 @@ export function cancelFavorite(shopId, resultNumber) {
};
}
export function getRecommendPop() {
// body...
return (dispatch, getState) => {
let {app, home} = getState();
let channelStr = channelTransfer.number2String(app.channel);
let currentData = home[channelStr];
if (currentData.recommendProduct.get('isFetching')) {
return;
}
let rec_pos = '';
if (channelStr == 'boy') {
rec_pos = 100010
} else if (channelStr == 'girl') {
rec_pos = 100011
}
dispatch(recommendPopRequest(channelStr));
let fetchRecommendPop = (fromPage, rec_pos) =>{
return new HomeService('http://dev-api.yohops.com:9999').recommendPop(fromPage, rec_pos)
.then(json =>{
let payload = floorParser.parseRecommendPop(json);
dispatch(recommendPopSuccess(payload, channelStr));
})
.catch(error =>{
dispatch(recommendPopFailure(error, channelStr));
});
}
let sourcePage = '';
Promise.all([
ReactNative.NativeModules.YH_CommonHelper.sourcePage('YH_HomeViewController')
]).then(result => {
sourcePage = result[0];
fetchRecommendPop(sourcePage, rec_pos);
})
.catch(error => {
fetchRecommendPop(sourcePage, rec_pos);
});
}
}
export function selectedRecommendProduct() {
return (dispatch, getState) => {
let {app, home} = getState();
let channelStr = channelTransfer.number2String(app.channel);
dispatch(didSelectedRecommendProduct(channelStr))
}
}
function didSelectedRecommendProduct(channelStr) {
return {
type: HOME_RECOMMEND_PRODUCT_SELECTED,
payload: channelStr
}
}
function recommendPopRequest(channelStr){
return {
type: HOME_RECOMMEND_PRODUCT_REQUEST,
payload: channelStr
}
}
function recommendPopSuccess(json, channelStr) {
return {
type: HOME_RECOMMEND_PRODUCT_SUCCESS,
payload: {
json,
channelStr
}
}
}
function recommendPopFailure(error, channelStr) {
return {
type: HOME_RECOMMEND_PRODUCT_FAILURE,
payload: {
error,
channelStr
}
}
}
/******** 首页楼层 *********/
// 开始请求
export function fetchFloorRequest(channelStr) {
... ...
... ... @@ -30,6 +30,14 @@ let cached = Immutable.fromJS({
content_code: ''
});
let recommendProduct = Immutable.fromJS({
isFetching: false,
error: null,
list: List(),
rec_id: '',
isVisible: false,
});
let templet = new (Record({
isFetching: false,
isFirstLoad: true,
... ... @@ -37,6 +45,7 @@ let templet = new (Record({
cached: cached, //缓存
list: List(),
shop: shop,
recommendProduct: recommendProduct,
bottomBanner: bottomBanner,
favorite: favorite, //男, 女,潮童频道 猜你喜欢列表
content_code: '',
... ...
... ... @@ -42,6 +42,12 @@ const {
SHOP_FAV_CANCEL_SUCCESS,
SHOP_FAV_CANCEL_FAILURE,
HOME_RECOMMEND_PRODUCT_REQUEST,
HOME_RECOMMEND_PRODUCT_SUCCESS,
HOME_RECOMMEND_PRODUCT_FAILURE,
HOME_RECOMMEND_PRODUCT_SELECTED,
} = require('../../constants/actionTypes').default;
const initialState = new InitialState;
... ... @@ -164,7 +170,7 @@ export default function homeReducer(state=initialState, action) {
case SHOP_FAV_CANCEL_FAILURE:{
return state;
}
case SHOP_FAV_ADD_SUCCESS:
case SHOP_FAV_CANCEL_SUCCESS:{
let {channelStr, resultNumber, shopId, collected} = action.payload;
... ... @@ -206,6 +212,34 @@ export default function homeReducer(state=initialState, action) {
.setIn([channelStr, 'endReached'], false);
}
}
/****************************
*** 猜你喜欢弹框
*****************************/
case HOME_RECOMMEND_PRODUCT_REQUEST: {
let channelStr = action.payload;
return state.setIn([channelStr, 'recommendProduct', 'isFetching'], true)
setIn([channelStr, 'recommendProduct', 'isVisible'], false);
}
case HOME_RECOMMEND_PRODUCT_SUCCESS: {
let {channelStr, json} = action.payload;
let {list, rec_id} = json;
let isVisible = (list.length > 0) ? true : false;
return state.setIn([channelStr, 'recommendProduct', 'isFetching'], false)
.setIn([channelStr, 'recommendProduct', 'list'], Immutable.fromJS(list))
.setIn([channelStr, 'recommendProduct', 'isVisible'], isVisible)
.setIn([channelStr, 'recommendProduct', 'rec_id'], rec_id);
}
case HOME_RECOMMEND_PRODUCT_FAILURE: {
let {channelStr, error} = action.payload;
return state.setIn([channelStr, 'recommendProduct', 'isFetching'], false)
.setIn([channelStr, 'recommendProduct', 'error'], error)
.setIn([channelStr, 'recommendProduct', 'isVisible'], false);
}
case HOME_RECOMMEND_PRODUCT_SELECTED: {
let channelStr = action.payload;
return state.setIn([channelStr, 'recommendProduct', 'isVisible'], false);
}
}
return state;
... ...
... ... @@ -173,4 +173,21 @@ export default class HomeService {
throw(error);
});
}
async recommendPop(fromPage, rec_pos) {
return await this.api.get({
url: '',
body: {
method: 'app.recommend.popup',
fromPage,
rec_pos
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
}
... ...
... ... @@ -128,6 +128,15 @@ function parseShopInfo(json) {
return payload;
}
function parseRecommendPop(json) {
let product_list = json && json.product_list ? json.product_list : [];
let rec_id = json && json.rec_id ? json.rec_id : '';
return {
list: product_list,
rec_id,
}
}
module.exports = {
homeDataParse,
channelCacheKey,
... ... @@ -137,4 +146,5 @@ module.exports = {
parseLifeStyleFavorite,
parseBottomBanner,
parseShopInfo,
parseRecommendPop
};
... ...