Authored by 于良

增加用户中心页面 和 下拉刷新控制 review by 盖剑秋

... ... @@ -32,12 +32,13 @@ import appInitialState from './reducers/app/appInitialState';
import homeInitialState from './reducers/home/homeInitialState';
import sectionInitialState from './reducers/section/sectionInitialState';
import postingInitialState from './reducers/posting/postingInitialState';
import userInitialState from './reducers/user/userInitialState';
import userThatNotMeInitialState from './reducers/userThatNotMe/userThatNotMeInitialState';
import HomeContainer from './containers/HomeContainer';
import SectionContainer from './containers/SectionContainer';
import UserContainer from './containers/UserContainer';
import UserThatNotMeContainer from './containers/UserThatNotMeContainer';
import SubjectPostContainer from './containers/SubjectPostContainer';
import PostingContainer from './containers/PostingContainer';
... ... @@ -76,6 +77,7 @@ function getInitialState() {
section: (new sectionInitialState()),
posting: (new postingInitialState()),
user: (new userInitialState()),
userThatNotMe: (new userThatNotMeInitialState()),
};
return _initState;
}
... ... @@ -170,6 +172,7 @@ export default function community(platform) {
hideNavBar={false}
component={SectionContainer}
initial={false}
clone={true}
rightTitle={null}
rightButtonImage={rightImage}
onRight={this.homeOnRight}
... ... @@ -192,6 +195,17 @@ export default function community(platform) {
initial={false}
/>
<Scene
key="UserThatNotMe"
title="用户中心"
hideNavBar={true}
component={UserThatNotMeContainer}
initial={false}
navigationBarStyle={{
backgroundColor: 'transparent',
}}
/>
<Scene
key="SubjectPost"
... ...
... ... @@ -214,8 +214,8 @@ export default class Home extends React.Component {
onPressPost={(url) => {
this.props.onPressPost && this.props.onPressPost(url);
}}
onPressAvatar={(url) => {
this.props.onPressAvatar && this.props.onPressAvatar(url);
onPressAvatar={() => {
this.props.onPressAvatar && this.props.onPressAvatar(rowData.get('author').get('uid'));
}}
onPressSectionTag={() => {
this.props.onPressSectionTag && this.props.onPressSectionTag(rowData.get('section').toJS());
... ... @@ -280,6 +280,7 @@ export default class Home extends React.Component {
renderSectionHeader={this._renderSectionHeader}
renderSeparator={this._renderSeparator}
enableEmptySections={true}
enablePullToRefresh={true}
isOnPullToRefresh={isRefreshing}
onRefreshData={() => {
this.props.onRefresh && this.props.onRefresh();
... ...
... ... @@ -59,7 +59,8 @@ export default class ListCell extends React.Component {
render() {
let data = this.props.data.toJS();
let {author, timeago, isOwner, isTop, isLike, title, desc, thumbs, section, commentCount, likeCount} = data;
let likeImage = isLike ? require('../../images/home/like.png') : require('../../images/home/unlike.png')
let likeImage = isLike ? require('../../images/home/like.png') : require('../../images/home/unlike.png');
return (
<TouchableOpacity
style={styles.row}
... ...
... ... @@ -265,6 +265,7 @@ export default class Section extends React.Component {
renderSectionHeader={this._renderSectionHeader}
renderSeparator={this._renderSeparator}
enableEmptySections={true}
enablePullToRefresh={true}
isOnPullToRefresh={isRefreshing}
onRefreshData={() => {
this.props.onRefresh && this.props.onRefresh();
... ...
... ... @@ -73,8 +73,8 @@ export default class SectionList extends React.Component {
onPressAvatar={(url) => {
this.props.onPressAvatar && this.props.onPressAvatar(url);
}}
onPressSectionTag={(url) => {
this.props.onPressSectionTag && this.props.onPressSectionTag(url);
onPressSectionTag={() => {
this.props.onPressSectionTag && this.props.onPressSectionTag(rowData.get('section').toJS());
}}
onPressComment={(url) => {
this.props.onPressComment && this.props.onPressComment(url);
... ...
... ... @@ -4,7 +4,6 @@ import React from 'react';
import ReactNative from 'react-native';
import ImmutablePropTypes from 'react-immutable-proptypes';
const {
View,
Image,
... ... @@ -18,13 +17,13 @@ const {
export default class UserCenterTop extends React.Component {
static propTypes = {
backgroundImage: React.PropTypes.string,
userInfo: ImmutablePropTypes.contains({
avatar: React.PropTypes.string,
name: React.PropTypes.string.isRequired,
backgroundImage: React.PropTypes.string,
nickName: React.PropTypes.string.isRequired,
sign: React.PropTypes.string,
}),
onPressUserAvatar: React.PropTypes.func,
onPressBackgroundImg: React.PropTypes.func,
};
... ... @@ -34,26 +33,21 @@ export default class UserCenterTop extends React.Component {
}
render() {
console.log('userinfo = ' + this.props.userInfo);
let data = this.props.userInfo;
let {backgroundImage, avatar, name, sign,} = data;
let {backgroundImage, avatar, nickName, sign,} = data;
return (
<View style={styles.container}>
<TouchableHighlight
underlayColor={'transparent'}
onPress={() => {
this.props.onPressBackgroundImg && this.props.onPressBackgroundImg();
}}
>
<Image
style={styles.backgroundImage}
defaultSource={require('../../images/user-bg.png')}
source={{uri:backgroundImage}}
>
<TouchableOpacity
onPress={() => {
this.props.onPressUserAvatar && this.props.onPressUserAvatar();
... ... @@ -64,15 +58,12 @@ export default class UserCenterTop extends React.Component {
defaultSource={require('../../images/avatar-default.png')}
source={{uri:avatar}}
>
</Image>
</TouchableOpacity>
<Text
style={styles.name}
>
{name}
{nickName}
</Text>
<Text
... ... @@ -81,11 +72,9 @@ export default class UserCenterTop extends React.Component {
{sign}
</Text>
</Image>
</TouchableHighlight>
</View>
);
}
}
... ...
'use strict';
import React, {
PropTypes,
} from 'react';
import {
Platform,
Animated,
Image,
StyleSheet,
Text,
TouchableOpacity,
View,
Dimensions,
BackAndroid,
} from 'react-native';
import {Actions} from 'react-native-router-flux';
import _backButtonImage from '../../images/home/menu_back1.png';
const styles = StyleSheet.create({
header: {
backgroundColor: 'transparent',
paddingTop: 0,
top: 0,
...Platform.select({
ios: {
height: 64,
},
android: {
height: 54,
},
}),
right: 0,
left: 0,
// borderBottomWidth: 0.5,
// borderBottomColor: '#828287',
position: 'absolute',
},
backButton: {
width: 60,
height: 37,
position: 'absolute',
...Platform.select({
ios: {
top: 22,
},
android: {
top: 10,
},
}),
left: 6,
padding: 8,
marginTop: 3.5,
flexDirection: 'row',
},
backButtonImage: {
width: 13,
height: 21,
},
image: {
width: Dimensions.get('window').width,
height: (Platform.OS === 'android') ? 50 : 64,
},
});
const propTypes = {
};
const contextTypes = {
};
const defaultProps = {
backButtonImage: _backButtonImage,
};
class UserNavBar extends React.Component {
constructor(props) {
super(props);
this.renderBackButton = this.renderBackButton.bind(this);
this.setAnimationValue = this.setAnimationValue.bind(this);
this.headerColor = this.headerColor.bind(this);
}
componentDidMount() {
this._listener = this.props.scrollValue.addListener(this.setAnimationValue);
}
componentWillUnmount() {
}
setAnimationValue({ value, }) {
if (value <= 2) {
this.headerView.setNativeProps({
style: {
backgroundColor: this.headerColor(value),
},
});
value = value > 1 ? 1 : value;
this.image.setNativeProps({
style: {
opacity: value,
},
});
}
}
//color between rgb(255,255,255) and rgb(0,0,0)
headerColor(progress) {
const red = 255 + (0 - 255) * progress;
const green = 255 + (0 - 255) * progress;
const blue = 255 + (0 - 255) * progress;
if (progress > 1) {
return `rgb(0,0,0)`; //`darkgray`
} else {
return `rgba(${red}, ${green}, ${blue}, ${progress})`;
}
}
renderBackButton() {
let onPress = Actions.pop;
return (
<TouchableOpacity
style={styles.backButton}
onPress={onPress}
>
<Image
style={styles.backButtonImage}
source={this.props.backButtonImage}
/>
</TouchableOpacity>
);
}
getNavBarBackgroundImage(channel) {
let img = require('../../images/nav/boy/navbar_bg.png');
switch (parseInt(channel)) {
case 1:
img = require('../../images/nav/boy/navbar_bg.png');
break;
case 2:
img = require('../../images/nav/girl/navbar_bg.png');
break;
case 3:
img = require('../../images/nav/kid/navbar_bg.png');
break;
case 4:
img = require('../../images/nav/lifestyle/navbar_bg.png');
break;
}
return img;
}
render() {
return (
<Animated.View
ref={(c) => {
this.headerView = c;
}}
style={[
styles.header,
]}
>
<Image
ref={(c) => {
this.image = c;
}}
source={this.getNavBarBackgroundImage(this.props.channel)}
style={[styles.image, {opacity: 0,}]}
>
</Image>
{this.renderBackButton()}
</Animated.View>
);
}
}
UserNavBar.propTypes = propTypes;
UserNavBar.contextTypes = contextTypes;
UserNavBar.defaultProps = defaultProps;
export default UserNavBar;
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import ScrollableTabView from 'react-native-scrollable-tab-view';
import ImmutablePropTypes from 'react-immutable-proptypes';
import UserCenterTop from '../user/UserCenterTop';
import ListCell from '../home/ListCell';
import LoadMoreIndicator from '../../../common/components/LoadMoreIndicator';
import UserNavBar from '../user/UserNavBar';
const {
View,
Text,
ListView,
Platform,
Dimensions,
StyleSheet,
Animated,
} = ReactNative;
export default class UserThatNotMe extends React.Component {
static propTypes = {
user: ImmutablePropTypes.contains({
avatar: React.PropTypes.string,
backgroundImage: React.PropTypes.string,
nickName: React.PropTypes.string.isRequired,
sign: React.PropTypes.string,
}),
posts: ImmutablePropTypes.listOf(
ImmutablePropTypes.contains({
author: ImmutablePropTypes.contains({
avatar: React.PropTypes.string,
uid: React.PropTypes.number,
name: React.PropTypes.string,
}),
timeago: React.PropTypes.string.isRequired,
isOwner: React.PropTypes.bool,
isTop: React.PropTypes.bool,
isLike: React.PropTypes.bool,
title: React.PropTypes.string,
desc: React.PropTypes.string,
thumbs: ImmutablePropTypes.listOf(
ImmutablePropTypes.contains({
src: React.PropTypes.string,
})
),
section: ImmutablePropTypes.contains({
id: React.PropTypes.number,
name: React.PropTypes.string,
}),
commentCount: React.PropTypes.number,
likeCount: React.PropTypes.number,
}),
),
onPressUserAvatar: React.PropTypes.func,
onPressComment: React.PropTypes.func,
onPressLike: React.PropTypes.func,
onPressPosts: React.PropTypes.func,
};
constructor(props) {
super (props);
this._renderHeader = this._renderHeader.bind(this);
this._renderRow = this._renderRow.bind(this);
this._renderSectionHeader = this._renderSectionHeader.bind(this);
this._renderSeparator = this._renderSeparator.bind(this);
this._updateScrollValue = this._updateScrollValue.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
sectionHeaderHasChanged: (s1, s2) => !Immutable.is(s1, s2),
});
this.state = {
scrollValue: new Animated.Value(0),
};
}
componentDidMount() {
}
_renderHeader() {
return (
<UserCenterTop
userInfo={this.props.user}
onPressUserAvatar={this.props.onPressUserAvatar}
onPressBackgroundImg={this.props.onPressBackgroundImg}
/>
);
}
_renderSectionHeader(sectionData, sectionID) {
switch (sectionID) {
case 'posts':
return (
<View style={styles.sectionHeader}>
<Text style={styles.sectionHeaderText}>TA的发布</Text>
<View style={styles.sectionHeaderLine}/>
</View>
);
default:
return null;
}
}
_renderSeparator(sectionID, rowID, adjacentRowHighlighted) {
return (
<View key={'separator' + sectionID + rowID} style={styles.separator}/>
);
}
_renderRow(rowData, sectionID, rowID, highlightRow) {
switch (sectionID) {
case 'posts':
return (
<ListCell
key={sectionID + rowID}
data={rowData}
onPressPost={(url) => {
this.props.onPressPost && this.props.onPressPost(url);
}}
onPressAvatar={(url) => {
this.props.onPressAvatar && this.props.onPressAvatar(url);
}}
onPressSectionTag={() => {
this.props.onPressSectionTag && this.props.onPressSectionTag(rowData.get('section').toJS());
}}
onPressComment={(url) => {
this.props.onPressComment && this.props.onPressComment(url);
}}
onPressLike={() => {
this.props.onPressLike && this.props.onPressLike(rowData);
}}
/>
);
default:
return null;
}
}
_updateScrollValue(value) {
this.state.scrollValue.setValue(value);
}
render() {
let {posts, endReached, isRefreshing, isLoadingMore, isFetching, navigationState} = this.props;
let dataSource = {
posts: posts.toArray(),
}
return (
<View style={styles.container}>
<ListView
ref={(c) => {
this.listView = c;
}}
onScroll={(e) => {
const offsetY = e.nativeEvent.contentOffset.y;
this._updateScrollValue(offsetY / 150);
}}
dataSource={this.dataSource.cloneWithRowsAndSections(dataSource)}
renderHeader={this._renderHeader}
renderRow={this._renderRow}
renderSectionHeader={this._renderSectionHeader}
renderSeparator={this._renderSeparator}
enableEmptySections={true}
enablePullToRefresh={false}
onEndReached={() => {
this.props.onEndReached && this.props.onEndReached();
}}
renderFooter={()=>{
if (endReached) {
return <LoadMoreIndicator
isVisible={true}
text={'没有更多啦'}
/>
} else {
return <LoadMoreIndicator
isVisible={isLoadingMore}
animating={isFetching}
/>
}
}}
/>
<UserNavBar scrollValue={this.state.scrollValue} channel={this.props.channel}/>
</View>
)
}
}
let {width, height} = Dimensions.get('window');
let styles = StyleSheet.create({
container: {
flex: 1,
},
sectionHeader: {
flexDirection: 'row',
height: 46,
alignItems: 'center',
backgroundColor: 'white',
borderTopWidth: 0.5,
borderTopColor: '#e0e0e0',
},
sectionHeaderText: {
fontSize: 18,
marginLeft: 17,
fontFamily: 'Helvetica Light',
},
sectionHeaderLine: {
position: 'absolute',
left: 17,
bottom: 0,
width: width,
height: 0.5,
backgroundColor: '#e0e0e0',
},
separator: {
height: 0.5,
backgroundColor: '#e0e0e0',
},
fly: {
position: 'absolute',
right: 20,
bottom: 20,
},
});
... ...
... ... @@ -14,11 +14,9 @@ export default keyMirror({
SYNC_USER_REQUEST: null,
SYNC_USER_SUCCESS: null,
SYNC_USER_FAILURE: null,
GET_USER_INFO_REQUEST: null,
GET_USER_INFO_SUCCESS: null,
GET_USER_INFO_FAILURE: null,
GO_TO_SECTION: null,
GO_TO_USER_THAT_NOT_ME: null,
GO_TO_SETTING: null,
GO_TO_MESSAGE: null,
GO_TO_SYS_MESSAGE: null,
... ... @@ -70,7 +68,17 @@ export default keyMirror({
USER_REPLY_SUCCESS: null,
USER_REPLY_FAILURE: null,
USER_INFO_REQUEST: null,
USER_INFO_SUCCESS: null,
USER_INFO_FAILURE: null,
USER_TNM_INFO_REQUEST: null,
USER_TNM_INFO_SUCCESS: null,
USER_TNM_INFO_FAILURE: null,
USER_TNM_POSTS_REQUEST: null,
USER_TNM_POSTS_SUCCESS: null,
USER_TNM_POSTS_FAILURE: null,
SETTING_SAVE_REQUEST: null,
SETTING_SAVE_SUCCESS: null,
... ...
... ... @@ -17,7 +17,6 @@ import * as postingActions from '../reducers/posting/postingActions';
import {Actions} from 'react-native-router-flux';
const {
StatusBar,
View,
StyleSheet,
Dimensions,
... ... @@ -100,7 +99,7 @@ class HomeContainer extends React.Component {
componentDidMount() {
InteractionManager.runAfterInteractions(() => {
this.props.actions.getUserInfo(false);
this.props.actions.syncUserInfo(false);
});
}
... ... @@ -133,8 +132,8 @@ class HomeContainer extends React.Component {
console.log('post');
}
_onPressAvatar(url) {
console.log('avatar');
_onPressAvatar(uid) {
this.props.actions.goToUserOrMe(uid);
}
_onPressSectionTag(section) {
... ...
... ... @@ -17,7 +17,6 @@ import {Actions} from 'react-native-router-flux';
import {shouldShowTabBar, shouldHideTabBar} from '../utils/tabBar';
const {
StatusBar,
View,
StyleSheet,
Dimensions,
... ... @@ -84,7 +83,7 @@ class SectionContainer extends React.Component {
componentDidMount() {
if (shouldHideTabBar(this.props.navigationState)) {
NativeModules.YH_CommunityHelper.hideTabBar();
ReactNative.NativeModules.YH_CommunityHelper.hideTabBar();
}
}
... ... @@ -113,8 +112,9 @@ class SectionContainer extends React.Component {
console.log('avatar');
}
_onPressSectionTag(url) {
_onPressSectionTag(section) {
console.log('section tag');
this.props.actions.goToSection(section)
}
_onPressComment(url) {
... ...
'use strict';
import React from 'react';
import ReactNative from 'react-native';
import Immutable, {Map} from 'immutable';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {Actions} from 'react-native-router-flux';
import UserThatNotMe from '../components/userThatNotMe/UserThatNotMe';
import * as userThatNotMeActions from '../reducers/userThatNotMe/userThatNotMeActions';
import {shouldShowTabBar, shouldHideTabBar} from '../utils/tabBar';
const {
StatusBar,
ScrollView,
View,
StyleSheet,
Dimensions,
Platform,
ActionSheetIOS,
InteractionManager,
} = ReactNative;
const actions = [
userThatNotMeActions,
];
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 UserThatNotMeContainer extends React.Component {
constructor(props) {
super(props);
this._onEndReached = this._onEndReached.bind(this);
}
componentDidMount() {
if (shouldHideTabBar(this.props.navigationState)) {
ReactNative.NativeModules.YH_CommunityHelper.hideTabBar();
}
InteractionManager.runAfterInteractions(() => {
this.props.actions.getUserThatNotMeInfo();
});
this._onEndReached();
}
componentWillUnmount() {
if (shouldShowTabBar(this.props.navigationState)) {
Actions.refresh({key: 'Home', showNativeTabBar: true});
}
// this.props.actions.sectionClean();
}
_onPressAvatar(url) {
console.log('avatar');
}
_onPressComment(url) {
console.log('comment');
}
_onPressLike(url) {
console.log('like');
}
_onPressPosts() {
console.log('posts');
}
_onEndReached() {
InteractionManager.runAfterInteractions(() => {
this.props.actions.userThatNotMePosts();
});
}
render() {
let {profile, posts} = this.props.userThatNotMe;
let postsData = Immutable.fromJS(posts.list);
return (
<View style={styles.container}>
<UserThatNotMe
channel={this.props.app.channel}
user={profile}
posts={postsData}
isFetching={posts.isFetching}
endReached={posts.endReached}
isLoadingMore={posts.isFetching}
onEndReached={this._onEndReached}
onPressAvatar={this._onPressAvatar}
onPressComment={this._onPressComment}
onPressLike={this._onPressLike}
onPressReply={this._onPressReply}
onPressPosts={this._onPressPosts}
onPressUserAvatar={this._onPressUserAvatar}
onPressBackgroundImg={this._onPressBackgroundImg}
/>
</View>
);
}
}
let {width, height} = Dimensions.get('window');
let navbarHeight = (Platform.OS === 'android') ? 50 : 64;
let styles = StyleSheet.create({
container: {
top: 0,
marginBottom: 0,
flex: 1,
},
});
export default connect(mapStateToProps, mapDispatchToProps)(UserThatNotMeContainer);
... ...
... ... @@ -23,6 +23,7 @@ const {
HOME_RECOMMENDATION_FAILURE,
GO_TO_SECTION,
GO_TO_USER_THAT_NOT_ME,
POST_LIKE_REQUEST,
POST_LIKE_SUCCESS,
... ... @@ -37,6 +38,22 @@ const {
} = require('../../constants/actionTypes').default;
export function goToUserOrMe(uid) {
return (dispatch, getState) => {
let {user} = getState();
if (uid != 0 && uid == user.profile.uid) {
Actions.User();
} else {
Actions.UserThatNotMe();
dispatch({
type: GO_TO_USER_THAT_NOT_ME,
payload: uid,
});
}
};
}
export function likeOperation(post) {
return (dispatch, getState) => {
... ...
... ... @@ -16,6 +16,7 @@ import home from './home/homeReducer';
import section from './section/sectionReducer';
import posting from './posting/postingReducer'
import user from './user/userReducer';
import userThatNotMe from './userThatNotMe/userThatNotMeReducer';
import setting from './setting/settingReducer';
import { combineReducers } from 'redux';
... ... @@ -32,6 +33,7 @@ const rootReducer = combineReducers({
section,
posting,
user,
userThatNotMe,
setting,
});
... ...
... ... @@ -26,9 +26,10 @@ const {
SYNC_USER_REQUEST,
SYNC_USER_SUCCESS,
SYNC_USER_FAILURE,
GET_USER_INFO_REQUEST,
GET_USER_INFO_SUCCESS,
GET_USER_INFO_FAILURE,
USER_INFO_REQUEST,
USER_INFO_SUCCESS,
USER_INFO_FAILURE,
SET_UID,
... ... @@ -174,40 +175,40 @@ export function goToStatsPage(type) {
}
}
export function getUserInfoRequest() {
export function userInfoRequest() {
return {
type: GET_USER_INFO_REQUEST,
type: USER_INFO_REQUEST,
};
}
export function getUserInfoSuccess(json) {
export function userInfoSuccess(json) {
return {
type: GET_USER_INFO_SUCCESS,
type: USER_INFO_SUCCESS,
payload: json
};
}
export function getUserInfoFailure(error) {
export function userInfoFailure(error) {
return {
type: GET_USER_INFO_FAILURE,
type: USER_INFO_FAILURE,
payload: error
};
}
export function getUserInfoWithUid(ssouid) {
export function getUserInfo(uid) {
return (dispatch, getState) => {
dispatch(getUserInfoRequest());
return new UserService().getUserInfo(ssouid)
dispatch(userInfoRequest());
return new UserService().getUserInfo(uid)
.then(json => {
dispatch(getUserInfoSuccess(json));
dispatch(userInfoSuccess(json));
})
.catch(error => {
dispatch(getUserInfoFailure(error));
dispatch(userInfoFailure(error));
});
};
}
export function getUserInfo(shouldDisplayLoginView=false) {
export function syncUserInfo(shouldDisplayLoginView=false) {
return (dispatch, getState) => {
let {user} = getState();
if (user.profile.uid === 0) {
... ... @@ -255,13 +256,13 @@ export function syncUserWithoutSSOUid(shouldDisplayLoginView) {
return (dispatch, getState) => {
ReactNative.NativeModules.YH_CommunityHelper.uid()
.then(uid => {
return dispatch(syncUserWithSSOUid(uid));
dispatch(syncUserWithSSOUid(uid));
})
.catch(error => {
if (shouldDisplayLoginView) {
ReactNative.NativeModules.YH_CommunityHelper.login()
.then(uid => {
return dispatch(syncUserWithSSOUid(uid));
dispatch(syncUserWithSSOUid(uid));
})
.catch(error => {
__DEV__ && console.log(error);
... ... @@ -302,7 +303,7 @@ function parseSyncUser(json) {
let profile = {
avatar: json.headIcon ? json.headIcon : '',
bgImage: '',
backgroundImage: json.backgroundImage ? json.backgroundImage : '',
nickName: json.nickName ? json.nickName : '',
realName: json.realName ? json.realName : '',
gender: json.gender ? json.gender : '',
... ...
... ... @@ -10,7 +10,7 @@ let InitialState = Record({
error: null,
uid: 0,
avatar: '',
bgImage: '',
backgroundImage: '',
nickName: '',
realName: '',
gender: '',
... ...
... ... @@ -24,9 +24,9 @@ const {
SYNC_USER_REQUEST,
SYNC_USER_SUCCESS,
SYNC_USER_FAILURE,
GET_USER_INFO_REQUEST,
GET_USER_INFO_SUCCESS,
GET_USER_INFO_FAILURE,
USER_INFO_REQUEST,
USER_INFO_SUCCESS,
USER_INFO_FAILURE,
SET_UID,
... ... @@ -83,11 +83,11 @@ export default function user(state = initialState, action) {
}
case SYNC_USER_SUCCESS: {
let {avatar, bgImage, nickName, realName, gender, sign, age, birthday, height, weight, msgCount} = action.payload;
let {avatar, backgroundImage, nickName, realName, gender, sign, age, birthday, height, weight, msgCount} = action.payload;
let nextState = state.setIn(['profile', 'isFetching'], false)
.setIn(['profile', 'error'], null)
.setIn(['profile', 'avatar'], avatar)
.setIn(['profile', 'bgImage'], bgImage)
.setIn(['profile', 'backgroundImage'], backgroundImage)
.setIn(['profile', 'nickName'], nickName)
.setIn(['profile', 'realName'], realName)
.setIn(['profile', 'gender'], gender)
... ...
'use strict'
import ReactNative from 'react-native';
import {Actions} from 'react-native-router-flux';
import UserService from '../../services/UserService';
import timeago from '../../utils/timeago';
const {
USER_TNM_INFO_REQUEST,
USER_TNM_INFO_SUCCESS,
USER_TNM_INFO_FAILURE,
USER_TNM_POSTS_REQUEST,
USER_TNM_POSTS_SUCCESS,
USER_TNM_POSTS_FAILURE,
} = require('../../constants/actionTypes').default;
export function goToUserOrMe(uid) {
return (dispatch, getState) => {
let {user} = getState();
if (user.profile.uid == uid) {
Actions.User();
} else {
Actions.UserThatNotMe();
}
dispatch({
type: GO_TO_USER_OR_ME,
payload: uid,
});
};
}
export function userThatNotMeInfoRequest() {
return {
type: USER_TNM_INFO_REQUEST,
};
}
export function userThatNotMeInfoSuccess(json) {
return {
type: USER_TNM_INFO_SUCCESS,
payload: json
};
}
export function userThatNotMeInfoFailure(error) {
return {
type: USER_TNM_INFO_FAILURE,
payload: error
};
}
export function getUserThatNotMeInfo() {
return (dispatch, getState) => {
let {userThatNotMe} = getState();
if (userThatNotMe.profile.isFetching) {
return;
}
dispatch(userThatNotMeInfoRequest());
let uid = userThatNotMe.profile.uid;
return new UserService().getUserInfo(uid)
.then(json => {
let profile = parseUserThatNotMeInfo(json);
dispatch(userThatNotMeInfoSuccess(profile));
})
.catch(error => {
dispatch(userThatNotMeInfoFailure(error));
});
};
}
export function tnmPostRequest() {
return {
type: USER_TNM_POSTS_REQUEST
};
}
export function tnmPostSuccess(json) {
return {
type: USER_TNM_POSTS_SUCCESS,
payload: json
};
}
export function tnmPostFailure(error) {
return {
type: USER_TNM_POSTS_FAILURE,
payload: error
};
}
export function userThatNotMePosts() {
return (dispatch, getState) => {
let {userThatNotMe} = getState();
if (userThatNotMe.posts.isFetching) {
return;
}
dispatch(tnmPostRequest());
let uid = userThatNotMe.profile.uid;
let lastedTime = userThatNotMe.posts.lastedTime;
let limit = 10;
return new UserService().posts(uid, lastedTime, limit)
.then(json => {
let payload = parseUserThatNotMePosts(json);
let oldList = userThatNotMe.posts.list.toJS();
let list = [...oldList, ...payload.list];
payload.list = list;
dispatch(tnmPostSuccess(payload));
})
.catch(error => {
dispatch(tnmPostFailure(error));
});
};
}
function parseUserThatNotMeInfo(json) {
let profile = {
avatar: json.headIco ? json.headIco : '',
backgroundImage: json.backgroundImage ? json.backgroundImage : '',
nickName: json.nickName ? json.nickName : '',
realName: json.realName ? json.realName : '',
gender: json.gender ? json.gender : '',
sign: json.signature ? json.signature : '',
age: json.age ? json.age : 0,
birthday: json.birthday ? json.birthday : '',
height: json.height ? json.height : 0,
weight: json.weight ? json.weight : 0,
}
return profile;
}
function parseUserThatNotMePosts(json) {
let {lastedTime, list} = json;
let posts = [];
list && list.map((item, i) => {
let {authorInfo, blocks} = item;
let desc = '';
let thumbs = [];
blocks && blocks.map((item, i) => {
let contentData = item.contentData ? item.contentData : '';
if (desc === '' && item.templateKey === 'text') {
desc = decodeURI(contentData);
}
if (item.templateKey === 'image') {
let thumb = {
src: contentData,
};
thumbs.push(thumb);
}
});
let avatar = authorInfo && authorInfo.headIcon ? authorInfo.headIcon : '';
let uid = authorInfo && authorInfo.uid ? authorInfo.uid : 0;
let name = authorInfo && authorInfo.nickName ? authorInfo.nickName : '';
let createTime = item.createTime;
let timeagoStr = timeago(createTime);
let isLike = item.hasPraise === 'Y' ? true : false;
let title = item.postsTitle ? decodeURI(item.postsTitle) : '';
let id = item.id ? item.id : 0;
let post = {
author: {
avatar,
uid,
name,
},
createTime,
timeago: timeagoStr,
isTop: item.isIndexTop === 0 ? false : true,
isLike,
id,
title,
desc,
thumbs,
section: {
id: item.forumCode,
name: item.forumName,
},
commentCount: item.comment,
likeCount: item.praise,
}
posts.push(post);
});
let endReached = posts.length == 0;
return {
lastedTime,
list: posts,
endReached,
}
}
... ...
'use strict'
import {Record, List} from 'immutable'
let InitialState = Record({
profile: new (Record({
isFetching: false,
error: null,
uid: 0,
avatar: '',
backgroundImage: '',
nickName: '',
realName: '',
gender: '',
sign: '',
age: 0,
birthday: '',
height: 0,
weight: 0,
msgCount: '0',
})),
posts: new (Record({
isFetching: false,
error: null,
lastedTime: 0,
list: List(),
endReached: false,
})),
});
export default InitialState;
... ...
'use strict'
import InitialState from './userThatNotMeInitialState';
import Immutable, {List, Record} from 'immutable';
const {
GO_TO_USER_THAT_NOT_ME,
USER_TNM_INFO_REQUEST,
USER_TNM_INFO_SUCCESS,
USER_TNM_INFO_FAILURE,
USER_TNM_POSTS_REQUEST,
USER_TNM_POSTS_SUCCESS,
USER_TNM_POSTS_FAILURE,
} = require('../../constants/actionTypes').default;
const initialState = new InitialState;
export default function userThatNotMe(state = initialState, action) {
if (!(state instanceof InitialState)) {
return initialState.merge(state);
}
switch (action.type) {
case GO_TO_USER_THAT_NOT_ME: {
let nextState = state.setIn(['profile', 'uid'], action.payload);
return nextState;
}
case USER_TNM_INFO_REQUEST: {
let nextState = state.setIn(['profile', 'isFetching'], true)
.setIn(['profile', 'error'], null);
return nextState;
}
case USER_TNM_INFO_SUCCESS: {
let {avatar, backgroundImage, nickName, realName, gender, sign, age, birthday, height, weight} = action.payload;
let nextState = state.setIn(['profile', 'isFetching'], false)
.setIn(['profile', 'error'], null)
.setIn(['profile', 'avatar'], avatar)
.setIn(['profile', 'backgroundImage'], backgroundImage)
.setIn(['profile', 'nickName'], nickName)
.setIn(['profile', 'realName'], realName)
.setIn(['profile', 'gender'], gender)
.setIn(['profile', 'sign'], sign)
.setIn(['profile', 'age'], age)
.setIn(['profile', 'birthday'], birthday)
.setIn(['profile', 'height'], height)
.setIn(['profile', 'weight'], weight);
return nextState;
}
case USER_TNM_INFO_FAILURE: {
let nextState = state.setIn(['profile', 'isFetching'], false)
.setIn(['profile', 'error'], action.payload);
return nextState;
}
case USER_TNM_POSTS_REQUEST: {
let nextState = state.setIn(['posts', 'isFetching'], true)
.setIn(['posts', 'error'], null);
return nextState;
}
case USER_TNM_POSTS_SUCCESS: {
let {lastedTime, list, endReached} = action.payload;
let nextState = state.setIn(['posts', 'isFetching'], false)
.setIn(['posts', 'error'], null)
.setIn(['posts', 'lastedTime'], lastedTime)
.setIn(['posts', 'list'], Immutable.fromJS(list))
.setIn(['posts', 'endReached'], endReached);
return nextState;
}
case USER_TNM_POSTS_FAILURE: {
let nextState = state.setIn(['posts', 'isFetching'], false)
.setIn(['posts', 'error'], action.payload);
return nextState;
}
}
return state;
}
... ...
... ... @@ -8,8 +8,22 @@ export default class UserService {
this.api = new Request();
}
async posts() {
async posts(uid, lastedTime, limit) {
return await this.api.get({
url: '',
body: {
method: 'app.social.getMyPostList',
uid,
lastedTime,
limit,
}
})
.then((json) => {
return json;
})
.catch((error) => {
throw(error);
});
}
async syncUser(uid, appChannel) {
... ...