Authored by 张文文

内容消息落地页

... ... @@ -18,9 +18,11 @@ 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 {
setPlatform,
... ... @@ -33,12 +35,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;
}
... ... @@ -68,6 +77,15 @@ export default function native(platform) {
<MessageListContainer />
</Provider>
);
} else if (type == 'content') {
store.dispatch(setContentListId(this.props.listId));
store.dispatch(setContentCategoryName(this.props.categoryName));
store.dispatch(setContentTipFlag(this.props.tipFlag));
return (
<Provider store={store}>
<ContentMessageContainer />
</Provider>
);
}
return null;
}
... ...
/**
* 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;
class ContentMessageTabItemView 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 + 1,
badgeX: imageLayout.x + imageLayout.width / 2 + 14,
})
}
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={'white'} fill={'#CE0824'} strokeWidth={strokeWidth}/>
</Group>
</Surface>
</View>
}
return null;
}
render() {
return <View style={[styles.container, this.props.style]}>
<YH_Image style={styles.icon}
onLayout={this.onImageLayout}
url={YH_Image.getSlicedUrl(this.props.icon, IMAGE_WIDTH, IMAGE_HEIGHT, 2)}/>
<Text style={styles.title}>{this.props.title}</Text>
{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: {
marginTop: 33,
marginBottom: 40,
flexDirection: 'column',
justifyContent: 'center',
paddingLeft: 30,
paddingRight: 30,
},
title: {
fontFamily: 'PingFang-SC-Medium',
fontSize: 14,
marginTop: 15,
color: '#4A4A4A',
textAlign:'center',
},
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',
}
})
export default ContentMessageTabItemView;
... ...
/**
* Created by zzz on 2019/3/5.
*/
'use strict';
import React, {Component} from "react";
import ReactNative, {
View,
Text,
Image,
ListView,
StyleSheet,
Dimensions,
TouchableOpacity,
InteractionManager,
Platform,
RefreshControl,
DeviceEventEmitter,
NativeModules
} from 'react-native';
import YH_PtrRefresh from '../../../common/components/YH_PtrRefresh';
import ContentMessageTabItemView from "./ContentMessageTabItemView";
export default class ContentMessageView extends Component {
constructor(props) {
super(props);
this._renderRow = this._renderRow.bind(this);
this._renderHeader = this._renderHeader.bind(this);
this.dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => !Immutable.is(r1, r2),
});
}
_renderRow(rowData, sectionID, rowID) {
return (
<View>
</View>
);
}
_renderHeader() {
let tipHeight = this.props.contentTipFlag==='0'?40:0;
let tipBorderWidth = this.props.contentTipFlag==='0'?1:0;
let lineHeight = this.props.contentTipFlag==='0'?1:0;
let buttonHeight = this.props.contentTipFlag==='0'?25:0;
let tabButtons = [{
"imgUrl": "http://img12.static.yhbimg.com/article/2018/10/11/18/027af4e8c8a34ec692fabbe07eca39d3f2.png",
"type": 1,
"description": "获赞收藏",
"unReadCount": 0
}, {
"imgUrl": "http://img13.static.yhbimg.com/article/2018/10/11/18/02e78e9d50b4265f09b21d248e3ca17567.png",
"type": 2,
"description": "关注",
"unReadCount": 2
}, {
"imgUrl": "http://img10.static.yhbimg.com/article/2018/10/11/18/013637a9a30d11387e8adfd0d642e35205.png",
"type": 3,
"description": "通知",
"unReadCount": 0
}]
function onNotifyPress() {
NativeModules.YH_CommonHelper.jumpToUpdateNotificationStatus();
}
function onPress(description, messageType) {
}
return (<View>
<View style={[styles.tipContainer,{height: tipHeight}]}>
<Text style={[styles.tipStyle]}>{'开启推送通知,第一时间收到互动消息'}</Text>
<TouchableOpacity style={{width:60, height: buttonHeight}} onPress={()=> {onNotifyPress()}}>
<Text style={[styles.openButton,{borderWidth: tipBorderWidth}]}>{'开启'}</Text>
</TouchableOpacity>
<View style={{backgroundColor: '#EEEEEE', left: 0, right: 0, bottom: 0, position: 'absolute', height: lineHeight}}/>
</View>
<View style={styles.tabContainer}>
{tabButtons.map((button, index)=> {
return <TouchableOpacity style={styles.tabItemContainer} key={index} onPress={()=> {
onPress(button.description, button.type)
}}>
<ContentMessageTabItemView icon={button.imgUrl} title={button.description} badge={button.unReadCount}/>
</TouchableOpacity>
})}
</View>
<View style={{backgroundColor: '#F0F0F0', left: 0, right: 0, bottom: 0, position: 'absolute', height: 10}}/>
</View>
)
}
componentDidMount() {
}
componentWillReceiveProps(nextProps) {
}
render() {
let listData = [];
return (
<View style={styles.container}>
{
Platform.OS === 'ios' ?
<ListView
ref={(c) => {
this.listView = c;
}}
contentContainerStyle={styles.contentContainer}
dataSource={this.dataSource.cloneWithRows(listData)}
renderRow={this._renderRow}
renderHeader={this._renderHeader}
enableEmptySections={true}
enablePullToRefresh={true}
// isOnPullToRefresh={isPullToRefresh}
// onRefreshData={() => {
// this.props.onRefresh && this.props.onRefresh();
// }}
/>
:
<ListView
ref={(c) => {
this.listView = c;
}}
contentContainerStyle={styles.contentContainer}
dataSource={this.dataSource.cloneWithRows(listData)}
renderRow={this._renderRow}
renderHeader={this._renderHeader}
enableEmptySections={true}
enablePullToRefresh={true}
// refreshControl={
// <YH_PtrRefresh
// refreshing={isPullToRefresh}
// onRefresh={() => {
// this.props.onRefresh && this.props.onRefresh();
// }}
// colors={['#000000', '#ff0000']}
// progressBackgroundColor="#ffffff"
// />
// }
/>
}
</View>
);
}
}
let {width} = Dimensions.get('window');
const ROW_COLUMN = 3
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,
},
tipStyle: {
fontFamily: 'PingFang-SC-Medium',
fontSize: 14,
color: '#4A4A4A',
backgroundColor: 'transparent',
textAlign: 'center',
},
openButton: {
fontFamily: 'PingFang-SC-Medium',
fontSize: 14,
color: '#4A4A4A',
borderColor: '#4A4A4A',
borderRadius: 5,
paddingTop: 2,
paddingLeft: 10,
paddingRight: 10,
paddingBottom: 2,
textAlign: 'center',
},
line: {
backgroundColor: '#EEEEEE',
left: 0,
right: 0,
bottom: 0,
position: 'absolute'
},
})
... ...
... ... @@ -26,4 +26,9 @@ 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,
});
... ...
/**
* 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 LoadingIndicator from '../../common/components/LoadingIndicator';
import * as contentActions from "../reducers/content/contentActions";
import ContentMessageView from '../components/content/ContentMessageView';
const actions = [
contentActions,
]
function mapStateToProps(state) {
return {
contentTipFlag: state.content.contentTipFlag,
};
}
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._onEndReached = this._onEndReached.bind(this);
}
componentDidMount() {
}
componentWillUnmount() {
}
onRefresh() {
}
_onEndReached() {
}
render() {
let {
contentTipFlag,
} = this.props
let isFetching = false;
return (
<View style={styles.container}>
<ContentMessageView
ref={(view) => {
this.messageView = view
}}
contentTipFlag={contentTipFlag}/>
<LoadingIndicator isVisible={isFetching}/>
</View>
)
}
}
let styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default connect(mapStateToProps, mapDispatchToProps, null, {withRef: true})(ContentMessageContainer)
... ...
'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,
} = 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,
};
}
... ...
'use strict';
import Immutable, {Record, List, Map} from 'immutable';
let InitialState = Record({
contentListId: 0,
contentCategoryName: '',
contentTipFlag: 0,
});
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,
} = 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);
}
}
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;
... ...
'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);
}
}
... ...