Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7,418 changes: 4,410 additions & 3,008 deletions __tests__/__snapshots__/Storyshots.test.js.snap

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions app/containers/message/Discussion.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import { CustomIcon } from '../../lib/Icons';
import { DISCUSSION } from './constants';
import { themes } from '../../constants/colors';
import MessageContext from './Context';
import { formatDateThreads } from '../../utils/room';
import { formatDate } from '../../utils/room';

const Discussion = React.memo(({
msg, dcount, dlm, theme
}) => {
let time;
if (dlm) {
time = formatDateThreads(dlm);
time = formatDate(dlm);
}
const buttonText = formatMessageCount(dcount, DISCUSSION);
const { onDiscussionPress } = useContext(MessageContext);
Expand Down
36 changes: 16 additions & 20 deletions app/containers/message/Thread.js
Original file line number Diff line number Diff line change
@@ -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 '../../views/ThreadMessagesView/ThreadDetails';
import I18n from '../../i18n';

const Thread = React.memo(({
msg, tcount, tlm, isThreadRoom, theme, id
Expand All @@ -21,28 +18,27 @@ 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}
theme={theme}
/>
</View>
);
}, (prevProps, nextProps) => {
Expand Down
4 changes: 4 additions & 0 deletions app/containers/message/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,9 @@ export default StyleSheet.create({
},
encrypted: {
justifyContent: 'center'
},
threadDetails: {
flex: 1,
marginLeft: 12
}
});
42 changes: 29 additions & 13 deletions app/utils/room.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,35 @@ export const capitalize = (s) => {
return s.charAt(0).toUpperCase() + s.slice(1);
};

export const formatDate = date => moment(date).calendar(null, {
lastDay: `[${ I18n.t('Yesterday') }]`,
sameDay: 'LT',
lastWeek: 'dddd',
sameElse: 'L'
});

export const formatDateThreads = date => moment(date).calendar(null, {
sameDay: 'LT',
lastDay: `[${ I18n.t('Yesterday') }] LT`,
lastWeek: 'dddd LT',
sameElse: 'LL'
});
export const formatDate = (date) => {
Comment thread
diegolmello marked this conversation as resolved.
Outdated
const now = moment();
const momentDate = moment(date);
const diff = momentDate.diff(now, 'days', true);

// If it is more than one week ago
if (diff < -6) {
let format = momentDate.format('ll');

// If it is same year
if (now.year() === momentDate.year()) {
format = format.replace(momentDate.format('YYYY'), ''); // Remove year
}

return format
.replace(/[рг]\./, '') // Remove year letter from RU/UK locales
.replace(/de/g, '') // Remove preposition from PT
Comment thread
diegolmello marked this conversation as resolved.
Outdated
.replace(/b\.$/, '') // Remove year prefix from SE
.trim() // Remove spaces from the start and the end
.replace(/,$/g, ''); // Remove comma from the end
}

return momentDate.calendar(null, {
sameDay: 'LT',
lastDay: `[${ I18n.t('Yesterday') }] LT`,
lastWeek: 'dddd LT',
sameElse: 'll'
});
};

export const getBadgeColor = ({ subscription, messageId, theme }) => {
if (subscription?.tunreadUser?.includes(messageId)) {
Expand Down
78 changes: 19 additions & 59 deletions app/views/ThreadMessagesView/Item.js
Original file line number Diff line number Diff line change
@@ -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 { formatDate, makeThreadName } from '../../utils/room';
import ThreadDetails from './ThreadDetails';

const styles = StyleSheet.create({
container: {
Expand Down Expand Up @@ -38,48 +38,22 @@ 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'
},
badge: {
width: 12,
height: 12,
borderRadius: 6
threadDetails: {
marginTop: 8
}
});

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;
if (item?.ts) {
time = formatDateThreads(item.ts);
}

let tlm;
if (item?.tlm) {
tlm = formatDateThreads(item.tlm);
time = formatDate(item.ts);
}

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}
Expand All @@ -97,32 +71,17 @@ const Item = ({
<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>
<ThreadDetails
item={item}
user={user}
badgeColor={badgeColor}
toggleFollowThread={toggleFollowThread}
style={styles.threadDetails}
theme={theme}
/>
</View>
{badgeColor
? (
<View style={styles.badgeContainer}>
<View style={[styles.badge, { backgroundColor: badgeColor }]} />
</View>
)
: null}
</View>
</Touch>
</Touchable>
);
};

Expand All @@ -133,7 +92,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);
122 changes: 122 additions & 0 deletions app/views/ThreadMessagesView/ThreadDetails.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
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 { formatDate } from '../../utils/room';
import sharedStyles from '../Styles';

const styles = StyleSheet.create({
container: {
flex: 1,
width: '100%',
flexDirection: 'row',
alignItems: 'center'
},
detailsContainer: {
flex: 1,
marginRight: 48,
flexDirection: 'row'
},
detailContainer: {
flexDirection: 'row',
alignItems: 'center',
marginRight: 8,
maxWidth: '32%'
},
timeDetailContainer: {
flex: 1,
maxWidth: '50%'
},
detailText: {
fontSize: 10,
marginLeft: 2,
...sharedStyles.textSemibold
},
badgeContainer: {
right: 0,
position: 'absolute',
flexDirection: 'row',
alignItems: 'center'
},
badge: {
width: 8,
height: 8,
borderRadius: 4,
marginRight: 8
}
});

const ThreadDetails = ({
item,
user,
badgeColor,
toggleFollowThread,
style,
theme
}) => {
let tlm;
if (item?.tlm) {
tlm = formatDate(item.tlm);
}

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={20} 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={20} color={themes[theme].auxiliaryText} />
<Text style={[styles.detailText, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>{replies}</Text>
</View>

<View style={[styles.detailContainer, styles.timeDetailContainer]}>
<CustomIcon name='clock' size={20} color={themes[theme].auxiliaryText} />
<Text style={[styles.detailText, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>{tlm}</Text>
</View>
</View>

<Touchable style={styles.badgeContainer} onPress={() => toggleFollowThread?.(isFollowing, item.id)}>
<>
{badgeColor ? <View style={[styles.badge, { backgroundColor: badgeColor }]} /> : null }
<CustomIcon
size={24}
name={isFollowing ? 'notification' : 'notification-disabled'}
color={themes[theme].auxiliaryTintColor}
/>
</>
</Touchable>
</View>
);
};
ThreadDetails.propTypes = {
item: PropTypes.object,
user: PropTypes.object,
badgeColor: PropTypes.string,
toggleFollowThread: PropTypes.func,
style: PropTypes.object,
theme: PropTypes.string
};

export default ThreadDetails;
Loading