Authored by chenl

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

... ... @@ -16,8 +16,10 @@ 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 MessageContainer from './containers/MessageContainer';
import MessageListContainer from './containers/MessageListContainer';
import {
setPlatform,
... ... @@ -25,11 +27,16 @@ import {
setServiceHost,
} from './reducers/app/appActions';
import {
setListId,
} from './reducers/list/listActions';
function getInitialState() {
const _initState = {
app: (new appInitialState()),
message: (new messageInitialState()),
list: (new listInitialState()),
};
return _initState;
}
... ... @@ -44,11 +51,24 @@ export default function native(platform) {
store.dispatch(setHost(this.props.host));
store.dispatch(setServiceHost(this.props.serviceHost));
return (
<Provider store={store}>
<MessageContainer />
</Provider>
);
let type = this.props.type;
if (type == 'main') {
return (
<Provider store={store}>
<MessageContainer />
</Provider>
);
} else if (type == 'list') {
store.dispatch(setListId(this.props.listId));
return (
<Provider store={store}>
<MessageListContainer />
</Provider>
);
}
return null;
}
});
... ...
'use strict';
import React, {Component} from 'react';
import ReactNative, {
View,
Text,
Image,
ListView,
StyleSheet,
Dimensions,
TouchableOpacity,
InteractionManager,
Platform,
} from 'react-native';
import LoadMoreIndicator from '../../../common/components/LoadMoreIndicator';
export default class MessageList extends Component {
constructor(props) {
super(props);
this.renderRow = this.renderRow.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
});
}
componentDidMount() {
}
renderRow(rowData, sectionID, rowID) {
return (
<TouchableOpacity
onPress={() => {
console.log(rowID)
}}
>
<View style={styles.rowContainer}>
<Text>{rowID}</Text>
</View>
</TouchableOpacity>
);
}
render() {
let {isFetching, endReached, list} = this.props.data;
let isLoadingMore = list.size != 0 && isFetching;
return (
<View style={styles.container}>
<ListView
contentContainerStyle={styles.contentContainer}
dataSource={this.dataSource.cloneWithRows(list.toArray())}
renderRow={this.renderRow}
enableEmptySections={true}
onEndReached={() => {
if (list.size != 0) {
this.props.onEndReached && this.props.onEndReached();
}
}}
renderFooter={() => {
if (endReached) {
return <LoadMoreIndicator
isVisible={true}
text={'暂无更多'}
/>;
} else {
return <LoadMoreIndicator
isVisible={isLoadingMore}
animating={isFetching}
/>;
}
}}
/>
</View>
);
}
}
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f0f0f0',
},
contentContainer: {
},
rowContainer: {
height: 100,
},
});
... ...
... ... @@ -13,14 +13,15 @@ import ReactNative, {
Platform,
} from 'react-native';
import MessageCell from './MessageCell';
export default class Message extends Component {
constructor(props) {
super(props);
this._renderRow = this._renderRow.bind(this);
this.renderRow = this.renderRow.bind(this);
this.trigggePullToRefresh = this.trigggePullToRefresh.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
... ... @@ -28,37 +29,49 @@ export default class Message extends Component {
}
componentDidMount() {
this.trigggePullToRefresh();
}
trigggePullToRefresh() {
if (Platform.OS === 'ios') {
InteractionManager.runAfterInteractions(() => {
this.listView && this.listView.getScrollResponder().startPullToRefresh();
});
} else {
this.props.onRefresh && this.props.onRefresh();
}
}
_renderRow(rowData: object, sectionID: number, rowID: number) {
renderRow(rowData, sectionID, rowID) {
return (
<TouchableOpacity
onPress={() => {
console.log(rowID)
}}
>
<View style={styles.rowContainer}>
<Text>{rowData.get('title')}</Text>
<Text>{rowData.get('detail')}</Text>
</View>
</TouchableOpacity>
);
return <MessageCell
rowID={rowID}
data={rowData}
onPressListItem={this.props.onPressListItem}
/>;
}
render() {
let dataSource = this.props.data.dataSource.toArray();
let {defaults, latest} = this.props.data;
let dataSource = defaults.toArray();
let isPullToRefresh = latest.isFetching;
return (
<View style={styles.container}>
<ListView
ref={(c) => {
this.listView = c;
}}
contentContainerStyle={styles.contentContainer}
dataSource={this.dataSource.cloneWithRows(dataSource)}
renderRow={this._renderRow}
renderRow={this.renderRow}
enableEmptySections={true}
enablePullToRefresh={true}
isOnPullToRefresh={isPullToRefresh}
onRefreshData={() => {
this.props.onRefresh && this.props.onRefresh();
}}
/>
</View>
);
... ... @@ -70,12 +83,9 @@ let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f0f0f0',
backgroundColor: 'white',
},
contentContainer: {
},
rowContainer: {
},
});
... ...
'use strict';
import React, {Component} from 'react';
import ReactNative, {
View,
Text,
Image,
StyleSheet,
Dimensions,
TouchableOpacity,
} from 'react-native';
export default class MessageCell extends Component {
constructor(props) {
super(props);
}
render() {
let {data, rowID} = this.props;
return (
<TouchableOpacity
onPress={() => {
this.props.onPressListItem && this.props.onPressListItem(data);
}}
>
<View style={styles.rowContainer}>
<Image style={styles.icon} source={require('../../../common/images/tag/tip_hot.png')}/>
<View style={styles.right}>
<Text style={styles.title}>{data.get('title')}</Text>
<Text style={styles.detail} numberOfLines={1}>{data.get('detail')}</Text>
</View>
<Text style={styles.count}>10</Text>
</View>
</TouchableOpacity>
);
}
}
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},
contentContainer: {
},
rowContainer: {
flexDirection: 'row',
marginTop: 10,
marginBottom: 10,
},
icon: {
width: 35,
height: 35,
borderRadius: 3,
marginLeft: 10,
},
right: {
marginLeft: 10,
justifyContent: 'center',
},
title: {
color: '#444444',
fontSize: 16,
fontWeight: 'bold',
marginTop: 2,
},
detail: {
color: '#b0b0b0',
fontSize: 12,
marginTop: 2,
},
count: {
position: 'absolute',
top: -8.5,
left: 38,
width: 17,
height: 17,
borderRadius: 17 / 2,
textAlign: 'center',
paddingTop: 1.5,
color: 'white',
fontSize: 12,
backgroundColor: 'red',
}
});
... ...
... ... @@ -7,6 +7,14 @@ export default keyMirror({
SET_SERVICE_HOST: null,
SET_CHANNEL: null,
LATEST_MESSAGE_REQUEST: null,
LATEST_MESSAGE_SUCCESS: null,
LATEST_MESSAGE_FAILURE: null,
SET_LIST_ID: null,
MESSAGE_LIST_REQUEST: null,
MESSAGE_LIST_SUCCESS: null,
MESSAGE_LIST_FAILURE: null,
});
... ...
... ... @@ -47,9 +47,9 @@ class MessageContainer extends Component {
constructor(props) {
super(props);
this._onRefresh = this._onRefresh.bind(this);
this.onRefresh = this.onRefresh.bind(this);
this.onPressListItem = this.onPressListItem.bind(this);
}
componentDidMount() {
... ... @@ -60,8 +60,12 @@ class MessageContainer extends Component {
}
_onRefresh() {
onRefresh() {
this.props.actions.latestMessage();
}
onPressListItem(item) {
NativeModules.YH_CommonHelper.jumpToMessageList(item.toJS());
}
render() {
... ... @@ -70,7 +74,8 @@ class MessageContainer extends Component {
<View style={styles.container}>
<Message
data={message}
onRefresh={this._onRefresh}
onRefresh={this.onRefresh}
onPressListItem={this.onPressListItem}
/>
</View>
);
... ...
'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 listActions from '../reducers/list/listActions';
import MessageList from '../components/list/MessageList';
const actions = [
listActions,
];
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 MessageListContainer extends Component {
constructor(props) {
super(props);
this.onEndReached = this.onEndReached.bind(this);
}
componentDidMount() {
this.props.actions.messageList();
}
componentWillUnmount() {
}
onEndReached() {
this.props.actions.messageList();
}
render() {
let {list} = this.props;
return (
<View style={styles.container}>
<MessageList
data={list}
onEndReached={this.onEndReached}
/>
</View>
);
}
}
let styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default connect(mapStateToProps, mapDispatchToProps)(MessageListContainer);
... ...
import {combineReducers} from 'redux';
import app from './app/appReducer';
import message from './message/messageReducer';
import list from './list/listReducer';
const rootReducer = combineReducers({
app,
message,
list,
});
export default rootReducer;
... ...
'use strict';
import ReactNative from 'react-native';
import MessageListService from '../../services/MessageListService';
const {
SET_LIST_ID,
MESSAGE_LIST_REQUEST,
MESSAGE_LIST_SUCCESS,
MESSAGE_LIST_FAILURE,
} = require('../../constants/actionTypes').default;
export function setListId(id) {
return {
type: SET_LIST_ID,
payload: id,
};
}
export function messageListRequest() {
return {
type: MESSAGE_LIST_REQUEST,
};
}
export function messageListSuccess(json) {
return {
type: MESSAGE_LIST_SUCCESS,
payload: json
};
}
export function messageListFailure(error) {
return {
type: MESSAGE_LIST_FAILURE,
payload: error
};
}
export function messageList() {
return (dispatch, getState) => {
let {app, list} = getState();
if (list.isFetching || list.endReached || list.error) {
return;
}
let page = list.currentPage + 1;
let pageSize = list.pageSize;
dispatch(messageListRequest());
return new MessageListService(app.serviceHost).fetchMessageList('', 0, page, pageSize)
.then(json => {
let payload = parseMessageList(json);
payload.endReached = payload.currentPage == payload.pageCount;
if (payload.currentPage > 1) {
let oldList = list.list.toJS();
let newList = [...oldList, ...payload.list];
payload.list = newList;
}
dispatch(messageListSuccess(payload));
})
.catch(error => {
dispatch(messageListFailure(error));
});
};
}
function parseMessageList(json) {
let currentPage = json && json.page ? json.page : 1;
let pageCount = json && json.total_page ? json.total_page : 0;
let total = json && json.total ? json.total : 0;
let list = json && json.list ? json.list : [];
return {
list,
currentPage,
pageCount,
total,
};
}
... ...
'use strict';
import Immutable, {Record, List, Map} from 'immutable';
let InitialState = Record({
listId: 0,
listType: '',
isFetching: false,
error: null,
list: List(),
currentPage: 0,
pageCount: 0,
pageSize: 60,
total: 0,
endReached: false,
});
export default InitialState;
... ...
'use strict';
import InitialState from './listInitialState';
import Immutable, {Map} from 'immutable';
const {
SET_LIST_ID,
MESSAGE_LIST_REQUEST,
MESSAGE_LIST_SUCCESS,
MESSAGE_LIST_FAILURE,
} = require('../../constants/actionTypes').default;
const initialState = new InitialState;
export default function listReducer(state=initialState, action) {
switch(action.type) {
case SET_LIST_ID: {
return state.set('listId', action.payload);
}
case MESSAGE_LIST_REQUEST: {
return state.set('isFetching', true)
.set('error', null);
}
case MESSAGE_LIST_SUCCESS: {
let {
list,
currentPage,
pageCount,
total,
endReached,
} = action.payload;
let newState = state.set('isFetching', false)
.set('error', null)
.set('list', Immutable.fromJS(list))
.set('currentPage', currentPage)
.set('pageCount', pageCount)
.set('total', total)
.set('endReached', endReached);
return newState;
}
case MESSAGE_LIST_FAILURE: {
return state.set('isFetching', false)
.set('error', action.payload);
}
}
return state;
}
... ...
... ... @@ -4,8 +4,74 @@ import ReactNative from 'react-native';
import MessageService from '../../services/MessageService';
const {
SET_CHANNEL,
LATEST_MESSAGE_REQUEST,
LATEST_MESSAGE_SUCCESS,
LATEST_MESSAGE_FAILURE,
} = require('../../constants/actionTypes').default;
export function latestMessageRequest() {
return {
type: LATEST_MESSAGE_REQUEST,
};
}
export function latestMessageSuccess(json) {
return {
type: LATEST_MESSAGE_SUCCESS,
payload: json
};
}
export function latestMessageFailure(error) {
return {
type: LATEST_MESSAGE_FAILURE,
payload: error
};
}
export function latestMessage() {
return (dispatch, getState) => {
let {app, message} = getState();
let {latest} = message;
if (latest.isFetching) {
return;
}
let fetchMessage = (gender, uid, page, pageSize, sourcePage) => {
dispatch(latestMessageRequest());
return new MessageService(app.serviceHost).fetchInterestList(gender, uid, page, pageSize, sourcePage)
.then(json => {
dispatch(latestMessageSuccess(json));
})
.catch(error => {
dispatch(latestMessageFailure(error));
});
}
let gender = '';
let uid = 0;
let sourcePage = '';
let page = 1;
let pageSize = 60;
Promise.all([
ReactNative.NativeModules.YH_CommonHelper.currentGender(),
ReactNative.NativeModules.YH_CommonHelper.sourcePage('YH_CategoryConcernVC'),
]).then(result => {
gender = result[0];
sourcePage = result[1];
return ReactNative.NativeModules.YH_CommonHelper.uid();
}).then(data => {
uid = data;
fetchMessage(gender, uid, page, pageSize, sourcePage);
})
.catch(error => {
fetchMessage(gender, uid, page, pageSize, sourcePage);
});
};
}
\ No newline at end of file
... ...
... ... @@ -2,7 +2,7 @@
import Immutable, {Record, List, Map} from 'immutable';
let dataSource = Immutable.fromJS([
let defaults = Immutable.fromJS([
{
id: 'member',
title: '会员信息',
... ... @@ -26,8 +26,8 @@ let dataSource = Immutable.fromJS([
]);
let InitialState = Record({
dataSource,
newMessage: new (Record({
defaults,
latest: new (Record({
isFetching: false,
error: null,
data: List(),
... ...
... ... @@ -4,7 +4,9 @@ import InitialState from './messageInitialState';
import Immutable, {Map} from 'immutable';
const {
SET_CHANNEL,
LATEST_MESSAGE_REQUEST,
LATEST_MESSAGE_SUCCESS,
LATEST_MESSAGE_FAILURE,
} = require('../../constants/actionTypes').default;
... ... @@ -12,7 +14,21 @@ const initialState = new InitialState;
export default function messageReducer(state=initialState, action) {
switch(action.type) {
case LATEST_MESSAGE_REQUEST: {
return state.setIn(['latest', 'isFetching'], true)
.setIn(['latest', 'error'], null);
}
case LATEST_MESSAGE_SUCCESS: {
return state.setIn(['latest', 'isFetching'], false)
.setIn(['latest', 'error'], null);
}
case LATEST_MESSAGE_FAILURE: {
return state.setIn(['latest', 'isFetching'], false)
.setIn(['latest', 'error'], action.payload);
}
}
... ...
'use strict';
import Request from '../../common/services/Request';
export default class MessageListService {
constructor (host) {
let baseURL = 'http://api.yoho.cn';
if(host){
baseURL = host;
}
this.api = new Request(baseURL);
}
async fetchMessageList(gender='', uid=0, page=1, limit=60, fromPage='') {
return await this.api.get({
url: '/guang/api/v1/attention/getlist',
body: {
uid,
gender,
page,
limit,
fromPage,
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
}
... ...
... ... @@ -11,5 +11,25 @@ export default class MessageService {
}
this.api = new Request(baseURL);
}
async fetchInterestList(gender='', uid=0, page=1, limit=60, fromPage='') {
return await this.api.get({
url: '/guang/api/v1/attention/getlist',
body: {
uid,
gender,
page,
limit,
fromPage,
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
}
... ...
... ... @@ -86,17 +86,10 @@ export default function native(platform) {
render() {
const store = configureStore(getInitialState());
store.dispatch(setPlatform(platform));
let segment = getInitSegment(this.props.initType);
store.dispatch(setSegment(segment));
if (this.props.initType == 0) {
let gender = this.props.genderType;
store.dispatch(setGender(gender));
}
let type = this.props.type;
if (type == 'detail') {
let id=this.props.id;
let udid=this.props.udid;
let id = this.props.id;
store.dispatch(setId(id));
return (
<Provider store={store}>
... ... @@ -104,6 +97,13 @@ export default function native(platform) {
</Provider>
);
} else {
let segment = getInitSegment(this.props.initType);
store.dispatch(setSegment(segment));
if (this.props.initType == 0) {
let gender = this.props.genderType;
store.dispatch(setGender(gender));
}
return (
<Provider store={store}>
<PlustarContainer />
... ...
No preview for this file type