[IMPROVEMENT] Threads layout tweaks (#2686)
* improvement: Thread Details * fix: re-render Thread Messages Item * fix: update snapshots * improve: thread details component * fix: cast replies length * improvement: format date of threads * improvement: thread details styles * fix: wrap text * tests: update snapshot * improvement: use same date format for all dates * Icon size 24 * Remove date * Remove prop drill * Badge position * Badge container tweak * Fix inline style * Move ThreadDetails to containers * Update stories * Fix lint * Remove wrong prop Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
32b1b36e48
commit
4d13689503
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,103 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import Touchable from 'react-native-platform-touchable';
|
||||
|
||||
import { CustomIcon } from '../lib/Icons';
|
||||
import { themes } from '../constants/colors';
|
||||
import sharedStyles from '../views/Styles';
|
||||
import { withTheme } from '../theme';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
detailsContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row'
|
||||
},
|
||||
detailContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginRight: 8
|
||||
},
|
||||
detailText: {
|
||||
fontSize: 10,
|
||||
marginLeft: 2,
|
||||
...sharedStyles.textSemibold
|
||||
},
|
||||
badgeContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
badge: {
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: 4,
|
||||
marginRight: 8
|
||||
}
|
||||
});
|
||||
|
||||
const ThreadDetails = ({
|
||||
item,
|
||||
user,
|
||||
badgeColor,
|
||||
toggleFollowThread,
|
||||
style,
|
||||
theme
|
||||
}) => {
|
||||
let { tcount } = item;
|
||||
if (tcount >= 1000) {
|
||||
tcount = '+999';
|
||||
} else if (tcount >= 100) {
|
||||
tcount = '+99';
|
||||
}
|
||||
|
||||
let replies = item?.replies?.length ?? 0;
|
||||
if (replies >= 1000) {
|
||||
replies = '+999';
|
||||
} else if (replies >= 100) {
|
||||
replies = '+99';
|
||||
}
|
||||
|
||||
const isFollowing = item.replies?.find(u => u === user?.id);
|
||||
|
||||
return (
|
||||
<View style={[styles.container, style]}>
|
||||
<View style={styles.detailsContainer}>
|
||||
<View style={styles.detailContainer}>
|
||||
<CustomIcon name='threads' size={24} color={themes[theme].auxiliaryText} />
|
||||
<Text style={[styles.detailText, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>{tcount}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.detailContainer}>
|
||||
<CustomIcon name='user' size={24} color={themes[theme].auxiliaryText} />
|
||||
<Text style={[styles.detailText, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>{replies}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.badgeContainer}>
|
||||
{badgeColor ? <View style={[styles.badge, { backgroundColor: badgeColor }]} /> : null }
|
||||
<Touchable onPress={() => toggleFollowThread?.(isFollowing, item.id)}>
|
||||
<CustomIcon
|
||||
size={24}
|
||||
name={isFollowing ? 'notification' : 'notification-disabled'}
|
||||
color={themes[theme].auxiliaryTintColor}
|
||||
/>
|
||||
</Touchable>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
ThreadDetails.propTypes = {
|
||||
item: PropTypes.object,
|
||||
user: PropTypes.object,
|
||||
badgeColor: PropTypes.string,
|
||||
toggleFollowThread: PropTypes.func,
|
||||
style: PropTypes.object,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
export default withTheme(ThreadDetails);
|
|
@ -1,15 +1,12 @@
|
|||
import React, { useContext } from 'react';
|
||||
import { View, Text } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import Touchable from 'react-native-platform-touchable';
|
||||
|
||||
import { formatMessageCount } from './utils';
|
||||
import styles from './styles';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
import { THREAD } from './constants';
|
||||
import { themes } from '../../constants/colors';
|
||||
import { formatDateThreads } from '../../utils/room';
|
||||
import MessageContext from './Context';
|
||||
import ThreadDetails from '../ThreadDetails';
|
||||
import I18n from '../../i18n';
|
||||
|
||||
const Thread = React.memo(({
|
||||
msg, tcount, tlm, isThreadRoom, theme, id
|
||||
|
@ -21,28 +18,26 @@ const Thread = React.memo(({
|
|||
const {
|
||||
threadBadgeColor, toggleFollowThread, user, replies
|
||||
} = useContext(MessageContext);
|
||||
const time = formatDateThreads(tlm);
|
||||
const buttonText = formatMessageCount(tcount, THREAD);
|
||||
const isFollowing = replies?.find(u => u === user.id);
|
||||
return (
|
||||
<View style={styles.buttonContainer}>
|
||||
<View
|
||||
style={[styles.button, { backgroundColor: themes[theme].tintColor }]}
|
||||
testID={`message-thread-button-${ msg }`}
|
||||
>
|
||||
<CustomIcon name='threads' size={16} style={[styles.buttonIcon, { color: themes[theme].buttonText }]} />
|
||||
<Text style={[styles.buttonText, { color: themes[theme].buttonText }]}>{buttonText}</Text>
|
||||
<Text style={[styles.buttonText, { color: themes[theme].buttonText }]}>{I18n.t('Reply')}</Text>
|
||||
</View>
|
||||
<Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
||||
{threadBadgeColor ? <View style={[styles.threadBadge, { backgroundColor: threadBadgeColor }]} /> : null}
|
||||
<Touchable onPress={() => toggleFollowThread(isFollowing, id)}>
|
||||
<CustomIcon
|
||||
name={isFollowing ? 'notification' : 'notification-disabled'}
|
||||
size={24}
|
||||
color={themes[theme].auxiliaryText}
|
||||
style={styles.threadBell}
|
||||
/>
|
||||
</Touchable>
|
||||
<ThreadDetails
|
||||
item={{
|
||||
tcount,
|
||||
replies,
|
||||
tlm,
|
||||
id
|
||||
}}
|
||||
user={user}
|
||||
badgeColor={threadBadgeColor}
|
||||
toggleFollowThread={toggleFollowThread}
|
||||
style={styles.threadDetails}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}, (prevProps, nextProps) => {
|
||||
|
|
|
@ -176,5 +176,9 @@ export default StyleSheet.create({
|
|||
},
|
||||
encrypted: {
|
||||
justifyContent: 'center'
|
||||
},
|
||||
threadDetails: {
|
||||
flex: 1,
|
||||
marginLeft: 12
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import Touchable from 'react-native-platform-touchable';
|
||||
|
||||
import { withTheme } from '../../theme';
|
||||
import Avatar from '../../containers/Avatar';
|
||||
import Touch from '../../utils/touch';
|
||||
import sharedStyles from '../Styles';
|
||||
import { themes } from '../../constants/colors';
|
||||
import Markdown from '../../containers/markdown';
|
||||
import { CustomIcon } from '../../lib/Icons';
|
||||
import { formatDateThreads, makeThreadName } from '../../utils/room';
|
||||
import ThreadDetails from '../../containers/ThreadDetails';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
|
@ -38,34 +38,26 @@ const styles = StyleSheet.create({
|
|||
avatar: {
|
||||
marginRight: 8
|
||||
},
|
||||
detailsContainer: {
|
||||
marginTop: 8,
|
||||
flexDirection: 'row'
|
||||
},
|
||||
detailContainer: {
|
||||
marginRight: 8,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
detailText: {
|
||||
fontSize: 10,
|
||||
marginLeft: 2,
|
||||
...sharedStyles.textSemibold
|
||||
},
|
||||
badgeContainer: {
|
||||
marginLeft: 8,
|
||||
justifyContent: 'center'
|
||||
threadDetails: {
|
||||
marginTop: 8
|
||||
},
|
||||
badge: {
|
||||
width: 12,
|
||||
height: 12,
|
||||
borderRadius: 6
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: 4,
|
||||
marginHorizontal: 8,
|
||||
alignSelf: 'center'
|
||||
},
|
||||
messageContainer: {
|
||||
flexDirection: 'row'
|
||||
},
|
||||
markdown: {
|
||||
flex: 1
|
||||
}
|
||||
});
|
||||
|
||||
const Item = ({
|
||||
item, baseUrl, theme, useRealName, user, badgeColor, onPress
|
||||
item, baseUrl, theme, useRealName, user, badgeColor, onPress, toggleFollowThread
|
||||
}) => {
|
||||
const username = (useRealName && item?.u?.name) || item?.u?.username;
|
||||
let time;
|
||||
|
@ -73,13 +65,8 @@ const Item = ({
|
|||
time = formatDateThreads(item.ts);
|
||||
}
|
||||
|
||||
let tlm;
|
||||
if (item?.tlm) {
|
||||
tlm = formatDateThreads(item.tlm);
|
||||
}
|
||||
|
||||
return (
|
||||
<Touch theme={theme} onPress={() => onPress(item)} testID={`thread-messages-view-${ item.msg }`} style={{ backgroundColor: themes[theme].backgroundColor }}>
|
||||
<Touchable onPress={() => onPress(item)} testID={`thread-messages-view-${ item.msg }`} style={{ backgroundColor: themes[theme].backgroundColor }}>
|
||||
<View style={styles.container}>
|
||||
<Avatar
|
||||
style={styles.avatar}
|
||||
|
@ -96,33 +83,19 @@ const Item = ({
|
|||
<Text style={[styles.title, { color: themes[theme].titleText }]} numberOfLines={1}>{username}</Text>
|
||||
<Text style={[styles.time, { color: themes[theme].auxiliaryText }]}>{time}</Text>
|
||||
</View>
|
||||
<Markdown msg={makeThreadName(item)} baseUrl={baseUrl} username={username} theme={theme} numberOfLines={2} preview />
|
||||
<View style={styles.detailsContainer}>
|
||||
<View style={styles.detailContainer}>
|
||||
<CustomIcon name='threads' size={20} color={themes[theme].auxiliaryText} />
|
||||
<Text style={[styles.detailText, { color: themes[theme].auxiliaryText }]}>{item?.tcount}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.detailContainer}>
|
||||
<CustomIcon name='user' size={20} color={themes[theme].auxiliaryText} />
|
||||
<Text style={[styles.detailText, { color: themes[theme].auxiliaryText }]}>{item?.replies?.length}</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.detailContainer}>
|
||||
<CustomIcon name='clock' size={20} color={themes[theme].auxiliaryText} />
|
||||
<Text style={[styles.detailText, { color: themes[theme].auxiliaryText }]}>{tlm}</Text>
|
||||
</View>
|
||||
<View style={styles.messageContainer}>
|
||||
<Markdown msg={makeThreadName(item)} baseUrl={baseUrl} username={username} theme={theme} numberOfLines={2} style={[styles.markdown]} preview />
|
||||
{badgeColor ? <View style={[styles.badge, { backgroundColor: badgeColor }]} /> : null }
|
||||
</View>
|
||||
<ThreadDetails
|
||||
item={item}
|
||||
user={user}
|
||||
toggleFollowThread={toggleFollowThread}
|
||||
style={styles.threadDetails}
|
||||
/>
|
||||
</View>
|
||||
{badgeColor
|
||||
? (
|
||||
<View style={styles.badgeContainer}>
|
||||
<View style={[styles.badge, { backgroundColor: badgeColor }]} />
|
||||
</View>
|
||||
)
|
||||
: null}
|
||||
</View>
|
||||
</Touch>
|
||||
</Touchable>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -133,7 +106,8 @@ Item.propTypes = {
|
|||
useRealName: PropTypes.bool,
|
||||
user: PropTypes.object,
|
||||
badgeColor: PropTypes.string,
|
||||
onPress: PropTypes.func
|
||||
onPress: PropTypes.func,
|
||||
toggleFollowThread: PropTypes.func
|
||||
};
|
||||
|
||||
export default withTheme(Item);
|
||||
|
|
|
@ -33,6 +33,8 @@ import { isIOS } from '../../utils/deviceInfo';
|
|||
import { getBadgeColor, makeThreadName } from '../../utils/room';
|
||||
import { getHeaderTitlePosition } from '../../containers/Header';
|
||||
import SearchHeader from './SearchHeader';
|
||||
import EventEmitter from '../../utils/events';
|
||||
import { LISTENER } from '../../containers/Toast';
|
||||
|
||||
const API_FETCH_COUNT = 50;
|
||||
|
||||
|
@ -410,6 +412,15 @@ class ThreadMessagesView extends React.Component {
|
|||
this.setState({ currentFilter: filter, displayingThreads });
|
||||
}
|
||||
|
||||
toggleFollowThread = async(isFollowingThread, tmid) => {
|
||||
try {
|
||||
await RocketChat.toggleFollowMessage(tmid, !isFollowingThread);
|
||||
EventEmitter.emit(LISTENER, { message: isFollowingThread ? I18n.t('Unfollowed_thread') : I18n.t('Following_thread') });
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
}
|
||||
|
||||
renderItem = ({ item }) => {
|
||||
const {
|
||||
user, navigation, baseUrl, useRealName
|
||||
|
@ -426,6 +437,7 @@ class ThreadMessagesView extends React.Component {
|
|||
badgeColor
|
||||
}}
|
||||
onPress={this.onThreadPress}
|
||||
toggleFollowThread={this.toggleFollowThread}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -469,11 +469,6 @@ export default ({ theme }) => {
|
|||
tcount={1}
|
||||
tlm={date}
|
||||
/>
|
||||
<Message
|
||||
msg='How are you?'
|
||||
tcount={9999}
|
||||
tlm={date}
|
||||
/>
|
||||
<Message
|
||||
msg="I'm fine!"
|
||||
tmid='1'
|
||||
|
|
Loading…
Reference in New Issue