Unread and date separator layout improved (#319)
<!-- INSTRUCTION: Keep the line below to notify all core developers about this new PR --> @RocketChat/ReactNative <!-- INSTRUCTION: Inform the issue number that this PR closes, or remove the line below --> - [x] Unread and date separator layout - [x] "Start of conversation"/"Loading messages" label ![screen shot 2018-05-30 at 18 10 43](https://user-images.githubusercontent.com/804994/40747867-0424964a-6435-11e8-9293-31cc43c110ab.png) ![screen shot 2018-05-30 at 18 09 05](https://user-images.githubusercontent.com/804994/40747868-04484784-6435-11e8-8c31-92e0776276f0.png) <!-- INSTRUCTION: Tell us more about your PR with screen shots if you can -->
This commit is contained in:
parent
466a57e6b1
commit
22cbcf0b40
|
@ -29,20 +29,6 @@ async function loadMessagesForRoomDDP(...args) {
|
|||
console.warn('loadMessagesForRoomDDP', e);
|
||||
return loadMessagesForRoomRest.call(this, ...args);
|
||||
}
|
||||
|
||||
// }
|
||||
// if (cb) {
|
||||
// cb({ end: data && data.messages.length < 20 });
|
||||
// }
|
||||
// return data.message;
|
||||
// }, (err) => {
|
||||
// if (err) {
|
||||
// if (cb) {
|
||||
// cb({ end: true });
|
||||
// }
|
||||
// return Promise.reject(err);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
|
||||
export default async function loadMessagesForRoom(...args) {
|
||||
|
@ -52,13 +38,14 @@ export default async function loadMessagesForRoom(...args) {
|
|||
try {
|
||||
// eslint-disable-next-line
|
||||
const data = (await (false && this.ddp.status ? loadMessagesForRoomDDP.call(this, ...args) : loadMessagesForRoomRest.call(this, ...args))).map(buildMessage);
|
||||
if (data) {
|
||||
if (data && data.length) {
|
||||
InteractionManager.runAfterInteractions(() => {
|
||||
db.write(() => data.forEach(message => db.create('messages', message, true)));
|
||||
return resolve(data);
|
||||
});
|
||||
} else {
|
||||
return resolve([]);
|
||||
}
|
||||
return resolve([]);
|
||||
} catch (e) {
|
||||
log('loadMessagesForRoom', e);
|
||||
reject(e);
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
import React from 'react';
|
||||
import { View, StyleSheet, Text } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import moment from 'moment';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
dateSeparator: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginVertical: -5
|
||||
},
|
||||
dateSeparatorLine: {
|
||||
borderTopColor: '#eaeaea',
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
flex: 1
|
||||
},
|
||||
dateSeparatorBadge: {
|
||||
color: '#444444',
|
||||
backgroundColor: '#fff',
|
||||
fontSize: 11,
|
||||
paddingHorizontal: 10,
|
||||
transform: [{ scaleY: -1 }]
|
||||
}
|
||||
});
|
||||
|
||||
const DateSeparator = ({ ts }) => {
|
||||
const text = moment(ts).format('MMMM DD, YYYY');
|
||||
return (
|
||||
<View style={styles.dateSeparator}>
|
||||
<View style={styles.dateSeparatorLine} />
|
||||
<Text style={styles.dateSeparatorBadge}>{text}</Text>
|
||||
<View style={styles.dateSeparatorLine} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
DateSeparator.propTypes = {
|
||||
ts: PropTypes.instanceOf(Date)
|
||||
};
|
||||
|
||||
export default DateSeparator;
|
|
@ -6,8 +6,7 @@ import moment from 'moment';
|
|||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import DateSeparator from './DateSeparator';
|
||||
import UnreadSeparator from './UnreadSeparator';
|
||||
import Separator from './Separator';
|
||||
import styles from './styles';
|
||||
import Typing from '../../containers/Typing';
|
||||
import database from '../../lib/realm';
|
||||
|
@ -72,7 +71,7 @@ export class List extends React.Component {
|
|||
onEndReachedThreshold={100}
|
||||
renderFooter={this.props.renderFooter}
|
||||
renderHeader={() => <Typing />}
|
||||
onEndReached={() => this.props.onEndReached(this.data)}
|
||||
onEndReached={() => this.props.onEndReached(this.data[this.data.length - 1])}
|
||||
dataSource={this.dataSource}
|
||||
renderRow={(item, previousItem) => this.props.renderRow(item, previousItem)}
|
||||
initialListSize={20}
|
||||
|
@ -128,17 +127,21 @@ export class ListView extends OldList2 {
|
|||
|
||||
|
||||
if (!previousMessage) {
|
||||
bodyComponents.push(<Separator key={message.ts.toISOString()} ts={message.ts} />);
|
||||
continue; // eslint-disable-line
|
||||
}
|
||||
|
||||
if (this.props.lastOpen &&
|
||||
const showUnreadSeparator = this.props.lastOpen &&
|
||||
moment(message.ts).isAfter(this.props.lastOpen) &&
|
||||
moment(previousMessage.ts).isBefore(this.props.lastOpen)
|
||||
) {
|
||||
bodyComponents.push(<UnreadSeparator key='unread-separator' />);
|
||||
}
|
||||
if (!moment(message.ts).isSame(previousMessage.ts, 'day')) {
|
||||
bodyComponents.push(<DateSeparator key={message.ts.toISOString()} ts={message.ts} />);
|
||||
moment(previousMessage.ts).isBefore(this.props.lastOpen);
|
||||
const showDateSeparator = !moment(message.ts).isSame(previousMessage.ts, 'day');
|
||||
|
||||
if (showUnreadSeparator || showDateSeparator) {
|
||||
bodyComponents.push(<Separator
|
||||
key={message.ts.toISOString()}
|
||||
ts={showDateSeparator ? message.ts : null}
|
||||
unread={showUnreadSeparator}
|
||||
/>);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import React from 'react';
|
||||
import { View, StyleSheet, Text } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import moment from 'moment';
|
||||
import I18n from '../../i18n';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginVertical: 10
|
||||
},
|
||||
line: {
|
||||
borderTopColor: '#eaeaea',
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
flex: 1
|
||||
},
|
||||
text: {
|
||||
color: '#444444',
|
||||
fontSize: 11,
|
||||
paddingHorizontal: 10,
|
||||
transform: [{ scaleY: -1 }]
|
||||
},
|
||||
unreadLine: {
|
||||
borderTopColor: 'red'
|
||||
},
|
||||
unreadText: {
|
||||
color: 'red'
|
||||
}
|
||||
});
|
||||
|
||||
const DateSeparator = ({ ts, unread }) => {
|
||||
const date = ts ? moment(ts).format('MMMM DD, YYYY') : null;
|
||||
if (ts && unread) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text style={[styles.text, styles.unreadText]}>{date}</Text>
|
||||
<View style={[styles.line, styles.unreadLine]} />
|
||||
<Text style={[styles.text, styles.unreadText]}>{I18n.t('unread_messages')}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
if (ts) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={styles.line} />
|
||||
<Text style={styles.text}>{date}</Text>
|
||||
<View style={styles.line} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={[styles.line, styles.unreadLine]} />
|
||||
<Text style={[styles.text, styles.unreadText]}>{I18n.t('unread_messages')}</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
DateSeparator.propTypes = {
|
||||
ts: PropTypes.instanceOf(Date),
|
||||
unread: PropTypes.bool
|
||||
};
|
||||
|
||||
export default DateSeparator;
|
|
@ -1,38 +0,0 @@
|
|||
import React from 'react';
|
||||
import { View, StyleSheet, Text, LayoutAnimation } from 'react-native';
|
||||
import I18n from '../../i18n';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
firstUnread: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginVertical: -7
|
||||
},
|
||||
firstUnreadLine: {
|
||||
borderTopColor: 'red',
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
flex: 1
|
||||
},
|
||||
firstUnreadBadge: {
|
||||
color: 'red',
|
||||
backgroundColor: '#fff',
|
||||
fontSize: 11,
|
||||
paddingHorizontal: 10,
|
||||
transform: [{ scaleY: -1 }]
|
||||
}
|
||||
});
|
||||
|
||||
export default class UnreadSeparator extends React.PureComponent {
|
||||
componentWillUnmount() {
|
||||
LayoutAnimation.linear();
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.firstUnread}>
|
||||
<View style={styles.firstUnreadLine} />
|
||||
<Text style={styles.firstUnreadBadge}>{I18n.t('unread_messages')}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -70,7 +70,8 @@ export default class RoomView extends LoggedView {
|
|||
this.state = {
|
||||
loaded: true,
|
||||
joined: typeof props.rid === 'undefined',
|
||||
room: {}
|
||||
room: {},
|
||||
end: false
|
||||
};
|
||||
this.onReactionPress = this.onReactionPress.bind(this);
|
||||
}
|
||||
|
@ -87,20 +88,15 @@ export default class RoomView extends LoggedView {
|
|||
this.props.editCancel();
|
||||
}
|
||||
|
||||
onEndReached = (data) => {
|
||||
if (this.props.loading || this.state.end) {
|
||||
onEndReached = (lastRowData) => {
|
||||
if (!lastRowData) {
|
||||
this.setState({ end: true });
|
||||
return;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const lastRowData = data[data.length - 1];
|
||||
if (!lastRowData) {
|
||||
return;
|
||||
}
|
||||
// TODO: fix
|
||||
RocketChat.loadMessagesForRoom({ rid: this.rid, t: this.state.room.t, latest: lastRowData.ts }, ({ end }) => end && this.setState({
|
||||
end
|
||||
}));
|
||||
requestAnimationFrame(async() => {
|
||||
const result = await RocketChat.loadMessagesForRoom({ rid: this.rid, t: this.state.room.t, latest: lastRowData.ts });
|
||||
this.setState({ end: result < 20 });
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue