Authored by 陈林

Merge branch 'V6.8.9' into 'master'

V6.8.9



See merge request !29
Showing 75 changed files with 3776 additions and 40 deletions
import React from 'react';
import {} from 'react-native';
import RCTLog from 'RCTLog';
... ...
... ... @@ -22,6 +22,7 @@ import appInitialState from './reducers/app/appInitialState';
import couponInitialState from './reducers/coupon/couponInitialState';
import CouponCenterContainer from './containers/CouponCenterContainer';
import CouponListContainer from './containers/CouponListContainer';
import {
setPlatform,
... ... @@ -54,11 +55,20 @@ export default function native(platform) {
store.dispatch(setContentCode(this.props.contentCode));
}
store.dispatch(setHost(this.props.host));
return (
<Provider store={store}>
<CouponCenterContainer />
</Provider>
);
let type = this.props.type;
if (type == 'couponList') {
return (
<Provider store={store}>
<CouponListContainer />
</Provider>
)
} else {
return (
<Provider store={store}>
<CouponCenterContainer />
</Provider>
);
}
}
});
... ...
import React, { Component } from 'react';
import ReactNative, {
Dimensions,
Image,
SectionList,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
import CouponListCell from './CouponListCell';
let {width, height} = Dimensions.get('window');
const DEVICE_WIDTH_RATIO = width / 375;
export default class CouponList extends Component {
state = { }
render() {
let data = [{title: '', data: this.props.data || []}];
if (!this.props.isFetching && data[0].data.length == 0) {
return (
<View style={styles.emptyContainer}>
<Image source={require('../../images/coupon.png')} style={styles.emptyImage} />
<Text style={styles.couponErrorPageText}>暂无优惠券</Text>
</View>
)
} else {
return (
<SectionList
style={styles.container}
contentContainerStyle={styles.contentContainerStyle}
renderItem={this.renderItem}
sections={data}
onEndReached={this.props.onEndReached}
keyExtractor={(item, index) => '' + index}
/>
);
}
}
renderItem = ({item, index}) => {
return (
<CouponListCell data={item} type={this.props.type} goCouponProductList={() => this.goCouponProductList(item)}/>
)
}
goCouponProductList(item) {
let url = `http://m.yohobuy.com?openby:yohobuy={"action":"go.couponProductList",
"params":{"coupon_id":"${item.coupon_id}","coupon_code":"${item.coupon_code}","title": "优惠活动商品",
"coupon_title":"${item.coupon_name}"}}`;
ReactNative.NativeModules.YH_CommonHelper.jumpWithUrl(url);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f0f0f0',
},
emptyContainer: {
flex: 1,
backgroundColor: '#f0f0f0',
justifyContent: 'center',
alignItems: 'center',
},
contentContainerStyle: {
paddingVertical: 5 * DEVICE_WIDTH_RATIO
},
emptyImage: {
width: 104 * DEVICE_WIDTH_RATIO,
height: 65 * DEVICE_WIDTH_RATIO,
// marginTop: 131 * DEVICE_WIDTH_RATIO,
marginBottom: 15 * DEVICE_WIDTH_RATIO,
},
couponErrorPageText: {
fontSize: 14,
color: '#b0b0b0',
}
})
\ No newline at end of file
... ...
import React, { Component } from 'react';
import {
Dimensions,
Image,
SectionList,
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
let {width, height} = Dimensions.get('window');
const DEVICE_WIDTH_RATIO = width / 375;
export default class CouponListCell extends Component {
state = { }
render() {
let { type } = this.props;
let data = this.props.data || {};
let coupon_value = data && data.coupon_value_str ? data.coupon_value_str : 0;
let coupon_name = data && data.coupon_name ? data.coupon_name : '';
let coupon_validity = data && data.coupon_validity ? data.coupon_validity : '';
let use_rule = data && data.use_rule ? data.use_rule : '';
let notes = data && data.notes ? data.notes : [];
let { catalog, catalog_name, is_online_avail, is_overdue_soon } = data;
let image, color;
if (catalog == '100') {
image = require('../../images/bgyellow.png');
color = '#ffa72e';
} else if (catalog == '200') {
image = require('../../images/bgred.png');
color = '#fc5960';
} else {
image = require('../../images/bgblack.png');
color = '#000000';
}
let arrowImage = this.state.showDetail ? require('../../images/up.png') : require('../../images/down.png');
let grayImage = type == 'use' ? require('../../images/yishiyong.png') : type == 'overtime' ? require('../../images/guoqi.png') : require('../../images/yishiyong.png');
if (type != 'notuse') {
color = '#b0b0b0';
image = require('../../images/bggrey.png');
}
return (
<View style={styles.container}>
<View style={[styles.row, this.state.showDetail && styles.shadow]}>
<View>
<Image source={image} resizeMode="stretch" style={styles.bgImg}/>
<View style={styles.leftAb}>
<Text style={[styles.price, {color}, (data.coupon_value != data.coupon_value_str) && {fontSize: 24 * DEVICE_WIDTH_RATIO}]} numberOfLines={1}>
{coupon_value}
</Text>
{use_rule ? <Text style={[styles.priceDetail, {color}]}>
{use_rule}
</Text> : null}
</View>
</View>
<View style={styles.right}>
<Text style={[styles.titleTip, {color}]} numberOfLines={2} >
[{catalog_name}]
<Text style={[styles.title, type != 'notuse' && {color}]} >
{' '} {coupon_name}
</Text>
</Text>
<Text style={styles.time}>
{coupon_validity}
</Text>
<TouchableOpacity
activeOpacity={1.0}
onPress={() => {
this.setState({showDetail: !this.state.showDetail})
}}>
<View style={styles.bottom}>
<Text style={styles.bottomText}>
使用说明
</Text>
<Image source={arrowImage} style={styles.bottomIcon} resizeMode="contain"/>
</View>
</TouchableOpacity>
{(type == 'notuse' && is_online_avail) ? <TouchableOpacity onPress={this.props.goCouponProductList} style={styles.immediatelyUse}>
<Text style={styles.immediatelyUseText}>立即使用</Text>
</TouchableOpacity> : null}
{type != 'notuse' ? <Image source={grayImage} style={styles.grayImage} /> : null}
{is_overdue_soon ? <Image source={require('../../images/tip.png')} style={styles.overDueSoon} resizeMode="stretch" /> : null}
</View>
</View>
{this.state.showDetail ?
<View style={styles.detail}>
{notes.map((item, i) => {
return (
<View key={i} style={styles.detailTextView}>
<Text style={styles.detailText} numberOfLines={2}>
{item}
</Text>
</View>
)
})}
</View>
: null
}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
marginVertical: 5 * DEVICE_WIDTH_RATIO,
marginHorizontal: 10 * DEVICE_WIDTH_RATIO,
},
row: {
flexDirection: 'row',
zIndex: 1,
},
bgImg: {
width: 110 * DEVICE_WIDTH_RATIO,
height: 100 * DEVICE_WIDTH_RATIO,
},
right: {
width: 245 * DEVICE_WIDTH_RATIO,
height: 100 * DEVICE_WIDTH_RATIO,
backgroundColor: '#ffffff',
borderTopRightRadius: 4 * DEVICE_WIDTH_RATIO,
borderBottomRightRadius: 4 * DEVICE_WIDTH_RATIO,
},
price: {
fontSize: 30 * DEVICE_WIDTH_RATIO,
color: '#002B47',
letterSpacing: 0,
textAlign: 'center',
fontWeight: 'bold',
},
priceDetail: {
fontFamily: 'PingFang-SC-Regular',
fontSize: 12*DEVICE_WIDTH_RATIO,
color: '#002B47',
letterSpacing: 0,
textAlign: 'center',
},
leftAb: {
position: 'absolute',
top: 0,
left: 8*DEVICE_WIDTH_RATIO,
right: 5*DEVICE_WIDTH_RATIO,
bottom: 0,
justifyContent: 'center',
alignItems: 'center',
},
titleTip: {
fontFamily: 'PingFang-SC-Medium',
fontSize: 12*DEVICE_WIDTH_RATIO,
color: '#002B47',
letterSpacing: 0,
marginLeft: 10*DEVICE_WIDTH_RATIO,
marginTop: 10*DEVICE_WIDTH_RATIO,
height: 35*DEVICE_WIDTH_RATIO,
maxWidth: 185*DEVICE_WIDTH_RATIO,
fontWeight: 'bold'
},
title: {
fontFamily: 'PingFang-SC-Regular',
fontSize: 12*DEVICE_WIDTH_RATIO,
color: '#444444',
fontWeight: 'normal',
letterSpacing: 0,
lineHeight: 15*DEVICE_WIDTH_RATIO,
},
time: {
marginLeft: 10*DEVICE_WIDTH_RATIO,
fontFamily: 'PingFang-SC-Regular',
fontSize: 11*DEVICE_WIDTH_RATIO,
color: '#b0b0b0',
letterSpacing: 0,
},
bottom: {
marginTop: 5*DEVICE_WIDTH_RATIO,
height: 35*DEVICE_WIDTH_RATIO,
flexDirection: 'row',
marginLeft: 10*DEVICE_WIDTH_RATIO,
alignItems: 'center',
},
bottomText: {
fontFamily: 'PingFang-SC-Regular',
fontSize: 11*DEVICE_WIDTH_RATIO,
color: '#B0B0B0',
letterSpacing: 0,
},
bottomIcon: {
width: 10*DEVICE_WIDTH_RATIO,
height: 10*DEVICE_WIDTH_RATIO,
marginLeft: 5*DEVICE_WIDTH_RATIO,
},
immediatelyUse: {
position: 'absolute',
right: 10*DEVICE_WIDTH_RATIO,
bottom: 10*DEVICE_WIDTH_RATIO,
width: 65*DEVICE_WIDTH_RATIO,
height: 25*DEVICE_WIDTH_RATIO,
borderWidth: 1,
borderColor: '#444444',
borderRadius: 12.5*DEVICE_WIDTH_RATIO,
justifyContent: 'center',
alignItems: 'center',
},
immediatelyUseText: {
fontSize: 10*DEVICE_WIDTH_RATIO,
color: '#444444',
},
grayImage: {
position: 'absolute',
top: 21.5*DEVICE_WIDTH_RATIO,
right: 10*DEVICE_WIDTH_RATIO,
width: 63*DEVICE_WIDTH_RATIO,
height: 57*DEVICE_WIDTH_RATIO,
},
detail: {
width: 355*DEVICE_WIDTH_RATIO,
marginTop: -6*DEVICE_WIDTH_RATIO,
backgroundColor: 'rgba(255,255,255,0.7)',
paddingTop: 18*DEVICE_WIDTH_RATIO,
paddingBottom: 11*DEVICE_WIDTH_RATIO,
paddingHorizontal: 11*DEVICE_WIDTH_RATIO,
zIndex: 0,
},
detailTextView: {
flexDirection: 'row',
},
detailText: {
fontFamily: 'PingFang-SC-Regular',
fontSize: 11*DEVICE_WIDTH_RATIO,
color: '#444444',
letterSpacing: 0,
},
shadow: {
shadowColor: 'rgba(0,0,0,0.1)',
shadowOffset: {width: 0, height: 3},
shadowOpacity: 0.5,
shadowRadius: 5,
elevation: 1,
},
overDueSoon: {
position: 'absolute',
top: 0,
right: 0,
width: 42*DEVICE_WIDTH_RATIO,
height: 42*DEVICE_WIDTH_RATIO,
}
})
\ No newline at end of file
... ...
'use strict';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native';
export default class CouponTabs extends Component {
static propType = {
goToPage: PropTypes.func,
activeTab: PropTypes.number,
tabs: PropTypes.array,
tabNames: PropTypes.array,
tabIconNames: PropTypes.array,
selectedTabIconNames: PropTypes.array
};
render() {
const { activeTab } = this.props;
let image = this.props.showFilter ? require('../../images/blackupa.png') : require('../../images/blackdwona.png');
return (
<View style={styles.tabs}>
{this.props.tabs.map((tab, i) => {
return (
<TouchableOpacity key={i} activeOpacity={0.8} style={styles.tab} onPress={() => this._goToPage(i)}>
<View style={[styles.tabRow, i > 0 && styles.tabBorder]}>
<Text style={[styles.tabText, activeTab == i && styles.activeTabText]}>{tab}{this.renderNums(i)}</Text>
{i == 0 && <Image source={activeTab == i ? image : require('../../images/down.png')} style={styles.image} resizeMode="stretch" />}
</View>
</TouchableOpacity>
)
})}
</View>
);
}
renderNums(i) {
const { couponNums } = this.props;
let nums;
if (!couponNums) {
return
} else {
switch(i) {
case 0: {
nums = couponNums.notuse;
break;
}
case 1: {
nums = couponNums.use;
break;
}
case 2: {
nums = couponNums.overtime;
break;
}
}
}
if (nums) {
return ` (${nums})`;
}
}
_goToPage(i) {
let shouldGo = this.props.selectTab(i);
if (shouldGo) {
this.props.goToPage(i);
}
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ffffff',
marginTop: 20
},
tabs: {
flexDirection: 'row',
height: 45,
borderBottomColor: '#E0E0E0',
borderBottomWidth: 1
},
tab: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
tabRow: {
flex: 1,
height: 30,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
tabBorder: {
borderLeftWidth: 1,
borderColor: '#e0e0e0',
},
tabText: {
fontSize: 14,
color: '#b0b0b0',
},
activeTabText: {
color: '#444444'
},
icon: {
width: 24,
height: 38,
},
image: {
width: 9,
height: 6,
marginLeft: 5,
}
});
\ No newline at end of file
... ...
... ... @@ -13,11 +13,16 @@ export default keyMirror({
GET_COUPON_REQUEST: null,
GET_COUPON_SUCCESS: null,
GET_COUPON_FAILURE: null,
GET_COUPON_FAILURE: null,
GET_COUPONLIST_SUCCESS: null,
GET_COUPONNUMS_SUCCESS: null,
JUMP_WITH_URL: null,
HIDE_SUCCESS_PROMPT: null,
HIDE_NET_ERROR_PROMPT: null,
SHOW_PROMPT_TIP: null,
GET_COUPONLIST_REQUEST: null,
GET_COUPONLIST_FAILURE: null,
});
... ...
import React, { Component } from 'react';
import {
StyleSheet,
Text,
TextInput,
TouchableOpacity,
View,
} from 'react-native';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import ScrollableTabView from 'react-native-scrollable-tab-view';
import {Map} from 'immutable';
import * as couponActions from '../reducers/coupon/couponActions';
import CouponList from '../components/coupon/CouponList';
import CouponTabs from '../components/coupon/CouponTabs';
import Prompt from '../components/coupon/Prompt';
const actions = [
couponActions,
];
function mapStateToProps(state) {
return {
...state
};
}
function mapDispatchToProps(dispatch) {
const creators = Map()
.merge(...actions)
.filter(value => typeof value === 'function')
.toObject();
return {
actions: bindActionCreators(creators, dispatch),
dispatch
};
}
class CouponListContainer extends Component {
constructor(props) {
super(props);
props.actions.getCouponNums();
props.actions.getCouponList('notuse', true);
props.actions.getCouponList('use', true);
props.actions.getCouponList('overtime', true);
}
state = {couponCode: '', selectedTab: 0, showFilter: false, selectedFilter: 0}
render() {
const { showFilter, selectedFilter } = this.state;
const { couponNums, notuse, use, overtime, showSuccessTip } = this.props.coupon;
return (
<View style={styles.container}>
<ScrollableTabView
onChangeTab={this.onChangeTab}
renderTabBar={() => <CouponTabs couponNums={couponNums} selectTab={this.selectTab} showFilter={this.state.showFilter}/>}
>
<View style={styles.container} tabLabel="未使用">
<View style={styles.bindCouponContainer}>
<TextInput
style={styles.textInput}
onChangeText={text => this.setState({couponCode: text})}
placeholder="请输入优惠券码"
underlineColorAndroid="transparent"
/>
<TouchableOpacity
onPress={this.bindCoupon}
style={[styles.bindCouponBtn, this.state.couponCode.length > 0 && styles.blackBack]}
>
<Text style={styles.bindCouponText}>兑换</Text>
</TouchableOpacity>
</View>
<CouponList isFetching={notuse.isFetching} data={notuse.list} onEndReached={() => this.props.actions.getCouponList('notuse')} type="notuse"/>
</View>
<View style={styles.container} tabLabel="已使用">
<CouponList isFetching={use.isFetching} data={use.list} onEndReached={() => this.props.actions.getCouponList('use')} type="use"/>
</View>
<View style={styles.container} tabLabel="已失效">
<CouponList isFetching={overtime.isFetching} data={overtime.list} onEndReached={() => this.props.actions.getCouponList('overtime')} type="overtime"/>
</View>
</ScrollableTabView>
{showSuccessTip ? <Prompt
text={showSuccessTip}
duration={800}
onPromptHidden={this._onPromptHidden}
/> : null}
{showFilter ? <View style={styles.filterContianer}>
<View style={styles.filterTabs}>
{notuse.filters && notuse.filters.map((item, index) => {
return (
<TouchableOpacity
activeOpacity={0.9}
key={index}
style={[styles.filterTab, (selectedFilter == item.filter_id) && styles.selectedFilterTab]}
onPress={() => this.pressFilter(item.filter_id)}>
<Text style={[styles.filterText, (selectedFilter == item.filter_id) && styles.selectedFilterText]}>{item.filter_name}</Text>
</TouchableOpacity>
)
})}
</View>
<TouchableOpacity style={styles.container} onPress={() => this.setState({showFilter: false})} />
</View> : null}
</View>
)
}
selectTab = i => {
if (i == 0 && this.state.selectedTab == 0) {
this.setState({showFilter: !this.state.showFilter});
return false;
} else {
return true;
}
}
onChangeTab = tab => {
const { i } = tab;
let { showFilter } = this.state;
if (i != 0) {
showFilter = false;
}
this.setState({selectedTab: i, showFilter});
}
bindCoupon = () => {
this.props.actions.bindCoupon(this.state.couponCode);
}
pressFilter(filter_id) {
if (this.state.selectedFilter == filter_id) {
return
}
this.props.actions.getCouponList('notuse', true, filter_id);
this.setState({selectedFilter: filter_id, showFilter: false});
}
_onPromptHidden = () => {
this.props.actions.promptHidden();
}
_onNetPromptHidden() {
this.props.actions.netPromptHidden();
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
bindCouponContainer: {
height: 45,
paddingHorizontal: 10,
paddingVertical: 8,
flexDirection: 'row',
},
textInput: {
flex: 1,
height: 30,
marginRight: 10,
fontSize: 12,
padding: 0,
paddingLeft: 10,
backgroundColor: '#f0f0f0',
borderRadius: 2,
},
bindCouponBtn: {
width: 60,
height: 30,
backgroundColor: '#b0b0b0',
borderRadius: 2,
justifyContent: 'center',
alignItems: 'center',
},
blackBack: {
backgroundColor: '#444444',
},
bindCouponText: {
fontSize: 14,
color: '#ffffff',
},
filterContianer: {
position: 'absolute',
top: 45,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.4)'
},
filterTabs: {
height: 65,
flexDirection: 'row',
justifyContent: 'space-around',
alignItems: 'center',
backgroundColor: '#ffffff'
},
filterTab: {
width: 73,
height: 33,
borderWidth: 1,
borderColor: '#e0e0e0',
borderRadius: 2,
justifyContent: 'center',
alignItems: 'center',
},
selectedFilterTab: {
backgroundColor: '#444444'
},
filterText: {
fontSize: 14,
color: '#444444',
},
selectedFilterText: {
color: '#ffffff',
}
})
export default connect(mapStateToProps, mapDispatchToProps)(CouponListContainer);
\ No newline at end of file
... ...
... ... @@ -16,6 +16,11 @@ const {
JUMP_WITH_URL,
HIDE_SUCCESS_PROMPT,
HIDE_NET_ERROR_PROMPT,
GET_COUPONLIST_SUCCESS,
GET_COUPONNUMS_SUCCESS,
SHOW_PROMPT_TIP,
GET_COUPONLIST_REQUEST,
GET_COUPONLIST_FAILURE,
} = require('../../constants/actionTypes').default;
export function setContentCode(code) {
... ... @@ -367,3 +372,101 @@ export function netPromptHidden() {
type: HIDE_NET_ERROR_PROMPT,
};
}
export function getCouponListSuccess(payload) {
return {
type: GET_COUPONLIST_SUCCESS,
payload,
}
}
export function getCouponListRequest(payload) {
return {
type: GET_COUPONLIST_REQUEST,
payload,
}
}
export function getCouponListFailure(payload) {
return {
type: GET_COUPONLIST_FAILURE,
payload,
}
}
export function getCouponList(type, init, filter) {
return (dispatch, getState) => {
let {app, coupon} = getState();
let couponData = (coupon.toJS())[type] || {};
if (!init && (couponData.isFetching || couponData.reachedEnd)) return
dispatch(getCouponListRequest(type))
if (init) {
couponData.page = 1;
} else {
couponData.page += 1;
}
if (filter != null && filter != undefined) {
couponData.filter = filter;
}
return new CouponService(app.host).getCouponList(couponData)
.then(data => {
let length = couponData.list.length;
if (init) {
couponData.list = data.couponList;
} else {
couponData.list = [...couponData.list, ...data.couponList];
}
if (couponData.list.length == length) {
couponData.reachedEnd = true;
couponData.page -= 1;
}
couponData.filters = data.filters;
couponData.isFetching = false;
dispatch(getCouponListSuccess(couponData));
})
.catch(e => {
dispatch(getCouponListFailure(e));
})
}
}
export function getCouponNumsSuccess(payload) {
return {
type: GET_COUPONNUMS_SUCCESS,
payload,
}
}
export function getCouponNums() {
return (dispatch, getState) => {
let {app} = getState();
return new CouponService(app.host).getCouponNums()
.then(data => {
dispatch(getCouponNumsSuccess(data));
})
.catch(e => {
dispatch(getCouponListFailure(e));
})
}
}
export function bindCoupon(coupon_code) {
return (dispatch, getState) => {
let {app} = getState();
return new CouponService(app.host).bindCoupon({coupon_code})
.then(data => {
dispatch(showPrompt(data.message || '兑换成功'));
dispatch(getCouponList('notuse', true));
})
.catch(e => {
dispatch(showPrompt(e.message || '兑换失败'));
})
}
}
export function showPrompt(payload) {
return {
type: SHOW_PROMPT_TIP,
payload,
}
}
\ No newline at end of file
... ...
... ... @@ -10,6 +10,34 @@ let InitialState = Record({
floors: List(),
showSuccessTip: false,
showNetErrorTip: false,
notuse: new (Record({
type: 'notuse',
isFetching: false,
reachedEnd: false,
list: [],
filter: 0,
page: 0,
limit: 10,
})),
use: new (Record({
type: 'use',
isFetching: false,
reachedEnd: false,
list: [],
filter: 0,
page: 0,
limit: 10,
})),
overtime: new (Record({
type: 'overtime',
isFetching: false,
reachedEnd: false,
list: [],
filter: 0,
page: 0,
limit: 10,
})),
couponNums: null,
});
export default InitialState;
... ...
... ... @@ -15,6 +15,11 @@ const {
JUMP_WITH_URL,
HIDE_SUCCESS_PROMPT,
HIDE_NET_ERROR_PROMPT,
GET_COUPONLIST_SUCCESS,
GET_COUPONNUMS_SUCCESS,
SHOW_PROMPT_TIP,
GET_COUPONLIST_REQUEST,
GET_COUPONLIST_FAILURE,
} = require('../../constants/actionTypes').default;
const initialState = new InitialState;
... ... @@ -73,6 +78,25 @@ export default function couponReducer(state=initialState, action) {
case HIDE_NET_ERROR_PROMPT: {
return state.set('showNetErrorTip', false);
}
case GET_COUPONLIST_SUCCESS: {
let data = action.payload || {};
return state.set(data.type, new (Immutable.Record(data)));
}
case GET_COUPONNUMS_SUCCESS: {
return state.set('couponNums', action.payload);
}
case SHOW_PROMPT_TIP: {
return state.set('showSuccessTip', action.payload);
}
case GET_COUPONLIST_REQUEST: {
return state.setIn([action.payload, 'isFetching'], true);
}
case GET_COUPONLIST_FAILURE: {
return state.set('error', action.payload);
}
}
return state;
... ...
... ... @@ -64,4 +64,53 @@ export default class CouponService {
});
}
async getCouponList(data) {
let params = {...data};
delete params.list;
delete params.filters;
return await this.api.get({
url: '',
body: {
method: 'app.coupons.get',
...params
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
async getCouponNums() {
return await this.api.get({
url: '',
body: {
method: 'app.coupons.getCouponNums',
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
async bindCoupon(params) {
return await this.api.get({
url: '',
body: {
method: 'app.coupons.bindPCoupon',
...params
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
}
... ...
... ... @@ -37,19 +37,46 @@ export default class ShareViewModal extends React.Component {
<View style={styles.topView}/>
</TouchableWithoutFeedback>
<View style={styles.shareView}>
<View style={styles.buttonViews}>
<View style={styles.buttons}>
<TouchableOpacity activeOpacity={0.5} style={styles.button1} onPress={() => {
this.props.shareMiniApp && this.props.shareMiniApp();
}}>
<Image source={unionType ? require('../images/unionShare_wx_icon.png') : require('../images/weixin.png')} style={styles.icon} />
<Text style={styles.text}>分享给好友</Text>
<Text style={styles.text}>微信好友</Text>
</TouchableOpacity>
<TouchableOpacity activeOpacity={0.5} style={styles.button2} onPress={() => {
<TouchableOpacity activeOpacity={0.5} style={styles.button1} onPress={() => {
this.props.shareQQApp && this.props.shareQQApp();
}}>
<Image source={unionType ? require('../images/unionShare_qq_icon.png') : require('../images/share_qq_icon.png')} style={styles.icon} />
<Text style={styles.text}>QQ好友</Text>
</TouchableOpacity>
<TouchableOpacity activeOpacity={0.5} style={styles.button1} onPress={() => {
this.props.shareWXSceneTimeline && this.props.shareWXSceneTimeline();
}}>
<Image source={unionType ? require('../images/unionShare_timeline_icon.png') : require('../images/share_timeline_icon.png')} style={styles.icon} />
<Text style={styles.text}>微信朋友圈</Text>
</TouchableOpacity>
<TouchableOpacity activeOpacity={0.5} style={styles.button1} onPress={() => {
this.props.shareWeiBoApp && this.props.shareWeiBoApp();
}}>
<Image source={unionType ? require('../images/unionShare_sina_icon.png') : require('../images/share_sina_icon.png')} style={styles.icon} />
<Text style={styles.text}>新浪微博</Text>
</TouchableOpacity>
</View>
<View style={styles.buttons}>
<TouchableOpacity activeOpacity={0.5} style={styles.button1} onPress={() => {
this.props.showSnapshootShare && this.props.showSnapshootShare(true);
}}>
<Image source={unionType ? require('../images/unionShare_qr_icon.png') : require('../images/pengyou.png')} style={styles.icon} />
<Text style={styles.text}>微信快照</Text>
<Text style={styles.text}>生成海报</Text>
</TouchableOpacity>
<View style={styles.button1}/>
<View style={styles.button1}/>
<View style={styles.button1}/>
</View>
</View>
<View style={styles.line}/>
<TouchableOpacity activeOpacity={0.5} style={styles.cancel} onPress={() => {
... ... @@ -80,7 +107,7 @@ let styles = StyleSheet.create({
},
shareView: {
width,
height: 171*DEVICE_WIDTH_RATIO,
height: 256*DEVICE_WIDTH_RATIO,
backgroundColor: 'white',
alignItems: 'center',
},
... ... @@ -89,21 +116,22 @@ let styles = StyleSheet.create({
height: 88*DEVICE_WIDTH_RATIO,
marginTop: 25*DEVICE_WIDTH_RATIO,
},
buttons: {
buttonViews: {
width,
height: 115*DEVICE_WIDTH_RATIO,
alignItems: 'center',
flexDirection: 'row',
height: 200*DEVICE_WIDTH_RATIO,
},
buttons: {
width,
height: 100*DEVICE_WIDTH_RATIO,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'row',
},
button1: {
marginLeft: (width - 180*DEVICE_WIDTH_RATIO)/3,
width: 90*DEVICE_WIDTH_RATIO,
alignItems: 'center',
},
button2: {
marginLeft: (width - 180*DEVICE_WIDTH_RATIO)/3,
alignItems: 'center',
},
icon: {
width: 45*DEVICE_WIDTH_RATIO,
height: 45*DEVICE_WIDTH_RATIO,
... ...
'use strict';
import React, {Component} from 'react';
import ReactNative, {Dimensions, NativeAppEventEmitter, Platform, StyleSheet, View} from 'react-native'
import ReactNative, {Dimensions, NativeAppEventEmitter, InteractionManager, StyleSheet, View} from 'react-native'
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
... ... @@ -48,6 +48,9 @@ class GroupPurchaseContainer extends Component {
this._didTouchProduct = this._didTouchProduct.bind(this);
this.showShareView = this.showShareView.bind(this);
this.shareMiniApp = this.shareMiniApp.bind(this);
this.shareWeiBoApp = this.shareWeiBoApp.bind(this);
this.shareQQApp = this.shareQQApp.bind(this);
this.shareWXSceneTimeline = this.shareWXSceneTimeline.bind(this);
this.showSnapshootShare = this.showSnapshootShare.bind(this);
this.shareSnapshootAction = this.shareSnapshootAction.bind(this);
this.jumpToMinePurchaseOrder = this.jumpToMinePurchaseOrder.bind(this);
... ... @@ -103,6 +106,7 @@ class GroupPurchaseContainer extends Component {
shareType,
imageUrl: url,
fromPage,
shareCotentType: 'image',
}
ReactNative.NativeModules.YH_CommonHelper.shareImageToWXMini(param);
... ... @@ -148,6 +152,87 @@ class GroupPurchaseContainer extends Component {
ReactNative.NativeModules.YH_CommonHelper.shareWXMiniProgram(param);
}
shareWXSceneTimeline() {
this.props.actions.showShareView(false);
let fromPage = 'GroupPurchase';
let {
activityId,
shareCodeInfo,
groupListParams,
} = this.props.groupPurchase;
let { unionType } = this.props.app;
let bigImage = shareCodeInfo.get('bigImage');
let productIcon = bigImage ? getSlicedUrl(bigImage,150*DEVICE_WIDTH_RATIO, 120*DEVICE_WIDTH_RATIO, 2) : '';
let shareUrl = `https://m.yohobuy.com/activity/group/list?union_type=${unionType}`;
shareUrl = Helper.joinWxProgramPath(shareUrl,groupListParams)
InteractionManager.runAfterInteractions(()=>{
let param = {
shareCotentType: 'mini',
shareType: 'WXSceneTimeline',
imageUrl: productIcon,
title: shareCodeInfo.get('title'),
shareUrl,
fromPage,
}
ReactNative.NativeModules.YH_CommonHelper.shareImageToWXMini(param);
});
}
shareWeiBoApp() {
this.props.actions.showShareView(false);
let {
activityId,
shareCodeInfo,
groupListParams,
} = this.props.groupPurchase;
let { unionType } = this.props.app;
let bigImage = shareCodeInfo.get('bigImage');
let productIcon = bigImage ? getSlicedUrl(bigImage,150*DEVICE_WIDTH_RATIO, 120*DEVICE_WIDTH_RATIO, 2) : '';
let shareUrl = `https://m.yohobuy.com/activity/group/list?union_type=${unionType}`;
shareUrl = Helper.joinWxProgramPath(shareUrl,groupListParams)
InteractionManager.runAfterInteractions(()=>{
let param = {
title: shareCodeInfo.get('title'),
shareImageUrl: productIcon,
shareUrl,
content: shareCodeInfo.get('title'),
}
ReactNative.NativeModules.YH_CommonHelper.shareToSina(param);
});
}
shareQQApp() {
this.props.actions.showShareView(false);
let {
activityId,
shareCodeInfo,
groupListParams,
} = this.props.groupPurchase;
let { unionType } = this.props.app;
let bigImage = shareCodeInfo.get('bigImage');
let productIcon = bigImage ? getSlicedUrl(bigImage,150*DEVICE_WIDTH_RATIO, 120*DEVICE_WIDTH_RATIO, 2) : '';
let shareUrl = `https://m.yohobuy.com/activity/group/list?union_type=${unionType}`;
shareUrl = Helper.joinWxProgramPath(shareUrl,groupListParams)
InteractionManager.runAfterInteractions(()=>{
let param = {
title: shareCodeInfo.get('title'),
shareImageUrl: productIcon,
content: '',
shareUrl,
}
ReactNative.NativeModules.YH_CommonHelper.sendQQChat(param);
});
}
showSnapshootShare(show){
this.props.actions.showShareView(false);
this.props.actions.showSnapshootShare(show);
... ... @@ -184,7 +269,7 @@ class GroupPurchaseContainer extends Component {
return (
<View style={styles.container}>
<ShareViewModal show={showShareView} unionType={unionType} showShareView={this.showShareView} shareMiniApp={this.shareMiniApp} showSnapshootShare={this.showSnapshootShare}/>
<ShareViewModal show={showShareView} unionType={unionType} showShareView={this.showShareView} shareMiniApp={this.shareMiniApp} shareWeiBoApp={this.shareWeiBoApp} shareQQApp={this.shareQQApp} shareWXSceneTimeline={this.shareWXSceneTimeline} showSnapshootShare={this.showSnapshootShare}/>
<ListSnapshootShare
show={showSnapshootShare}
unionType={unionType}
... ...
'use strict';
import React, {Component} from 'react';
import ReactNative, {Platform, StyleSheet, View, Dimensions, NativeAppEventEmitter} from 'react-native'
import ReactNative, {InteractionManager, StyleSheet, View, Dimensions, NativeAppEventEmitter} from 'react-native'
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
... ... @@ -49,6 +49,9 @@ class GroupPurchaseDetailContainer extends Component {
this.didTouchButton = this.didTouchButton.bind(this);
this.showShareView = this.showShareView.bind(this);
this.shareMiniApp = this.shareMiniApp.bind(this);
this.shareWeiBoApp = this.shareWeiBoApp.bind(this);
this.shareQQApp = this.shareQQApp.bind(this);
this.shareWXSceneTimeline = this.shareWXSceneTimeline.bind(this);
this.showSnapshootShare = this.showSnapshootShare.bind(this);
this.shareSnapshootAction = this.shareSnapshootAction.bind(this);
... ... @@ -111,6 +114,7 @@ class GroupPurchaseDetailContainer extends Component {
shareType,
imageUrl: url,
fromPage,
shareCotentType: 'image',
}
ReactNative.NativeModules.YH_CommonHelper.shareImageToWXMini(param);
... ... @@ -184,6 +188,117 @@ class GroupPurchaseDetailContainer extends Component {
ReactNative.NativeModules.YH_CommonHelper.shareWXMiniProgram(param);
}
shareWXSceneTimeline() {
this.props.actions.showShareView(false);
let {
activityId,
groupNo,
groupDetail,
} = this.props.groupPurchaseDetail;
let { unionType } = this.props.app;
let productDetail = groupDetail ? groupDetail.toJS():null;
if(!productDetail){
return;
}
let membershipItems = productDetail.membershipItems;
let resource = membershipItems ? membershipItems[0]: null;
let lackNum = productDetail ? productDetail.lackNum : 0;
if (!resource) {
return;
}
let productIcon = resource.productIcon ? getSlicedUrl(resource.productIcon,150*DEVICE_WIDTH_RATIO, 120*DEVICE_WIDTH_RATIO, 2) : '';
let productGroupPrice = resource.productGroupPrice;
let productName = resource.productName;
let title = '【还差' + lackNum + '人】' + productGroupPrice + '拼' + productName;
let shareUrl = `https://m.yohobuy.com/activity/group/progress?groupNo=${groupNo}&activityId=${activityId}&union_type=${unionType}`;
InteractionManager.runAfterInteractions(()=>{
let param = {
shareCotentType: 'mini',
shareType: 'WXSceneTimeline',
imageUrl: productIcon,
title,
shareUrl,
}
ReactNative.NativeModules.YH_CommonHelper.shareImageToWXMini(param);
});
}
shareWeiBoApp() {
this.props.actions.showShareView(false);
let {
activityId,
groupNo,
groupDetail,
} = this.props.groupPurchaseDetail;
let { unionType } = this.props.app;
let productDetail = groupDetail ? groupDetail.toJS():null;
if(!productDetail){
return;
}
let membershipItems = productDetail.membershipItems;
let resource = membershipItems ? membershipItems[0]: null;
let lackNum = productDetail ? productDetail.lackNum : 0;
if (!resource) {
return;
}
let productIcon = resource.productIcon ? getSlicedUrl(resource.productIcon,150*DEVICE_WIDTH_RATIO, 120*DEVICE_WIDTH_RATIO, 2) : '';
let productGroupPrice = resource.productGroupPrice;
let productName = resource.productName;
let title = '【还差' + lackNum + '人】' + productGroupPrice + '拼' + productName;
let shareUrl = `https://m.yohobuy.com/activity/group/progress?groupNo=${groupNo}&activityId=${activityId}&union_type=${unionType}`;
InteractionManager.runAfterInteractions(()=>{
let param = {
title,
shareImageUrl: productIcon,
content: title,
shareUrl,
}
ReactNative.NativeModules.YH_CommonHelper.shareToSina(param);
});
}
shareQQApp() {
this.props.actions.showShareView(false);
let {
activityId,
groupNo,
groupDetail,
} = this.props.groupPurchaseDetail;
let { unionType } = this.props.app;
let productDetail = groupDetail ? groupDetail.toJS():null;
if(!productDetail){
return;
}
let membershipItems = productDetail.membershipItems;
let resource = membershipItems ? membershipItems[0]: null;
let lackNum = productDetail ? productDetail.lackNum : 0;
if (!resource) {
return;
}
let productIcon = resource.productIcon ? getSlicedUrl(resource.productIcon,150*DEVICE_WIDTH_RATIO, 120*DEVICE_WIDTH_RATIO, 2) : '';
let productGroupPrice = resource.productGroupPrice;
let productName = resource.productName;
let title = '【还差' + lackNum + '人】' + productGroupPrice + '拼' + productName;
let shareUrl = `https://m.yohobuy.com/activity/group/progress?groupNo=${groupNo}&activityId=${activityId}&union_type=${unionType}`;
InteractionManager.runAfterInteractions(()=>{
let param = {
title,
shareImageUrl: productIcon,
content: '',
shareUrl,
}
ReactNative.NativeModules.YH_CommonHelper.sendQQChat(param);
});
}
showSnapshootShare(show){
this.props.actions.showShareView(false);
this.props.actions.showSnapshootShare(show);
... ... @@ -218,7 +333,7 @@ class GroupPurchaseDetailContainer extends Component {
return (
<View style={styles.container}>
<ShareViewModal show={showShareView} unionType={unionType} showShareView={this.showShareView} shareMiniApp={this.shareMiniApp} showSnapshootShare={this.showSnapshootShare}/>
<ShareViewModal show={showShareView} unionType={unionType} showShareView={this.showShareView} shareMiniApp={this.shareMiniApp} shareWeiBoApp={this.shareWeiBoApp} shareWXSceneTimeline={this.shareWXSceneTimeline} shareQQApp={this.shareQQApp} showSnapshootShare={this.showSnapshootShare}/>
<SnapshootShare
show={showSnapshootShare}
unionType={unionType}
... ...
... ... @@ -18,14 +18,18 @@ import {Record, List, Map} from 'immutable';
import appInitialState from './reducers/app/appInitialState';
import messageInitialState from './reducers/message/messageInitialState';
import listInitialState from './reducers/list/listInitialState';
import contentInitialState from './reducers/content/contentInitialState';
import MessageContainer from './containers/MessageContainer';
import MessageListContainer from './containers/MessageListContainer';
import ContentMessageContainer from './containers/ContentMessageContainer';
import ContentListContainer from './containers/ContentListContainer';
import {
setPlatform,
setHost,
setServiceHost,
setCommunityHost,
} from './reducers/app/appActions';
import {
... ... @@ -33,12 +37,19 @@ import {
setCategoryName,
} from './reducers/list/listActions';
import {
setContentListId,
setContentCategoryName,
setContentTipFlag
} from './reducers/content/contentActions';
function getInitialState() {
const _initState = {
app: (new appInitialState()),
message: (new messageInitialState()),
list: (new listInitialState()),
content: (new contentInitialState()),
};
return _initState;
}
... ... @@ -48,10 +59,11 @@ export default function native(platform) {
let YH_Message = createReactClass({
render() {
const store = configureStore(getInitialState());
const store = configureStore(getInitialState());
store.dispatch(setPlatform(platform));
store.dispatch(setHost(this.props.host));
store.dispatch(setServiceHost(this.props.serviceHost));
store.dispatch(setCommunityHost(this.props.communityHost));
let type = this.props.type;
if (type == 'main') {
... ... @@ -68,6 +80,21 @@ export default function native(platform) {
<MessageListContainer />
</Provider>
);
} else if (type == 'grassMain') {
store.dispatch(setContentListId(this.props.listId));
store.dispatch(setContentTipFlag(this.props.tipFlag));
return (
<Provider store={store}>
<ContentMessageContainer />
</Provider>
);
} else if (type == 'grassList') {
store.dispatch(setContentListId(this.props.listId));
return (
<Provider store={store}>
<ContentListContainer />
</Provider>
);
}
return null;
}
... ...
/**
* Created by zzz on 2019/3/12.
*/
'use strict';
import React, {Component} from 'react';
import ReactNative, {
View,
Text,
Image,
StyleSheet,
Dimensions,
TouchableOpacity,
Platform,
} from 'react-native';
export default class ContentEmptyView extends Component {
constructor(props) {
super(props);
this.emptyText = this.emptyText.bind(this);
}
emptyText(listId) {
var text = '暂无更新的消息';
switch (listId) {
case '1':
text = '暂无更新的消息';
break;
case '2':
text = '暂无更新的消息';
break;
case '3':
text = '暂无更新的消息';
break;
default:
}
return (
text
)
}
render() {
let {listId} = this.props;
return(
<View style={styles.container}>
<Image
style={styles.iconStyle}
source={require('../../images/message_empty_ic.png')}
/>
<Text style={styles.textStyle}>
{this.emptyText(listId)}
</Text>
</View>
);
};
}
let {width, height} = Dimensions.get('window');
let scale = (width/375.0);
let styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
},
iconStyle: {
width: Math.ceil(96*scale),
height: Math.ceil(70*scale),
backgroundColor: 'transparent',
},
textStyle: {
marginTop: Math.ceil(28*scale),
color: '#444444',
backgroundColor: 'transparent',
fontSize: 15,
},
})
... ...
/**
* Created by zzz on 2019/3/12.
*/
'use strict';
import React, {Component} from 'react';
import {Dimensions, Image, StyleSheet, Text, TouchableOpacity, View, NativeModules} from 'react-native';
import {Immutable} from "immutable";
import YH_Image from '../../../common/components/YH_Image';
export default class ContentFansListCell extends Component {
constructor(props) {
super(props);
}
render() {
let {data} = this.props;
let resource = data.isMutualAttention ? require('../../images/content_attentioned.png') : require('../../images/content_fan.png');
let status = data.isMutualAttention ? 1 : 0;
let optUid = data.optUid;
let {width, height} = Dimensions.get('window');
let sliceCount = width <= 320 ? 6 : 10;
let userName = data.userName.length > sliceCount ? data.userName.slice(0,sliceCount)+'...' : data.userName;
return (
<View>
<View style={styles.headerBackground}>
<TouchableOpacity activeOpacity={1} onPress={()=> this.props.jumpToPersonalGrassPage && this.props.jumpToPersonalGrassPage(optUid)}>
<YH_Image style={styles.headIcon} url={data.headIco} circle={true}/>
</TouchableOpacity>
<View style={styles.textView}>
<TouchableOpacity style={styles.toubleView} activeOpacity={1} onPress={()=> this.props.jumpToPersonalGrassPage && this.props.jumpToPersonalGrassPage(optUid)}>
<Text style={styles.nicknameText} numberOfLines={1}>{userName}</Text>
<Text style={styles.defaultReplyText}>关注了你</Text>
</TouchableOpacity>
<Text style={styles.startTimeText}>{data.createTime}</Text>
</View>
<TouchableOpacity activeOpacity={1} style={styles.fanContainer} onPress={()=> {this.props.updateAttentionAction && this.props.updateAttentionAction(status,optUid)}}>
<Image style={styles.fanImage} resizeMode="contain" source={resource}/>
</TouchableOpacity>
</View>
{ this.props.showLine ? <View style={styles.lineView}/> : null }
</View>
);
}
}
let { width, height } = Dimensions.get('window');
const DEVICE_HEIGHT_RATIO = height / 667;
let styles = StyleSheet.create({
headerBackground: {
width: width,
height: 80,
backgroundColor: 'white',
flexDirection: 'row',
},
headIcon: {
width: 50,
height: 50,
marginLeft: 15,
marginTop: 15,
marginRight:15,
overflow: 'hidden',
borderRadius: 25,
},
textView: {
flexDirection: 'column',
justifyContent: 'space-between',
alignItems: 'flex-start',
marginTop: 15,
marginBottom: 20,
width: width - 160,
},
toubleView: {
flexDirection: 'row',
overflow:'hidden',
width: width - 160,
},
startTimeText: {
fontFamily: 'PingFang-SC-Regular',
fontSize: 12,
color: '#B0B0B0',
height: 17
},
nicknameText: {
fontSize: 14,
fontFamily: 'PingFang-SC-Regular',
color: 'black',
},
defaultReplyText :{
fontSize: 14,
fontFamily: 'PingFang-SC-Regular',
color: '#b0b0b0',
},
fanContainer: {
position: 'absolute',
top: 27,
right: 15,
width: 60,
height: 25,
},
fanImage: {
width: 60,
height: 25,
},
lineView: {
marginLeft:15,
marginRight: 0,
width: width-15,
height: 0.5,
backgroundColor: '#e0e0e0'
},
});
... ...
/**
* Created by zzz on 2019/3/12.
*/
'use strict';
import React, {Component} from 'react';
import {Dimensions, Image, ListView, StyleSheet, Text, TouchableOpacity, View, NativeModules} from 'react-native';
import {Immutable} from "immutable";
import YH_Image from '../../../common/components/YH_Image';
export default class ContentLikedListCell extends Component {
constructor(props) {
super(props);
}
render() {
let rowData = this.props.data;
let resource = rowData.coverImg ? typeof rowData.coverImg === 'string' ? JSON.parse(rowData.coverImg) : rowData.coverImg : '';
let picItem = resource ? typeof resource.data === 'string' ? JSON.parse(resource.data) : resource.data : '';
let imageUrl = YH_Image.getSlicedUrl(picItem.src, 50 * DEVICE_HEIGHT_RATIO, 50 * DEVICE_HEIGHT_RATIO, 2);
let {width, height} = Dimensions.get('window');
let sliceCount = width <= 320 ? 10 : 15;
let userName = rowData.userName.length > sliceCount ? rowData.userName.slice(0,sliceCount)+'...' : rowData.userName;
let subName = '赞了你';
switch (rowData.businessType) {
case 1000:
subName = '赞了你';
break;
case 1001:
subName = '收藏了你';
break;
case 1002:
subName = '评论了你';
break;
case 1003:
subName = '回复了你';
break;
case 1004:
subName = '关注了你';
break;
default:
}
return (
<View>
<View style={styles.headerBackground}>
<TouchableOpacity activeOpacity={1} onPress={()=> this.props.jumpToPersonalGrassPage && this.props.jumpToPersonalGrassPage(rowData.optUid)}>
<YH_Image style={styles.headIcon} url={rowData.headIco} circle={true}/>
</TouchableOpacity>
<View style={styles.textView}>
<TouchableOpacity activeOpacity={1} onPress={()=> this.props.jumpToPersonalGrassPage && this.props.jumpToPersonalGrassPage(rowData.optUid)}>
<Text style={styles.nicknameText} numberOfLines={1}>{userName}</Text>
</TouchableOpacity>
<Text style={styles.subnameText}>{subName}</Text>
</View>
</View>
<TouchableOpacity activeOpacity={1} onPress={() => this.props.jumpToGrassDetailPage && this.props.jumpToGrassDetailPage(rowData.articleId)}>
<View style={ styles.originView} >
<Text style={styles.copyText}>原文</Text>
<YH_Image url={imageUrl} style={styles.imageStyle} />
{rowData.content ?
<Text style={styles.originText} numberOfLines={1}>{rowData.content}</Text>
:
<View style={{height: 5}} />
}
</View>
</TouchableOpacity>
<View style={styles.timeView}>
<Text style={styles.startTimeText}>{rowData.createTime}</Text>
</View>
{ this.props.showLine ? <View style={styles.lineView}/> : null }
</View>
);
}
}
let { width, height } = Dimensions.get('window');
const DEVICE_HEIGHT_RATIO = height / 667;
let styles = StyleSheet.create({
headerBackground: {
width: width,
height: 70,
backgroundColor: 'white',
flexDirection: 'row',
},
headIcon: {
width: 40,
height: 40,
marginLeft: 15,
marginTop: 15,
marginRight:10,
overflow: 'hidden',
borderRadius: 20,
},
textView: {
flexDirection: 'column',
justifyContent: 'space-between',
alignItems: 'flex-start',
marginTop: 18,
marginBottom: 15,
},
nicknameText: {
fontSize: 14,
fontFamily: 'PingFang-SC-Medium',
color: '#222222',
},
subnameText: {
fontSize: 12,
fontFamily: 'PingFang-SC-Regular',
color: '#B0B0B0',
},
originView: {
flex: 1,
marginLeft: 15,
marginRight: 15,
marginBottom: 10,
backgroundColor: '#f0f0f0'
},
copyText: {
fontFamily: 'PingFang-SC-Regular',
fontSize: 12,
color: '#444444',
marginTop: 10,
marginLeft: 10,
},
imageStyle: {
marginTop: 10,
marginBottom: 4,
marginLeft: 10,
width: 50 * DEVICE_HEIGHT_RATIO,
height: 50 * DEVICE_HEIGHT_RATIO,
},
originText: {
fontFamily: 'PingFang-SC-Regular',
fontSize: 12,
color: '#b0b0b0',
marginLeft: 10,
marginRight: 15,
marginBottom: 9,
},
timeView: {
alignItems: 'flex-start',
marginBottom: 10,
marginLeft: 15,
},
startTimeText: {
fontFamily: 'PingFang-SC-Regular',
fontSize: 12,
color: '#B0B0B0',
height: 17
},
lineView: {
marginLeft:15,
marginRight: 0,
width: width-15,
height: 0.5,
backgroundColor: '#e0e0e0'
},
});
... ...
/**
* Created by zzz on 2019/3/5.
*/
'use strict';
import React, {Component} from "react";
import ReactNative, {View, Text, Image, ListView, StyleSheet, InteractionManager, Dimensions, TouchableOpacity, Platform, RefreshControl, NativeModules
} from 'react-native';
import YH_PtrRefresh from '../../../common/components/YH_PtrRefresh';
import LoadMoreIndicator from '../../../common/components/LoadMoreIndicator';
import ContentEmptyView from './ContentEmptyView'
import ContentLikedListCell from './ContentLikedListCell'
import ContentFansListCell from './ContentFansListCell'
export default class ContentListView extends Component {
constructor(props) {
super(props);
this._renderRow = this._renderRow.bind(this);
this._renderFooter = this._renderFooter.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
});
}
_renderFooter(){
return (
<View style={[{height: 50}, {backgroundColor: 'white'}, {justifyContent:'center'}]}>
</View>
)
}
_renderRow(rowData, sectionID, rowID) {
let showLine = this.props.contentList.length === +rowID+1 ? false : true;
return (
<View>
{ this.props.contentListId === '1' ?
<ContentLikedListCell
key={'row'+ rowID}
data={rowData}
showLine={showLine}
jumpToPersonalGrassPage={this.props.jumpToPersonalGrassPage}
jumpToGrassDetailPage={this.props.jumpToGrassDetailPage}
/>
:
<ContentFansListCell
key={'row'+ rowID}
data={rowData}
showLine={showLine}
updateAttentionAction={this.props.updateAttentionAction}
jumpToPersonalGrassPage={this.props.jumpToPersonalGrassPage}
/>
}
</View>
);
}
render() {
let {contentList, isPullToRefresh, shouldShowEmpty} = this.props;
if (shouldShowEmpty) {
return (
<View style={styles.container}>
<ContentEmptyView
listId={this.props.contentListId}
/>
</View>
);
}
return (
<View style={styles.container}>
{
Platform.OS === 'ios' ?
<ListView
ref={(c) => {
this.listView = c;
}}
contentContainerStyle={styles.contentContainer}
dataSource={this.dataSource.cloneWithRows(contentList)}
renderRow={this._renderRow}
renderFooter={this._renderFooter}
enableEmptySections={true}
enablePullToRefresh={true}
isOnPullToRefresh={isPullToRefresh}
showsVerticalScrollIndicator={false}
onRefreshData={() => {
this.props.onRefresh && this.props.onRefresh();
}}
onEndReached={() => {
if (contentList.size !== 0) {
this.props.onEndReached && this.props.onEndReached();
}
}}
/>
:
<ListView
ref={(c) => {
this.listView = c;
}}
contentContainerStyle={styles.contentContainer}
dataSource={this.dataSource.cloneWithRows(contentList)}
renderRow={this._renderRow}
renderFooter={this._renderFooter}
enableEmptySections={true}
enablePullToRefresh={true}
showsVerticalScrollIndicator={false}
refreshControl={
<YH_PtrRefresh
refreshing={isPullToRefresh}
onRefresh={() => {
this.props.onRefresh && this.props.onRefresh();
}}
colors={['#000000', '#ff0000']}
progressBackgroundColor="#ffffff"
/>
}
onEndReached={() => {
if (contentList.size !== 0) {
this.props.onEndReached && this.props.onEndReached();
}
}}
/>
}
</View>
);
}
}
let styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},
contentContainer: {
},
})
... ...
/**
* Created by zzz on 2019/3/12.
*/
'use strict';
import React, {Component} from 'react';
import {Dimensions, Image, ListView, StyleSheet, Text, TouchableOpacity, View, NativeModules} from 'react-native';
import {Immutable} from "immutable";
import YH_Image from '../../../common/components/YH_Image';
export default class ContentMessageCell extends Component {
constructor(props) {
super(props);
}
render() {
let {rowData} = this.props;
let status = rowData.businessType === 1003 ? true : false;
let resource = rowData.coverImg ? typeof rowData.coverImg === 'string' ? JSON.parse(rowData.coverImg) : rowData.coverImg : '';
let picItem = resource ? typeof resource.data === 'string' ? JSON.parse(resource.data) : resource.data : '';
let imageUrl = YH_Image.getSlicedUrl(picItem.src, 50 * DEVICE_HEIGHT_RATIO, 50 * DEVICE_HEIGHT_RATIO, 2);
let {width, height} = Dimensions.get('window');
let sliceCount = width <= 320 ? 10 : 15;
let userName = rowData.userName.length > sliceCount ? rowData.userName.slice(0,sliceCount)+'...' : rowData.userName;
let subName = '赞了你';
switch (rowData.businessType) {
case 1000:
subName = '赞了你';
break;
case 1001:
subName = '收藏了你';
break;
case 1002:
subName = '评论了你';
break;
case 1003:
subName = '回复了你';
break;
case 1004:
subName = '关注了你';
break;
default:
}
return (
<View>
<View style={styles.headerBackground}>
<TouchableOpacity activeOpacity={1} onPress={()=> this.props.jumpToPersonalGrassPage && this.props.jumpToPersonalGrassPage(rowData.optUid)}>
<YH_Image style={styles.headIcon} url={rowData.headIco} circle={true}/>
</TouchableOpacity>
<View style={styles.textView}>
<TouchableOpacity activeOpacity={1} onPress={()=> this.props.jumpToPersonalGrassPage && this.props.jumpToPersonalGrassPage(rowData.optUid)}>
<Text style={styles.nicknameText} numberOfLines={1}>{userName}</Text>
</TouchableOpacity>
<Text style={styles.subnameText}>{subName}</Text>
</View>
<TouchableOpacity activeOpacity={1} style={styles.replyContainer} onPress={()=> this.props.jumpToShowKeyboard && this.props.jumpToShowKeyboard(rowData.userName, rowData.commentId)}>
<Image style={styles.replyImage} resizeMode="contain" source={require('../../images/content_reply.png')}/>
</TouchableOpacity>
</View>
<View style={styles.replyTextView}>
<Text style={styles.replyText} numberOfLines={2}>{rowData.commentContent}</Text>
</View>
{ status ?
<TouchableOpacity activeOpacity={1} onPress={() => this.props.jumpToGrassDetailPage && this.props.jumpToGrassDetailPage(rowData.articleId)}>
<View style={styles.ownerReplyView}>
<Text style={{paddingLeft: 10, paddingTop: 14, paddingRight: 1, paddingBottom: 12}}>
<Text style={styles.replyText}>我的评论:</Text>
<Text style={styles.defaultReplyText} numberOfLines={2}>{rowData.parentCommentContent}</Text>
</Text>
</View>
</TouchableOpacity>
:
<TouchableOpacity activeOpacity={1} onPress={() => this.props.jumpToGrassDetailPage && this.props.jumpToGrassDetailPage(rowData.articleId)}>
<View style={ styles.originContainerView} >
<Text style={styles.copyText}>原文</Text>
<YH_Image url={imageUrl} style={styles.imageStyle} />
{rowData.content ?
<Text style={styles.originText} numberOfLines={1}>{rowData.content}</Text>
:
<View style={{height: 5}} />
}
</View>
</TouchableOpacity> }
<View style={styles.timeView}>
<Text style={styles.startTimeText}>{rowData.createTime}</Text>
</View>
{ this.props.showLine ? <View style={styles.lineView}/> : null }
</View>
);
}
}
let { width, height } = Dimensions.get('window');
const DEVICE_WIDTH_RATIO = width / 375;
const DEVICE_HEIGHT_RATIO = height / 667;
let styles = StyleSheet.create({
headerBackground: {
width: width,
height: 70,
backgroundColor: 'white',
flexDirection: 'row',
},
headIcon: {
width: 40,
height: 40,
marginLeft: 15,
marginTop: 15,
marginRight:10,
overflow: 'hidden',
borderRadius: 20,
},
textView: {
flexDirection: 'column',
justifyContent: 'space-between',
alignItems: 'flex-start',
marginTop: 18,
marginBottom: 15,
},
nicknameText: {
fontSize: 14,
fontFamily: 'PingFang-SC-Medium',
color: '#222222',
},
subnameText: {
fontSize: 12,
fontFamily: 'PingFang-SC-Regular',
color: '#B0B0B0',
},
replyContainer: {
position: 'absolute',
top: 23,
right: 15,
width: 60,
height: 25,
},
replyImage: {
width: 60,
height: 25,
},
replyTextView: {
width: width - 30,
marginBottom: 15,
marginLeft: 15,
},
replyText: {
fontFamily: 'PingFang-SC-Regular',
fontSize: 12,
color: '#444444',
},
ownerReplyView : {
flexDirection: 'row',
alignItems: 'center',
width: width - 30,
marginLeft: 15,
backgroundColor: '#F0F0F0',
marginBottom: 10,
},
defaultReplyText: {
fontFamily: 'PingFang-SC-Regular',
fontSize: 12,
color: '#a8a8a8',
},
originContainerView: {
flex: 1,
marginLeft: 15,
marginRight: 15,
marginBottom: 10,
backgroundColor: '#f0f0f0'
},
copyText: {
fontFamily: 'PingFang-SC-Regular',
fontSize: 12,
color: '#444444',
marginTop: 10,
marginLeft: 9,
},
imageStyle: {
marginTop: 10,
marginBottom: 4,
marginLeft: 9,
width: 50 * DEVICE_HEIGHT_RATIO,
height: 50 * DEVICE_HEIGHT_RATIO,
},
originText: {
fontFamily: 'PingFang-SC-Regular',
fontSize: 12,
color: '#b0b0b0',
marginLeft: 9,
marginRight: 16,
marginBottom: 9,
},
timeView: {
alignItems: 'flex-start',
marginBottom: 10,
marginLeft: 15,
},
startTimeText: {
fontFamily: 'PingFang-SC-Regular',
fontSize: 12,
color: '#B0B0B0',
height: 17
},
lineView: {
marginLeft:15,
marginRight: 0,
width: width-15,
height: 0.5,
backgroundColor: '#e0e0e0'
},
});
... ...
/**
* Created by zzz on 2019/3/5.
*/
'use strict';
import React, {Component} from "react";
import {StyleSheet, Text, View, Image, ART} from "react-native";
import YH_Image from "../../../common/components/YH_Image";
const {Path, Shape, Surface, Group} = ART;
const INVALID_POSITION = -1;
const IMAGE_WIDTH = 50;
const IMAGE_HEIGHT = 50;
export default class ContentMessageTabView extends Component {
constructor(props) {
super(props);
this.state = {
badgeX: INVALID_POSITION,
badgeY: INVALID_POSITION,
badgeWidth: INVALID_POSITION,
badgeHeight: INVALID_POSITION
};
this.onImageLayout = this.onImageLayout.bind(this)
this.onBadgeLayout = this.onBadgeLayout.bind(this)
}
onImageLayout(event) {
let imageLayout = event.nativeEvent.layout
this.setState({
badgeY: imageLayout.y - 4,
badgeX: imageLayout.x + imageLayout.width / 2 + 17,
})
}
onBadgeLayout(event) {
let badgeLayout = event.nativeEvent.layout
this.setState({badgeWidth: badgeLayout.width, badgeHeight: badgeLayout.height})
}
renderSurface() {
if (this.state.badgeWidth != INVALID_POSITION && this.state.badgeHeight != INVALID_POSITION) {
let width = this.state.badgeWidth, height = this.state.badgeHeight
let strokeWidth = 1.5
const radius = (height - strokeWidth) / 2, x = strokeWidth / 2 + radius, y = (height) / 2;
const path = new Path()
.moveTo(x, y - radius)
.counterArc(0, radius * 2, radius)
.lineTo(width - radius - strokeWidth / 2, y + radius)
.counterArc(0, -radius * 2, radius).close();
return <View style={styles.surfaceContainer}>
<Surface width={width} height={height}>
<Group>
<Shape d={path} stroke={'#FB2330'} fill={'#FB2330'} strokeWidth={strokeWidth}/>
</Group>
</Surface>
</View>
}
return null;
}
render() {
return <View>
<View style={[styles.container, this.props.style]}>
<YH_Image style={styles.icon} onLayout={this.onImageLayout}
url={YH_Image.getSlicedUrl(this.props.icon.uri, IMAGE_WIDTH, IMAGE_HEIGHT, 2)}/>
<Text style={styles.title}>{this.props.title}</Text>
</View>
{this.props.badge > 0 && this.state.badgeX != INVALID_POSITION && this.state.badgeY != INVALID_POSITION &&
<View style={[styles.badgeContainer, {left: this.state.badgeX, top: this.state.badgeY}]}
onLayout={this.onBadgeLayout}>
{this.renderSurface()}
<Text style={styles.badgeTitle}>{this.props.badge}</Text>
</View>
}
</View>
}
}
let styles = StyleSheet.create({
container: {
paddingTop: 33,
paddingBottom: 40,
flexDirection: 'column',
justifyContent: 'center',
paddingLeft: 30,
paddingRight: 30,
alignItems: 'center'
},
title: {
fontFamily: 'PingFang-SC-Medium',
fontSize: 14,
marginTop: 15,
color: '#4A4A4A',
textAlign:'center',
width: 60,
},
icon: {
width: 50,
height: 50,
},
badgeContainer: {
backgroundColor: 'transparent',
height: 17,
minWidth: 17,
alignItems: 'center',
justifyContent: 'center',
flex: 0,
position: 'absolute'
},
surfaceContainer: {
position: 'absolute',
left: 0,
top: 0,
right: 0,
bottom: 0,
},
badgeTitle: {
marginLeft: 5,
marginRight: 5,
fontSize: 10,
color: 'white',
fontWeight: 'bold',
}
});
... ...
/**
* Created by zzz on 2019/3/5.
*/
'use strict';
import React, {Component} from "react";
import ReactNative, {View, Text, Image, DeviceEventEmitter, InteractionManager, ListView, StyleSheet, Dimensions, TouchableOpacity, Platform, RefreshControl, NativeModules
} from 'react-native';
import YH_PtrRefresh from '../../../common/components/YH_PtrRefresh';
import LoadMoreIndicator from '../../../common/components/LoadMoreIndicator';
import ContentMessageTabView from "./ContentMessageTabView";
import ContentMessageCell from "./ContentMessageCell";
import Prompt from '../../../common/components/Prompt';
export default class ContentMessageView extends Component {
constructor(props) {
super(props);
this._renderRow = this._renderRow.bind(this);
this._renderHeader = this._renderHeader.bind(this);
this._renderFooter = this._renderFooter.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
});
}
_renderRow(rowData, sectionID, rowID) {
let showLine = this.props.commentList.length === +rowID+1 ? false : true;
return (
<ContentMessageCell
key={'row'+ rowID}
rowData={rowData}
showLine={showLine}
jumpToShowKeyboard={this.props.jumpToShowKeyboard}
jumpToPersonalGrassPage={this.props.jumpToPersonalGrassPage}
jumpToGrassDetailPage={this.props.jumpToGrassDetailPage}
/>
);
}
_renderHeader() {
let showStatus = this.props.contentTipFlag==='0' ? true : false;
let tabStatus = this.props.commentList.length===0 ? false : true;
function onNotifyPress() {
NativeModules.YH_CommonHelper.jumpToUpdateNotificationStatus();
}
return (<View>
{showStatus ?
<View style={styles.tipContainer}>
<Text style={styles.tipStyle}>{'开启推送通知,第一时间收到互动消息'}</Text>
<TouchableOpacity style={{width:60, height: 25}} onPress={()=> {onNotifyPress()}}>
<Image style={styles.openImage} source={require('../../images/open_btn.png')}/>
</TouchableOpacity>
<View style={styles.line}/>
</View>
:
null
}
<View style={styles.tabContainer}>
{this.props.tabButtons.map((button, index)=> {
return <TouchableOpacity style={styles.tabItemContainer} key={index} onPress={()=> {
this.props.onPressTabAction && this.props.onPressTabAction(button.actionUrl, button.type);
}}>
<ContentMessageTabView icon={button.icon} title={button.description} badge={button.unReadCount}/>
</TouchableOpacity>
})}
</View>
{ tabStatus ? <View style={styles.tabLine} /> : null }
</View>
)
}
_renderFooter() {
let emptyViewShow = this.props.commentList.length===0 ? true : false;
return (
<View>
{ emptyViewShow && this.props.isFetching === false ?
<View style={styles.emptyView}>
<Image
style={styles.iconStyle}
source={require('../../images/message_empty_ic.png')}
/>
<Text style={styles.textStyle}>暂无更新的消息</Text>
</View>
:
<View style={[{height: 50}, {backgroundColor: 'white'}, {justifyContent:'center'}]}/> }
</View>
)
}
render() {
let {commentList, isPullToRefresh, tipMessage} = this.props;
return (
<View style={styles.container}>
{
Platform.OS === 'ios' ?
<ListView
ref={(c) => {
this.listView = c;
}}
contentContainerStyle={styles.contentContainer}
dataSource={this.dataSource.cloneWithRows(commentList)}
renderRow={this._renderRow}
renderHeader={this._renderHeader}
renderFooter={this._renderFooter}
enableEmptySections={true}
enablePullToRefresh={true}
isOnPullToRefresh={isPullToRefresh}
showsVerticalScrollIndicator={false}
onRefreshData={() => {
this.props.onRefresh && this.props.onRefresh();
}}
onEndReached={() => {
if (commentList.size !== 0) {
this.props.onEndReached && this.props.onEndReached();
}
}}
/>
:
<ListView
ref={(c) => {
this.listView = c;
}}
contentContainerStyle={styles.contentContainer}
dataSource={this.dataSource.cloneWithRows(commentList)}
renderRow={this._renderRow}
renderHeader={this._renderHeader}
renderFooter={this._renderFooter}
enableEmptySections={true}
enablePullToRefresh={true}
showsVerticalScrollIndicator={false}
refreshControl={
<YH_PtrRefresh
refreshing={isPullToRefresh}
onRefresh={() => {
this.props.onRefresh && this.props.onRefresh();
}}
colors={['#000000', '#ff0000']}
progressBackgroundColor="#ffffff"
/>
}
onEndReached={() => {
if (commentList.size !== 0) {
this.props.onEndReached && this.props.onEndReached();
}
}}
/>
}
{tipMessage != '' ?
<Prompt
text={tipMessage}
duration={800}
onPromptHidden={this.props.onHiddenTipMessage}
/>
: null
}
</View>
);
}
}
let {width} = Dimensions.get('window');
const ROW_COLUMN = 3
let scale = (width/375.0);
let styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},
contentContainer: {
},
tabContainer: {
flex: 1,
flexDirection: 'row',
flexWrap: 'wrap',
backgroundColor: 'white'
},
tabItemContainer: {
width: width / ROW_COLUMN,
alignItems: 'center',
justifyContent: 'center',
},
sectionTitle: {
fontWeight: 'bold',
fontSize: 20,
marginTop: 10,
marginBottom: 10,
marginLeft: 20,
color: 'black',
},
sectionContainer: {
backgroundColor: 'white',
},
emptyContainer: {
marginTop: 56,
alignItems: 'center',
justifyContent: 'center',
},
emptyTitle: {
fontWeight: 'bold',
fontSize: 24,
color: '#CCCCCC'
},
tipContainer: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#ffffff',
justifyContent: 'space-between',
paddingLeft: 11,
paddingRight: 14,
height: 40,
},
tipStyle: {
fontFamily: 'PingFang-SC-Medium',
fontSize: 14,
color: '#4A4A4A',
backgroundColor: 'transparent',
textAlign: 'center',
lineHeight: 40,
},
openImage: {
width: 60,
height: 25,
marginRight: 14
},
line: {
backgroundColor: '#EEEEEE',
left: 0,
right: 0,
bottom: 0,
position: 'absolute',
height: 1,
},
tabLine: {
backgroundColor: '#F0F0F0',
marginLeft: 0,
marginRight: 0,
marginTop: 0,
height: 10
},
emptyView: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
},
iconStyle: {
marginTop: Math.ceil(82*scale),
width: Math.ceil(96*scale),
height: Math.ceil(70*scale),
backgroundColor: 'transparent',
},
textStyle: {
marginTop: Math.ceil(28*scale),
color: '#444444',
backgroundColor: 'transparent',
fontSize: 15,
},
})
... ...
... ... @@ -24,6 +24,7 @@ export default class MessageCell extends Component {
4: require('../../images/message_other_ic.png'),
5: require('../../images/message_push_ic.png'),
6: require('../../images/message_ufo_ic.png'),
7: require('../../images/message_content_ic.png'),
99: require('../../images/message_zxkf_ic.png'),
}
}
... ... @@ -69,6 +70,9 @@ export default class MessageCell extends Component {
case 6:
text = isEmpty ? 'UFO出售、购买消息' : content;
break;
case 7:
text = isEmpty ? '没有收到新消息' : content;
break;
case 99:
text = isEmpty ? '服务时间:08:00-22:30' : content;
break;
... ...
... ... @@ -6,6 +6,7 @@ export default keyMirror({
SET_HOST: null,
SET_SERVICE_HOST: null,
SET_CHANNEL: null,
SET_COMMUNITY_HOST: null,
LATEST_MESSAGE_REQUEST: null,
LATEST_MESSAGE_SUCCESS: null,
... ... @@ -26,4 +27,39 @@ export default keyMirror({
MESSAGE_LIST_CHANGE_EDIT_STATUS: null,
MESSAGE_LIST_CHANGE_SELECTED_STATUS: null,
//内容消息
SET_CONTENT_LIST_ID: null,
SET_CONTENT_CATEGORY_NAME: null,
SET_CONTENT_TIP_FLAG: null,
FETCH_CONTENT_TYPE_REQUEST: null,
FETCH_CONTENT_TYPE_SUCCESS: null,
FETCH_CONTENT_TYPE_FAILURE: null,
FETCH_CONTENT_LIST_REQUEST: null,
FETCH_CONTENT_LIST_SUCCESS: null,
FETCH_CONTENT_LIST_FAILURE: null,
FETCH_COMMENT_LIST_REQUEST: null,
FETCH_COMMENT_LIST_SUCCESS: null,
FETCH_COMMENT_LIST_FAILURE: null,
//回复评论
ADD_ARTICLE_COMMENT_REQUEST: null,
ADD_ARTICLE_COMMENT_SUCCESS: null,
ADD_ARTICLE_COMMENT_FAILURE: null,
//关注用户
UPDATE_ATTENTION_REQUEST: null,
UPDATE_ATTENTION_SUCCESS: null,
UPDATE_ATTENTION_FAILURE: null,
SHOW_REPLY_TIP_MESSAGE: null,
HIDDEN_REPLY_TIP_MESSAGE: null,
MESSAGE_CLEAR_OTHER_LIST: null,
MESSAGE_CLEAR_COMMENT_LIST: null,
CLEAR_BADGE_NUM_SUCCESS: null,
UPDATE_TIP_FLAG_SUCCESS: null,
});
... ...
/**
* Created by zzz on 2019/3/5.
*/
'use strict'
import React, {Component} from "react";
import ReactNative, { StyleSheet, Dimensions, Platform, View, Text, NativeModules, InteractionManager, NativeAppEventEmitter,
} from 'react-native'
import {bindActionCreators} from "redux";
import {connect} from "react-redux";
import {Map} from "immutable";
import * as contentActions from "../reducers/content/contentActions";
import ContentListView from '../components/content/ContentListView';
const actions = [
contentActions,
]
function mapStateToProps(state) {
return {
contentListId: state.content.contentListId,
contentList: state.content.contentList.get('list').toJS(),
isPullToRefresh: state.content.contentList.get('isPullToRefresh'),
shouldShowEmpty: state.content.contentList.get('shouldShowEmpty'),
communityHost: state.app.communityHost,
};
}
function mapDispatchToProps(dispatch) {
const creators = Map()
.merge(...actions)
.filter(value => typeof value === 'function')
.toObject();
return {
actions: bindActionCreators(creators, dispatch),
dispatch
};
}
class ContentListContainer extends Component {
constructor(props) {
super(props);
this._updateAttentionAction = this._updateAttentionAction.bind(this);
this._onRefresh = this._onRefresh.bind(this);
this._onEndReached = this._onEndReached.bind(this);
this._jumpToPersonalGrassPage = this._jumpToPersonalGrassPage.bind(this);
this._jumpToGrassDetailPage = this._jumpToGrassDetailPage.bind(this);
}
_updateAttentionAction(status, optUid) {
this.props.actions.updateAttentionAction(status, optUid);
}
_onRefresh() {
this.props.actions.getContentList(true, this.props.contentListId)
}
_onEndReached() {
this.props.actions.getContentList(false, this.props.contentListId)
}
componentDidMount() {
this.props.actions.getContentList(false, this.props.contentListId)
}
componentWillUnmount() {
this.props.actions.clearOtherMessage()
}
//跳转到种草H5
_jumpToPersonalGrassPage(optUid) {
let {communityHost} = this.props
let url = communityHost + (Platform.OS === 'ios' ? '/grass': '') + `/author/${1}/${optUid}`
let action, params;
if(Platform.OS === 'ios') {
action = 'go.h5hn';
params = {
url,
headerid: -1,
}
} else {
action = 'go.h5';
params = {
url,
param : { headerid: "-1" }
}
}
let jumpParams = {
action,
params
}
let path = 'http://m.yohobuy.com?openby:yohobuy=' + JSON.stringify(jumpParams);
NativeModules.YH_CommonHelper.jumpWithUrl(path);
}
_jumpToGrassDetailPage(articleId) {
let {communityHost} = this.props
let url = communityHost + (Platform.OS === 'ios' ? '/grass': '') + `/article/detail/${articleId}`
let action, params;
if(Platform.OS === 'ios') {
action = 'go.h5hn';
params = {
url,
headerid: -1,
}
} else {
action = 'go.h5';
params = {
url,
param : { headerid: "-1" }
}
}
let jumpParams = {
action,
params
}
let path = 'http://m.yohobuy.com?openby:yohobuy=' + JSON.stringify(jumpParams);
NativeModules.YH_CommonHelper.jumpWithUrl(path);
}
render() {
return (
<View style={styles.container}>
<ContentListView
ref={(view) => {
this.messageView = view
}}
contentList={this.props.contentList}
isPullToRefresh={this.props.isPullToRefresh}
contentListId={this.props.contentListId}
shouldShowEmpty={this.props.shouldShowEmpty}
onRefresh={this._onRefresh}
onEndReached={this._onEndReached}
updateAttentionAction={this._updateAttentionAction}
jumpToPersonalGrassPage={this._jumpToPersonalGrassPage}
jumpToGrassDetailPage={this._jumpToGrassDetailPage}
/>
</View>
)
}
}
let styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default connect(mapStateToProps, mapDispatchToProps, null, {withRef: true})(ContentListContainer)
... ...
/**
* Created by zzz on 2019/3/5.
*/
'use strict'
import React, {Component} from "react";
import ReactNative, { StyleSheet, Dimensions, Platform, View, Text, NativeModules, DeviceEventEmitter, InteractionManager, NativeAppEventEmitter,
} from 'react-native'
import {bindActionCreators} from "redux";
import {connect} from "react-redux";
import {Map} from "immutable";
import * as contentActions from "../reducers/content/contentActions";
import ContentMessageView from '../components/content/ContentMessageView';
const actions = [
contentActions,
]
function mapStateToProps(state) {
return {
contentListId: state.content.contentListId,
tabButtons: state.content.contentType.get('list').toJS(),
commentList: state.content.commentList.get('list').toJS(),
isPullToRefresh: state.content.commentList.get('isPullToRefresh'),
isFetching: state.content.commentList.get('isFetching'),
contentTipFlag: state.content.contentTipFlag,
tipMessage: state.content.tipMessage,
communityHost: state.app.communityHost,
};
}
function mapDispatchToProps(dispatch) {
const creators = Map()
.merge(...actions)
.filter(value => typeof value === 'function')
.toObject();
return {
actions: bindActionCreators(creators, dispatch),
dispatch
};
}
class ContentMessageContainer extends Component {
constructor(props) {
super(props);
this._onRefresh = this._onRefresh.bind(this);
this._onEndReached = this._onEndReached.bind(this);
this._onHiddenTipMessage = this._onHiddenTipMessage.bind(this);
this._jumpToShowKeyboard = this._jumpToShowKeyboard.bind(this);
this._jumpToPersonalGrassPage = this._jumpToPersonalGrassPage.bind(this);
this._jumpToGrassDetailPage = this._jumpToGrassDetailPage.bind(this);
this._onPressTabAction = this._onPressTabAction.bind(this);
this.subscription = NativeAppEventEmitter.addListener(
'YHUpdateAuthStatusEvent',
(reminder) => {
let isfresh = this.props.contentTipFlag===reminder.flag? false : true;
if (isfresh) {
this.props.actions.updateContentTipFlag(reminder.flag)
}
}
);
}
componentDidMount() {
this.props.actions.getCommentList(false, this.props.contentListId)
this.props.actions.getContentType()
}
componentWillUnmount() {
this.props.actions.clearCommentMessageList()
this.subscription && this.subscription.remove()
}
_onRefresh() {
this.props.actions.getCommentList(true, this.props.contentListId)
this.props.actions.getContentType()
}
_onEndReached() {
this.props.actions.getCommentList(false, this.props.contentListId)
}
_onHiddenTipMessage() {
this.props.actions.hiddenTipMessage();
}
_onPressTabAction(actionUrl, tabType) {
this.props.actions.clearBadgeNumAction(tabType);
NativeModules.YH_CommonHelper.jumpWithUrl(actionUrl);
}
_jumpToShowKeyboard(userName, commentId) {
NativeModules.YH_CommonHelper.showkeyBoardView(userName).then(replyValue => {
this.props.actions.addArticleCommentAction(replyValue, commentId);
});
}
//跳转到种草H5
_jumpToPersonalGrassPage(optUid) {
let {communityHost} = this.props
let url = communityHost + (Platform.OS === 'ios' ? '/grass': '') + `/author/${1}/${optUid}`
let action, params;
if(Platform.OS === 'ios') {
action = 'go.h5hn';
params = {
url,
headerid: -1,
}
} else {
action = 'go.h5';
params = {
url,
param : { headerid: "-1" }
}
}
let jumpParams = {
action,
params
}
let path = 'http://m.yohobuy.com?openby:yohobuy=' + JSON.stringify(jumpParams);
NativeModules.YH_CommonHelper.jumpWithUrl(path);
}
_jumpToGrassDetailPage(articleId) {
let {communityHost} = this.props
let url = communityHost + (Platform.OS === 'ios' ? '/grass': '') + `/article/detail/${articleId}`
let action, params;
if(Platform.OS === 'ios') {
action = 'go.h5hn';
params = {
url,
headerid: -1,
}
} else {
action = 'go.h5';
params = {
url,
param : { headerid: "-1" }
}
}
let jumpParams = {
action,
params
}
let path = 'http://m.yohobuy.com?openby:yohobuy=' + JSON.stringify(jumpParams);
NativeModules.YH_CommonHelper.jumpWithUrl(path);
}
render() {
let {
contentTipFlag,
tipMessage,
tabButtons,
commentList,
isPullToRefresh,
isFetching
} = this.props
return (
<View style={styles.container}>
<ContentMessageView
ref={(view) => {
this.messageView = view
}}
contentTipFlag={contentTipFlag}
tabButtons={tabButtons}
commentList={commentList}
isPullToRefresh={isPullToRefresh}
isFetching={isFetching}
tipMessage={tipMessage}
onRefresh={this._onRefresh}
onEndReached={this._onEndReached}
onHiddenTipMessage={this._onHiddenTipMessage}
jumpToShowKeyboard={this._jumpToShowKeyboard}
jumpToPersonalGrassPage={this._jumpToPersonalGrassPage}
jumpToGrassDetailPage={this._jumpToGrassDetailPage}
onPressTabAction={this._onPressTabAction}
/>
</View>
)
}
}
let styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default connect(mapStateToProps, mapDispatchToProps, null, {withRef: true})(ContentMessageContainer)
... ...
... ... @@ -68,8 +68,8 @@ class MessageContainer extends Component {
this.props.actions.selectMessageCategory(item);
let id = item.get('id','');
if (id == 6) {
let url = item.get('url','');
let url = item.get('url','');
if (id == 6 || id == 7) {
NativeModules.YH_CommonHelper.jumpWithUrl(url);
return;
}
... ...
... ... @@ -3,10 +3,11 @@
import ReactNative from 'react-native';
const {
SET_PLATFORM,
SET_CHANNEL,
SET_PLATFORM,
SET_CHANNEL,
SET_HOST,
SET_SERVICE_HOST,
SET_COMMUNITY_HOST,
} = require('../../constants/actionTypes').default;
export function setPlatform(platform) {
... ... @@ -35,4 +36,11 @@ export function setServiceHost(host) {
type: SET_SERVICE_HOST,
payload: host
};
}
\ No newline at end of file
}
export function setCommunityHost(host) {
return {
type: SET_COMMUNITY_HOST,
payload: host
}
}
... ...
... ... @@ -7,6 +7,7 @@ let InitialState = Record({
channel: 1, // 1 - boy, 2 - girl, 3 - kid, 4 - lifestyle, 5 - yoho
host: 'http://api.yoho.cn',
serviceHost: 'http://api.yoho.cn',
communityHost: '',
});
export default InitialState;
... ...
... ... @@ -7,6 +7,7 @@ const {
SET_CHANNEL,
SET_HOST,
SET_SERVICE_HOST,
SET_COMMUNITY_HOST,
} = require('../../constants/actionTypes').default;
const initialState = new InitialState;
... ... @@ -23,6 +24,8 @@ export default function appReducer(state = initialState, action) {
return state.set('host', action.payload);
case SET_SERVICE_HOST:
return state.set('serviceHost', action.payload);
case SET_COMMUNITY_HOST:
return state.set('communityHost', action.payload);
}
return state;
... ...
'use strict';
import ReactNative from 'react-native';
import ContentService from '../../services/ContentService';
import Moment from "moment";
const {
SET_CONTENT_LIST_ID,
SET_CONTENT_CATEGORY_NAME,
SET_CONTENT_TIP_FLAG,
//消息类型
FETCH_CONTENT_TYPE_REQUEST,
FETCH_CONTENT_TYPE_SUCCESS,
FETCH_CONTENT_TYPE_FAILURE,
//消息内容
FETCH_CONTENT_LIST_REQUEST,
FETCH_CONTENT_LIST_SUCCESS,
FETCH_CONTENT_LIST_FAILURE,
//main评论内容
FETCH_COMMENT_LIST_REQUEST,
FETCH_COMMENT_LIST_SUCCESS,
FETCH_COMMENT_LIST_FAILURE,
//回复评论
ADD_ARTICLE_COMMENT_REQUEST,
ADD_ARTICLE_COMMENT_SUCCESS,
ADD_ARTICLE_COMMENT_FAILURE,
//关注用户
UPDATE_ATTENTION_REQUEST,
UPDATE_ATTENTION_SUCCESS,
UPDATE_ATTENTION_FAILURE,
//清除消息内容
MESSAGE_CLEAR_OTHER_LIST,
MESSAGE_CLEAR_COMMENT_LIST,
//回复成功提示
SHOW_REPLY_TIP_MESSAGE,
HIDDEN_REPLY_TIP_MESSAGE,
CLEAR_BADGE_NUM_SUCCESS,
UPDATE_TIP_FLAG_SUCCESS,
} = require('../../constants/actionTypes').default;
export function setContentListId(id) {
return {
type: SET_CONTENT_LIST_ID,
payload: id,
};
}
export function setContentCategoryName(name) {
return {
type: SET_CONTENT_CATEGORY_NAME,
payload: name,
};
}
export function setContentTipFlag(value) {
return {
type: SET_CONTENT_TIP_FLAG,
payload: value,
};
}
export function getContentTypeRequest() {
return {
type: FETCH_CONTENT_TYPE_REQUEST,
};
}
export function getContentTypeSuccess(json) {
return {
type: FETCH_CONTENT_TYPE_SUCCESS,
payload: json
}
}
export function getContentTypeFailure(error) {
return {
type: FETCH_CONTENT_TYPE_FAILURE,
payload: error
}
}
export function getCommentListRequest(isRefresh) {
return {
type: FETCH_COMMENT_LIST_REQUEST,
isRefresh
};
}
export function getCommentListSuccess(json) {
return {
type: FETCH_COMMENT_LIST_SUCCESS,
payload: json,
}
}
export function getCommentListFailure(error) {
return {
type: FETCH_COMMENT_LIST_FAILURE,
payload: error,
}
}
export function getContentListRequest(isRefresh) {
return {
type: FETCH_CONTENT_LIST_REQUEST,
isRefresh
};
}
export function getContentListSuccess(json) {
return {
type: FETCH_CONTENT_LIST_SUCCESS,
payload: json,
}
}
export function getContentListFailure(error) {
return {
type: FETCH_CONTENT_LIST_FAILURE,
payload: error,
}
}
export function addArticleCommentRequest() {
return {
type: ADD_ARTICLE_COMMENT_REQUEST,
}
}
export function addArticleCommentSuccess(json) {
return {
type: ADD_ARTICLE_COMMENT_SUCCESS,
payload: json,
}
}
export function addArticleCommentFailure(error) {
return {
type: ADD_ARTICLE_COMMENT_FAILURE,
payload: error,
}
}
export function updateAttentionRequest() {
return {
type: UPDATE_ATTENTION_REQUEST,
}
}
export function updateAttentionSuccess(json, optUid) {
return {
type: UPDATE_ATTENTION_SUCCESS,
payload: json,
optUid,
}
}
export function updateAttentionFailure(error) {
return {
type: UPDATE_ATTENTION_FAILURE,
payload: error,
}
}
//清除评论消息
export function clearCommentMessageListAction() {
return {
type: MESSAGE_CLEAR_COMMENT_LIST,
}
}
export function clearOtherMessageListAction() {
return {
type: MESSAGE_CLEAR_OTHER_LIST,
}
}
export function showTipMessage(message) {
return {
type: SHOW_REPLY_TIP_MESSAGE,
payload: message
}
}
export function hiddenTipMessage() {
return {
type:HIDDEN_REPLY_TIP_MESSAGE,
}
}
export function clearBadgeNumAction(tabType) {
return {
type:CLEAR_BADGE_NUM_SUCCESS,
payload: tabType,
}
}
export function updateContentTipFlag(tipFlag) {
return {
type:UPDATE_TIP_FLAG_SUCCESS,
payload: tipFlag,
}
}
/*
* 获取通知类型
*/
export function getContentType() {
return (dispatch, getState) => {
let {app, content} = getState();
let {isFetching} = content.toJS().contentType
if (isFetching) {
return;
}
let processor = ()=> {
dispatch(getContentTypeRequest());
return new ContentService(app.host).fetchContentType()
.then(json => {
let listJson = json ? json['grassInBoxTypeInfoList'] : [];
let list = listJson.map((item)=> {
return {
description: item.description,
type: item.type,
actionUrl: item.actionUrl,
icon: {uri: item.imgUrl},
unReadCount: item.unReadCount,
}
})
dispatch(getContentTypeSuccess(list));
})
.catch(error => {
dispatch(getContentTypeFailure(error));
});
}
ReactNative.NativeModules.YH_CommonHelper.uid()
.then(uid => {
processor()
})
.catch(error => {
ReactNative.NativeModules.YH_CommonHelper.login()
.then(uid => {
processor()
})
.catch(error => {
});
});
};
}
//查询消息
export function getCommentList(isRefresh, type) {
return (dispatch, getState) => {
let {app, content} = getState();
let {isFetching, endReached, currentPage, list} = content.get('commentList').toJS();
if (isFetching || (!isRefresh && endReached)) {
return;
}
let oldList = isRefresh ? [] : list
let processor = ()=> {
dispatch(getCommentListRequest(isRefresh));
return new ContentService(app.host).fetchContentList(isRefresh ? 1 : currentPage + 1, content.pageSize, type)
.then(json => {
let payload = parseContentList(json);
let list = [...oldList, ...payload.list];
payload.list = list;
payload.type = type;
if (payload.currentPage == 1 && payload.list.length == 0) {
payload.shouldShowEmpty = true;
}
dispatch(getCommentListSuccess(payload));
})
.catch(error => {
dispatch(getCommentListFailure(error));
});
}
ReactNative.NativeModules.YH_CommonHelper.uid()
.then(uid => {
processor()
})
.catch(error => {
ReactNative.NativeModules.YH_CommonHelper.login()
.then(uid => {
processor()
})
.catch(error => {
});
});
};
}
export function getContentList(isRefresh, type) {
return (dispatch, getState) => {
let {app, content} = getState();
let {isFetching, endReached, currentPage, list} = content.get('contentList').toJS();
if (isFetching || (!isRefresh && endReached)) {
return;
}
let oldList = isRefresh ? [] : list
let processor = ()=> {
dispatch(getContentListRequest(isRefresh));
return new ContentService(app.host).fetchContentList(isRefresh ? 1 : currentPage + 1, content.pageSize, type)
.then(json => {
let payload = parseContentList(json);
let list = [...oldList, ...payload.list];
payload.list = list;
payload.type = type;
if (payload.currentPage == 1 && payload.list.length == 0) {
payload.shouldShowEmpty = true;
}
dispatch(getContentListSuccess(payload));
})
.catch(error => {
dispatch(getContentListFailure(error));
});
}
ReactNative.NativeModules.YH_CommonHelper.uid()
.then(uid => {
processor()
})
.catch(error => {
ReactNative.NativeModules.YH_CommonHelper.login()
.then(uid => {
processor()
})
.catch(error => {
});
});
};
}
function parseContentList(json) {
let {data, page, totalPage} = json;
let parsedList = data ? data.map((item) => {
return Object.assign({},item,{
headIco: item.headIco,
userName: item.userName,
isMutualAttention: item.isMutualAttention,
optUid: item.uid,
commentId: item.commentId,
parentCommentId: item.parentCommentId,
commentContent: item.commentContent,
parentContent: item.parentCommentContent,
coverImg: item.coverImg,
articleId: item.articleId,
content: item.content,
businessType: item.businessType,
createTime: Moment(new Date(item.createTime * 1000)).format('YYYY.MM.DD HH:mm'),
})
}) : [];
let endReached = parsedList.length == 0 || page == totalPage;
let pageCount = totalPage;
return {
currentPage: page,
list: parsedList,
endReached,
pageCount,
}
}
//回复评论
export function addArticleCommentAction(content, commentId) {
return (dispatch, getState) => {
let {app} = getState();
let processor = (uid)=> {
dispatch(addArticleCommentRequest());
return new ContentService(app.host).addArticleComment(content, uid, commentId)
.then(json => {
dispatch(addArticleCommentSuccess(json));
dispatch(showTipMessage('回复成功'));
})
.catch(error => {
dispatch(addArticleCommentFailure(error));
dispatch(showTipMessage('回复失败'));
});
}
ReactNative.NativeModules.YH_CommonHelper.uid()
.then(uid => {
processor(uid)
})
.catch(error => {
ReactNative.NativeModules.YH_CommonHelper.login()
.then(uid => {
processor(uid)
})
.catch(error => {
});
});
};
}
//关注用户
export function updateAttentionAction(attentionStatus, optUid) {
return (dispatch, getState) => {
let {app} = getState();
let processor = (uid)=> {
dispatch(updateAttentionRequest());
return new ContentService(app.host).updateAttention(uid, optUid, attentionStatus)
.then(json => {
dispatch(updateAttentionSuccess(json, optUid));
})
.catch(error => {
dispatch(updateAttentionFailure(error));
});
}
ReactNative.NativeModules.YH_CommonHelper.uid()
.then(uid => {
processor(uid)
})
.catch(error => {
ReactNative.NativeModules.YH_CommonHelper.login()
.then(uid => {
processor(uid)
})
.catch(error => {
});
});
};
}
export function clearCommentMessageList() {
return (dispatch) => {
dispatch(clearCommentMessageListAction());
};
}
export function clearOtherMessage() {
return (dispatch) => {
dispatch(clearOtherMessageListAction());
};
}
... ...
'use strict';
import Immutable, {Record, List, Map} from 'immutable';
let InitialState = Record({
contentListId: 0,
contentCategoryName: '',
contentTipFlag: 3,
tipMessage: '',
pageSize: 20,
//内容消息类型
contentType: new (Record({
isFetching: false,
list: List(),
error:null,
})),
//main页面消息
commentList: new (Record({
isFetching: false,
list: List(),
currentPage: 0,
endReached: false,
isPullToRefresh: false,
error:null,
shouldShowEmpty: false,
pageCount: 0,
})),
//列表页面
contentList: new (Record({
isFetching: false,
list: List(),
currentPage: 0,
endReached: false,
isPullToRefresh: false,
error:null,
shouldShowEmpty: false,
pageCount: 0,
})),
addCommentContent: new (Record({
isFetching: false,
error: null,
})),
updateAttentionContent: new (Record({
isFetching: false,
error: null,
})),
});
export default InitialState;
... ...
'use strict';
import InitialState from './contentInitialState';
import Immutable, {Map} from 'immutable';
const {
SET_CONTENT_LIST_ID,
SET_CONTENT_CATEGORY_NAME,
SET_CONTENT_TIP_FLAG,
FETCH_CONTENT_TYPE_REQUEST,
FETCH_CONTENT_TYPE_SUCCESS,
FETCH_CONTENT_TYPE_FAILURE,
FETCH_CONTENT_LIST_REQUEST,
FETCH_CONTENT_LIST_SUCCESS,
FETCH_CONTENT_LIST_FAILURE,
//main评论内容
FETCH_COMMENT_LIST_REQUEST,
FETCH_COMMENT_LIST_SUCCESS,
FETCH_COMMENT_LIST_FAILURE,
//回复评论
ADD_ARTICLE_COMMENT_REQUEST,
ADD_ARTICLE_COMMENT_SUCCESS,
ADD_ARTICLE_COMMENT_FAILURE,
//关注用户
UPDATE_ATTENTION_REQUEST,
UPDATE_ATTENTION_SUCCESS,
UPDATE_ATTENTION_FAILURE,
//清除消息内容
MESSAGE_CLEAR_OTHER_LIST,
MESSAGE_CLEAR_COMMENT_LIST,
//回复成功提示
SHOW_REPLY_TIP_MESSAGE,
HIDDEN_REPLY_TIP_MESSAGE,
CLEAR_BADGE_NUM_SUCCESS,
UPDATE_TIP_FLAG_SUCCESS,
} = require('../../constants/actionTypes').default;
const initialState = new InitialState;
export default function grassReducer(state=initialState, action) {
switch(action.type) {
case SET_CONTENT_LIST_ID: {
return state.set('contentListId', action.payload);
}
case SET_CONTENT_CATEGORY_NAME: {
return state.set('contentCategoryName', action.payload);
}
case SET_CONTENT_TIP_FLAG: {
return state.set('contentTipFlag', action.payload);
}
case FETCH_CONTENT_TYPE_REQUEST: {
return state.setIn(['contentType', 'isFetching'], true)
}
case FETCH_CONTENT_TYPE_SUCCESS: {
return state.setIn(['contentType', 'isFetching'], false)
.setIn(['contentType', 'list'], Immutable.fromJS(action.payload))
}
case FETCH_CONTENT_TYPE_FAILURE: {
return state.setIn(['contentType', 'isFetching'], false)
.setIn(['contentType', 'error'], action.payload)
}
case FETCH_COMMENT_LIST_REQUEST: {
return state.setIn(['commentList', 'isFetching'], true)
.setIn(['commentList', 'isPullToRefresh'], action.isRefresh == true)
}
case FETCH_COMMENT_LIST_SUCCESS: {
state = state
.setIn(['commentList', 'isFetching'], false)
.setIn(['commentList', 'list'], Immutable.fromJS((action.payload.list)))
.setIn(['commentList', 'currentPage'], action.payload.currentPage)
.setIn(['commentList', 'endReached'], action.payload.endReached)
.setIn(['commentList', 'isPullToRefresh'], false)
.setIn(['commentList', 'pageCount'], action.payload.pageCount)
.setIn(['commentList', 'shouldShowEmpty'], action.payload.shouldShowEmpty)
return state;
}
case FETCH_COMMENT_LIST_FAILURE: {
return state.setIn(['commentList', 'isFetching'], false)
.setIn(['commentList', 'isPullToRefresh'], false)
.setIn(['commentList', 'error'], action.payload)
.setIn(['commentList', 'shouldShowEmpty'], true);
}
case FETCH_CONTENT_LIST_REQUEST: {
return state.setIn(['contentList', 'isFetching'], true)
.setIn(['contentList', 'isPullToRefresh'], action.isRefresh == true)
}
case FETCH_CONTENT_LIST_SUCCESS: {
state = state
.setIn(['contentList', 'isFetching'], false)
.setIn(['contentList', 'list'], Immutable.fromJS((action.payload.list)))
.setIn(['contentList', 'currentPage'], action.payload.currentPage)
.setIn(['contentList', 'endReached'], action.payload.endReached)
.setIn(['contentList', 'isPullToRefresh'], false)
.setIn(['contentList', 'pageCount'], action.payload.pageCount)
.setIn(['contentList', 'shouldShowEmpty'], action.payload.shouldShowEmpty)
return state;
}
case FETCH_CONTENT_LIST_FAILURE: {
return state.setIn(['contentList', 'isFetching'], false)
.setIn(['contentList', 'isPullToRefresh'], false)
.setIn(['contentList', 'error'], action.payload)
.setIn(['contentList', 'shouldShowEmpty'], true);
}
case ADD_ARTICLE_COMMENT_REQUEST: {
return state.setIn(['addCommentContent', 'isFetching'], true);
}
case ADD_ARTICLE_COMMENT_SUCCESS: {
return state.setIn(['addCommentContent', 'isFetching'], false);
}
case ADD_ARTICLE_COMMENT_FAILURE: {
return state.setIn(['addCommentContent', 'isFetching'], false)
.setIn(['addCommentContent', 'error'], action.payload);
}
case UPDATE_ATTENTION_REQUEST: {
return state.setIn(['updateAttentionContent', 'isFetching'], true);
}
case UPDATE_ATTENTION_SUCCESS: {
state = state.setIn(['updateAttentionContent', 'isFetching'], false);
let list = state.contentList.get('list').toJS();
return state.setIn(['contentList', 'list'], Immutable.fromJS(list.map((item)=> {
if (item.uid == action.optUid) {
return Object.assign({}, item, {
isMutualAttention: !item.isMutualAttention
})
}
return item;
})))
}
case UPDATE_ATTENTION_FAILURE: {
return state.setIn(['updateAttentionContent', 'isFetching'], false)
.setIn(['updateAttentionContent', 'error'], action.payload);
}
case MESSAGE_CLEAR_OTHER_LIST: {
return state.set('contentList', initialState.get('contentList'))
}
case MESSAGE_CLEAR_COMMENT_LIST: {
return state.set('commentList', initialState.get('commentList'))
}
case SHOW_REPLY_TIP_MESSAGE: {
return state.set('tipMessage', Immutable.fromJS(action.payload));
}
case HIDDEN_REPLY_TIP_MESSAGE: {
return state.set('tipMessage', '');
}
case CLEAR_BADGE_NUM_SUCCESS: {
let typeList = state.contentType.get('list').toJS();
return state.setIn(['contentType', 'list'], Immutable.fromJS(typeList.map((item)=> {
if (item.type == action.payload) {
return Object.assign({}, item, {
unReadCount: 0
})
}
return item;
})));
}
case UPDATE_TIP_FLAG_SUCCESS: {
return state.set('contentTipFlag', action.payload);
}
}
return state;
}
... ...
... ... @@ -2,11 +2,13 @@ import {combineReducers} from 'redux';
import app from './app/appReducer';
import message from './message/messageReducer';
import list from './list/listReducer';
import content from './content/contentReducer';
const rootReducer = combineReducers({
app,
message,
list,
content
});
export default rootReducer;
... ...
... ... @@ -4,11 +4,11 @@ import ReactNative from 'react-native';
import MessageService from '../../services/MessageService';
const {
LATEST_MESSAGE_REQUEST,
LATEST_MESSAGE_REQUEST,
LATEST_MESSAGE_SUCCESS,
LATEST_MESSAGE_FAILURE,
MESSAGE_CATEGORY_SELECT,
MESSAGE_CATEGORY_SELECT,
} = require('../../constants/actionTypes').default;
... ... @@ -63,7 +63,7 @@ export function latestMessage() {
let sourcePage = '';
Promise.all([
ReactNative.NativeModules.YH_CommonHelper.sourcePage('YH_RNMessageViewController'),
ReactNative.NativeModules.YH_CommonHelper.sourcePage('YH_RNMessageViewController'),
]).then(result => {
sourcePage = result[0];
return ReactNative.NativeModules.YH_CommonHelper.uid();
... ...
... ... @@ -53,6 +53,15 @@ let defaults = Immutable.fromJS([
url:`http://m.yohobuy.com?openby:yohobuy={"action":"go.ufo","params":{"pagename":"message"}}`,
},
{
id: 7,
inboxCatName: '内容消息',
content: '没有收到新消息',
unReadCount:0,
createTime:'',
img_url: '',
url:`http://m.yohobuy.com?openby:yohobuy={"action":"go.grassmsg","params":{"pagename":"main","title":"内容消息"}}`,
},
{
id: 99,
inboxCatName: '在线客服',
content: '服务时间:08:00-22:30',
... ...
'use strict';
import Request from '../../common/services/NativeRequest';
export default class ContentService {
constructor (host) {
let baseURL = 'http://api.yoho.cn';
if(host){
baseURL = host;
}
this.api = new Request(baseURL);
}
//查询消息类型
async fetchContentType() {
return await this.api.get({
url: '',
body: {
method: 'app.grass.queryInBoxTypeInfo',
}
})
.then(json => {
return json
})
.catch(error => {
throw(error)
})
}
//查询消息,1:获赞收藏,2:关注,3:通知,4:回复评论
async fetchContentList(page, pageSize, type) {
return await this.api.get({
url: '',
body: {
type,
page,
limit: pageSize,
method: 'app.grass.queryInBox',
}
})
.then(json => {
return json
})
.catch(error => {
throw(error)
})
}
//评论(回复)接口
async addArticleComment(content, uid, commentId, addType) {
return await this.api.get({
url: '',
body: {
content,
uid,
commentId,
addType: 1,
method: 'app.grass.addArticleComment',
}
})
.then(json => {
return json
})
.catch(error => {
throw(error)
})
}
//关注用户接口
async updateAttention(uid, optUid, attentionStatus) {
return await this.api.get({
url: '',
body: {
uid: uid,
topicId: 1,
followUid: optUid,
status: attentionStatus, //状态(0:关注,1:取消)
attentionType: 1, //类型(0:话题,1:用户)
authorType: 1,
method: 'app.grass.updateAttention',
}
})
.then(json => {
return json
})
.catch(error => {
throw(error)
})
}
}
... ...
... ... @@ -2177,7 +2177,6 @@
"resolved": "http://registry.npm.taobao.org/center-align/download/center-align-0.1.3.tgz",
"integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=",
"dev": true,
"optional": true,
"requires": {
"align-text": "^0.1.3",
"lazy-cache": "^1.0.3"
... ... @@ -2752,6 +2751,15 @@
"which": "^1.2.14"
}
},
"errno": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
"integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
"dev": true,
"requires": {
"prr": "~1.0.1"
}
},
"error-ex": {
"version": "1.3.1",
"resolved": "http://registry.npm.taobao.org/error-ex/download/error-ex-1.3.1.tgz",
... ... @@ -6764,6 +6772,12 @@
"object-assign": "^4.1.1"
}
},
"prr": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
"dev": true
},
"pseudomap": {
"version": "1.0.2",
"resolved": "http://registry.npm.taobao.org/pseudomap/download/pseudomap-1.0.2.tgz",
... ... @@ -7427,7 +7441,6 @@
"resolved": "http://registry.npm.taobao.org/right-align/download/right-align-0.1.3.tgz",
"integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=",
"dev": true,
"optional": true,
"requires": {
"align-text": "^0.1.1"
}
... ... @@ -7440,6 +7453,222 @@
"glob": "^7.0.5"
}
},
"rocket-bundler": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/rocket-bundler/-/rocket-bundler-0.14.2.tgz",
"integrity": "sha1-pxvcY0lM4fFZpwtTDWC2HxCYISY=",
"dev": true,
"requires": {
"absolute-path": "^0.0.0",
"async": "^2.4.0",
"babel-core": "^6.24.1",
"babel-generator": "^6.24.1",
"babel-plugin-external-helpers": "^6.18.0",
"babel-preset-es2015-node": "^6.1.1",
"babel-preset-fbjs": "^2.1.4",
"babel-preset-react-native": "^2.0.0",
"babel-register": "^6.24.1",
"babylon": "^6.18.0",
"chalk": "^1.1.1",
"concat-stream": "^1.6.0",
"core-js": "^2.2.2",
"debug": "^2.2.0",
"denodeify": "^1.2.1",
"fbjs": "0.8.14",
"graceful-fs": "^4.1.3",
"image-size": "^0.6.0",
"jest-docblock": "20.1.0-echo.1",
"jest-haste-map": "20.1.0-echo.1",
"json-stable-stringify": "^1.0.1",
"json5": "^0.4.0",
"left-pad": "^1.1.3",
"lodash": "^4.16.6",
"merge-stream": "^1.0.1",
"mime-types": "2.1.11",
"mkdirp": "^0.5.1",
"request": "^2.79.0",
"rimraf": "^2.5.4",
"source-map": "^0.5.6",
"temp": "0.8.3",
"throat": "^4.1.0",
"uglify-js": "2.7.5",
"write-file-atomic": "^1.2.0",
"xpipe": "^1.0.5"
},
"dependencies": {
"babel-plugin-react-transform": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/babel-plugin-react-transform/-/babel-plugin-react-transform-2.0.2.tgz",
"integrity": "sha1-UVu/qZaJOYEULZCx+bFjXeKZUQk=",
"dev": true,
"requires": {
"lodash": "^4.6.1"
}
},
"babel-preset-react-native": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/babel-preset-react-native/-/babel-preset-react-native-2.1.0.tgz",
"integrity": "sha1-kBPr2C2hyIECv1iIEP9Z4gnKK4o=",
"dev": true,
"requires": {
"babel-plugin-check-es2015-constants": "^6.5.0",
"babel-plugin-react-transform": "2.0.2",
"babel-plugin-syntax-async-functions": "^6.5.0",
"babel-plugin-syntax-class-properties": "^6.5.0",
"babel-plugin-syntax-flow": "^6.5.0",
"babel-plugin-syntax-jsx": "^6.5.0",
"babel-plugin-syntax-trailing-function-commas": "^6.5.0",
"babel-plugin-transform-class-properties": "^6.5.0",
"babel-plugin-transform-es2015-arrow-functions": "^6.5.0",
"babel-plugin-transform-es2015-block-scoping": "^6.5.0",
"babel-plugin-transform-es2015-classes": "^6.5.0",
"babel-plugin-transform-es2015-computed-properties": "^6.5.0",
"babel-plugin-transform-es2015-destructuring": "^6.5.0",
"babel-plugin-transform-es2015-for-of": "^6.5.0",
"babel-plugin-transform-es2015-function-name": "^6.5.0",
"babel-plugin-transform-es2015-literals": "^6.5.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.5.0",
"babel-plugin-transform-es2015-parameters": "^6.5.0",
"babel-plugin-transform-es2015-shorthand-properties": "^6.5.0",
"babel-plugin-transform-es2015-spread": "^6.5.0",
"babel-plugin-transform-es2015-template-literals": "^6.5.0",
"babel-plugin-transform-flow-strip-types": "^6.5.0",
"babel-plugin-transform-object-assign": "^6.5.0",
"babel-plugin-transform-object-rest-spread": "^6.5.0",
"babel-plugin-transform-react-display-name": "^6.5.0",
"babel-plugin-transform-react-jsx": "^6.5.0",
"babel-plugin-transform-react-jsx-source": "^6.5.0",
"babel-plugin-transform-regenerator": "^6.5.0",
"react-transform-hmr": "^1.0.4"
}
},
"camelcase": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
"integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=",
"dev": true
},
"cliui": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
"integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=",
"dev": true,
"requires": {
"center-align": "^0.1.1",
"right-align": "^0.1.1",
"wordwrap": "0.0.2"
}
},
"core-js": {
"version": "2.6.5",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz",
"integrity": "sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==",
"dev": true
},
"fbjs": {
"version": "0.8.14",
"resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.14.tgz",
"integrity": "sha1-0dviviVMNakeCfMfnNUKQLKg7Rw=",
"dev": true,
"requires": {
"core-js": "^1.0.0",
"isomorphic-fetch": "^2.1.1",
"loose-envify": "^1.0.0",
"object-assign": "^4.1.0",
"promise": "^7.1.1",
"setimmediate": "^1.0.5",
"ua-parser-js": "^0.7.9"
},
"dependencies": {
"core-js": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=",
"dev": true
}
}
},
"jest-docblock": {
"version": "20.1.0-echo.1",
"resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-20.1.0-echo.1.tgz",
"integrity": "sha512-zJPqHgxSlu5AYjyFLoXzwSqTZGeRAbtW9lTrWfjfDWyQCQjPlt9j9s7t3UBoDwUocM7qVNdlrcXPPtBkQR1dJw==",
"dev": true
},
"jest-haste-map": {
"version": "20.1.0-echo.1",
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-20.1.0-echo.1.tgz",
"integrity": "sha512-5hhKBYWNHIgVnGcOtzo0wsjHuMqZ+9RUmKou4py8yhjYmtbwVVVFcuvTBClwXt/NdrQ8JrbCvtHq5b4IWS7ieg==",
"dev": true,
"requires": {
"fb-watchman": "^2.0.0",
"graceful-fs": "^4.1.11",
"jest-docblock": "20.1.0-echo.1",
"micromatch": "^2.3.11",
"sane": "^2.0.0",
"worker-farm": "^1.3.1"
}
},
"json5": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/json5/-/json5-0.4.0.tgz",
"integrity": "sha1-BUNS5MTIDIbAkjh31EneF2pzLI0=",
"dev": true
},
"mime-db": {
"version": "1.23.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.23.0.tgz",
"integrity": "sha1-oxtAcK2uon1zLqMzdApk0OyaZlk=",
"dev": true
},
"mime-types": {
"version": "2.1.11",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.11.tgz",
"integrity": "sha1-wlnEcb2oCKhdbNGTtDCl+uRHOzw=",
"dev": true,
"requires": {
"mime-db": "~1.23.0"
}
},
"uglify-js": {
"version": "2.7.5",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.7.5.tgz",
"integrity": "sha1-RhLAx7qu4rp8SH3kkErhIgefLKg=",
"dev": true,
"requires": {
"async": "~0.2.6",
"source-map": "~0.5.1",
"uglify-to-browserify": "~1.0.0",
"yargs": "~3.10.0"
},
"dependencies": {
"async": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
"integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=",
"dev": true
}
}
},
"wordwrap": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
"integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=",
"dev": true
},
"yargs": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
"integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
"dev": true,
"requires": {
"camelcase": "^1.0.2",
"cliui": "^2.1.0",
"decamelize": "^1.0.0",
"window-size": "0.1.0"
}
}
}
},
"rsvp": {
"version": "3.6.2",
"resolved": "http://registry.npm.taobao.org/rsvp/download/rsvp-3.6.2.tgz",
... ... @@ -8965,8 +9194,7 @@
"version": "1.0.2",
"resolved": "http://registry.npm.taobao.org/uglify-to-browserify/download/uglify-to-browserify-1.0.2.tgz",
"integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=",
"dev": true,
"optional": true
"dev": true
},
"ultron": {
"version": "1.0.2",
... ... @@ -9221,14 +9449,22 @@
"version": "0.1.0",
"resolved": "http://registry.npm.taobao.org/window-size/download/window-size-0.1.0.tgz",
"integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=",
"dev": true,
"optional": true
"dev": true
},
"wordwrap": {
"version": "1.0.0",
"resolved": "http://registry.npm.taobao.org/wordwrap/download/wordwrap-1.0.0.tgz",
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
},
"worker-farm": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.6.0.tgz",
"integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==",
"dev": true,
"requires": {
"errno": "~0.1.7"
}
},
"wrap-ansi": {
"version": "2.1.0",
"resolved": "http://registry.npm.taobao.org/wrap-ansi/download/wrap-ansi-2.1.0.tgz",
... ...
... ... @@ -55,7 +55,8 @@
"babel-jest": "22.4.3",
"babel-preset-react-native": "4.0.0",
"jest": "22.4.3",
"react-test-renderer": "16.3.1"
"react-test-renderer": "16.3.1",
"rocket-bundler": "^0.14.2"
},
"jest": {
"preset": "react-native"
... ...