Add DiscussionDetails and Item for DiscussionsView; update ThreadDetails, BackgroundContainer and DiscussionsView

This commit is contained in:
Gerzon Z 2022-01-14 13:38:08 -04:00
parent 2b7f589847
commit dc70bf617b
6 changed files with 238 additions and 52 deletions

View File

@ -35,7 +35,7 @@ const styles = StyleSheet.create({
const BackgroundContainer = ({ theme, text, loading }: IBackgroundContainer) => ( const BackgroundContainer = ({ theme, text, loading }: IBackgroundContainer) => (
<View style={styles.container}> <View style={styles.container}>
<ImageBackground source={{ uri: `message_empty_${theme}` }} style={styles.image} /> <ImageBackground source={{ uri: `message_empty_${theme}` }} style={styles.image} />
{text ? <Text style={[styles.text, { color: themes[theme].auxiliaryTintColor }]}>{text}</Text> : null} {text && !loading ? <Text style={[styles.text, { color: themes[theme].auxiliaryTintColor }]}>{text}</Text> : null}
{loading ? <ActivityIndicator style={styles.text} color={themes[theme].auxiliaryTintColor} /> : null} {loading ? <ActivityIndicator style={styles.text} color={themes[theme].auxiliaryTintColor} /> : null}
</View> </View>
); );

View File

@ -1,11 +1,11 @@
import React from 'react'; import React from 'react';
import { StyleSheet, Text, View } from 'react-native'; import { StyleSheet, Text, View, ViewStyle } from 'react-native';
import Touchable from 'react-native-platform-touchable'; import Touchable from 'react-native-platform-touchable';
import { CustomIcon } from '../lib/Icons'; import { CustomIcon } from '../lib/Icons';
import { themes } from '../constants/colors'; import { themes } from '../constants/colors';
import sharedStyles from '../views/Styles'; import sharedStyles from '../views/Styles';
import { withTheme } from '../theme'; import { useTheme } from '../theme';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
@ -41,8 +41,7 @@ const styles = StyleSheet.create({
interface IThreadDetails { interface IThreadDetails {
item: { item: {
tcount?: number | string; tcount: number | string;
dcount?: number | string;
replies?: any; replies?: any;
id: string; id: string;
}; };
@ -53,25 +52,20 @@ interface IThreadDetails {
toggleFollowThread: Function; toggleFollowThread: Function;
thread: boolean; thread: boolean;
time: string; time: string;
style: object; style: ViewStyle;
theme: string;
} }
const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, thread, time, style, theme }: IThreadDetails) => { const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, style }: IThreadDetails) => {
const { tcount, dcount } = item; const { theme } = useTheme();
let count = tcount || dcount; let { tcount } = item;
if (count! >= 1000) { if (tcount >= 1000) {
count = '+999'; tcount = '+999';
} else if (count! >= 100) {
count = '+99';
} }
let replies = item?.replies?.length ?? 0; let replies = item?.replies?.length ?? 0;
if (replies >= 1000) { if (replies >= 1000) {
replies = '+999'; replies = '+999';
} else if (replies >= 100) {
replies = '+99';
} }
const isFollowing = item.replies?.find((u: any) => u === user?.id); const isFollowing = item.replies?.find((u: any) => u === user?.id);
@ -80,34 +74,31 @@ const ThreadDetails = ({ item, user, badgeColor, toggleFollowThread, thread, tim
<View style={[styles.container, style]}> <View style={[styles.container, style]}>
<View style={styles.detailsContainer}> <View style={styles.detailsContainer}>
<View style={styles.detailContainer}> <View style={styles.detailContainer}>
<CustomIcon name={thread ? 'threads' : 'discussions'} size={24} color={themes[theme].auxiliaryText} /> <CustomIcon name={'threads'} size={24} color={themes[theme!].auxiliaryText} />
<Text style={[styles.detailText, { color: themes[theme].auxiliaryText }]} numberOfLines={1}> <Text style={[styles.detailText, { color: themes[theme!].auxiliaryText }]} numberOfLines={1}>
{count} {tcount}
</Text> </Text>
</View> </View>
<View style={styles.detailContainer}> <View style={styles.detailContainer}>
<CustomIcon name={thread ? 'user' : 'clock'} size={24} color={themes[theme].auxiliaryText} /> <CustomIcon name={'user'} size={24} color={themes[theme!].auxiliaryText} />
<Text style={[styles.detailText, { color: themes[theme].auxiliaryText }]} numberOfLines={1}> <Text style={[styles.detailText, { color: themes[theme!].auxiliaryText }]} numberOfLines={1}>
{thread ? replies : time} {replies}
</Text> </Text>
</View> </View>
</View> </View>
<View style={styles.badgeContainer}>
{thread ? ( {badgeColor ? <View style={[styles.badge, { backgroundColor: badgeColor }]} /> : null}
<View style={styles.badgeContainer}> <Touchable onPress={() => toggleFollowThread?.(isFollowing, item.id)}>
{badgeColor ? <View style={[styles.badge, { backgroundColor: badgeColor }]} /> : null} <CustomIcon
<Touchable onPress={() => toggleFollowThread?.(isFollowing, item.id)}> size={24}
<CustomIcon name={isFollowing ? 'notification' : 'notification-disabled'}
size={24} color={themes[theme!].auxiliaryTintColor}
name={isFollowing ? 'notification' : 'notification-disabled'} />
color={themes[theme].auxiliaryTintColor} </Touchable>
/> </View>
</Touchable>
</View>
) : null}
</View> </View>
); );
}; };
export default withTheme(ThreadDetails); export default ThreadDetails;

View File

@ -0,0 +1,73 @@
import React from 'react';
import { StyleSheet, Text, View, ViewStyle } from 'react-native';
import { CustomIcon } from '../../lib/Icons';
import { themes } from '../../constants/colors';
import sharedStyles from '../Styles';
import { useTheme } 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
}
});
interface IDiscussionDetails {
item: {
dcount: number | string;
replies?: any;
id: string;
};
user: {
id: string;
};
time: string;
style: ViewStyle;
}
const DiscussionDetails = ({ item, time, style }: IDiscussionDetails) => {
const { theme } = useTheme();
let { dcount } = item;
if (dcount >= 1000) {
dcount = '+999';
}
return (
<View style={[styles.container, style]}>
<View style={styles.detailsContainer}>
<View style={styles.detailContainer}>
<CustomIcon name={'discussions'} size={24} color={themes[theme!].auxiliaryText} />
<Text style={[styles.detailText, { color: themes[theme!].auxiliaryText }]} numberOfLines={1}>
{dcount}
</Text>
</View>
<View style={styles.detailContainer}>
<CustomIcon name={'clock'} size={24} color={themes[theme!].auxiliaryText} />
<Text style={[styles.detailText, { color: themes[theme!].auxiliaryText }]} numberOfLines={1}>
{time}
</Text>
</View>
</View>
</View>
);
};
export default DiscussionDetails;

View File

@ -0,0 +1,111 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Touchable from 'react-native-platform-touchable';
import { useTheme } from '../../theme';
import Avatar from '../../containers/Avatar';
import sharedStyles from '../Styles';
import { themes } from '../../constants/colors';
import Markdown from '../../containers/markdown';
import { formatDateThreads, makeThreadName } from '../../utils/room';
import DiscussionDetails from './DiscussionDetails';
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
padding: 16
},
contentContainer: {
flexDirection: 'column',
flex: 1
},
titleContainer: {
flexDirection: 'row',
marginBottom: 2,
justifyContent: 'space-between'
},
title: {
flexShrink: 1,
fontSize: 18,
...sharedStyles.textMedium
},
time: {
fontSize: 14,
marginLeft: 4,
...sharedStyles.textRegular
},
avatar: {
marginRight: 8
},
discussionDetails: {
marginTop: 8
},
messageContainer: {
flexDirection: 'row'
},
markdown: {
flex: 1
}
});
interface IItem {
item: {
id: string;
u: {
username: string;
};
dcount: string | number;
replies?: any;
msg: string;
ts: string;
};
baseUrl: string;
user: {
id: string;
token: string;
};
onPress: {
(...args: any[]): void;
stop(): void;
};
}
const Item = ({ item, baseUrl, user, onPress }: IItem): JSX.Element => {
const { theme } = useTheme();
const username = item?.u?.username;
const date = formatDateThreads(item.ts);
return (
<Touchable
onPress={() => onPress(item)}
testID={`discussions-view-${item.msg}`}
style={{ backgroundColor: themes[theme!].backgroundColor }}>
<View style={styles.container}>
<Avatar style={styles.avatar} text={item?.u?.username} size={36} borderRadius={4} theme={theme} />
<View style={styles.contentContainer}>
<View style={styles.titleContainer}>
<Text style={[styles.title, { color: themes[theme!].titleText }]} numberOfLines={1}>
{username}
</Text>
<Text style={[styles.time, { color: themes[theme!].auxiliaryText }]}>{date}</Text>
</View>
<View style={styles.messageContainer}>
{/* @ts-ignore */}
<Markdown
msg={makeThreadName(item)}
baseUrl={baseUrl}
username={username}
theme={theme!}
numberOfLines={2}
style={[styles.markdown]}
preview
/>
</View>
<DiscussionDetails item={item} user={user} time={date} style={styles.discussionDetails} />
</View>
</View>
</Touchable>
);
};
export default Item;

View File

@ -20,22 +20,37 @@ import { getHeaderTitlePosition } from '../../containers/Header';
import { useTheme } from '../../theme'; import { useTheme } from '../../theme';
import RocketChat from '../../lib/rocketchat'; import RocketChat from '../../lib/rocketchat';
import SearchHeader from '../../containers/SearchHeader'; import SearchHeader from '../../containers/SearchHeader';
import Item from '../ThreadMessagesView/Item'; import Item from './Item';
import styles from './styles'; import styles from './styles';
const API_FETCH_COUNT = 50; const API_FETCH_COUNT = 50;
interface IItem {
rid: string;
drid: string;
prid: string;
id: string;
u: {
username: string;
};
dcount: string | number;
replies?: any;
msg: string;
ts: string;
}
interface IDiscussionsViewProps { interface IDiscussionsViewProps {
navigation: StackNavigationProp<any, 'DiscussionsView'>; navigation: StackNavigationProp<any, 'DiscussionsView'>;
route: RouteProp<any, 'DiscussionsView'>; route: RouteProp<any, 'DiscussionsView'>;
item: { item: IItem;
msg: string;
};
} }
interface IDiscussionsViewState { interface IDiscussionsViewState {
login?: { login: {
user: object; user: {
id: string;
token: string;
};
}; };
server: { server: {
server: string; server: string;
@ -54,8 +69,6 @@ const DiscussionsView = ({ navigation, route }: IDiscussionsViewProps): JSX.Elem
const user = useSelector((state: IDiscussionsViewState) => state.login?.user); const user = useSelector((state: IDiscussionsViewState) => state.login?.user);
const baseUrl = useSelector((state: IDiscussionsViewState) => state.server?.server); const baseUrl = useSelector((state: IDiscussionsViewState) => state.server?.server);
const useRealName = useSelector((state: IDiscussionsViewState) => state.settings?.UI_Use_Real_Name);
const timeFormat = useSelector((state: IDiscussionsViewState) => state.settings?.Message_TimeFormat);
const isMasterDetail = useSelector((state: IDiscussionsViewState) => state.app?.isMasterDetail); const isMasterDetail = useSelector((state: IDiscussionsViewState) => state.app?.isMasterDetail);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@ -174,7 +187,7 @@ const DiscussionsView = ({ navigation, route }: IDiscussionsViewProps): JSX.Elem
}, [navigation, isSearching]); }, [navigation, isSearching]);
const onDiscussionPress = debounce( const onDiscussionPress = debounce(
(item: any) => { (item: IItem) => {
navigation.push('RoomView', { navigation.push('RoomView', {
rid: item.drid, rid: item.drid,
prid: item.rid, prid: item.rid,
@ -186,16 +199,14 @@ const DiscussionsView = ({ navigation, route }: IDiscussionsViewProps): JSX.Elem
true true
); );
const renderItem = ({ item }: any) => ( const renderItem = ({ item }: { item: IItem }) => (
<Item <Item
{...{ {...{
item, item,
user, user,
navigation, navigation,
baseUrl, baseUrl
useRealName
}} }}
timeFormat={timeFormat}
onPress={onDiscussionPress} onPress={onDiscussionPress}
/> />
); );
@ -218,7 +229,7 @@ const DiscussionsView = ({ navigation, route }: IDiscussionsViewProps): JSX.Elem
windowSize={10} windowSize={10}
initialNumToRender={7} initialNumToRender={7}
removeClippedSubviews={isIOS} removeClippedSubviews={isIOS}
onEndReached={() => (isSearching ? searchTotal > API_FETCH_COUNT ?? load() : total > API_FETCH_COUNT ?? load())} onEndReached={() => (searchTotal || total) > API_FETCH_COUNT ?? load()}
ItemSeparatorComponent={List.Separator} ItemSeparatorComponent={List.Separator}
ListFooterComponent={loading ? <ActivityIndicator theme={theme} /> : null} ListFooterComponent={loading ? <ActivityIndicator theme={theme} /> : null}
scrollIndicatorInsets={{ right: 1 }} scrollIndicatorInsets={{ right: 1 }}

View File

@ -1869,7 +1869,7 @@
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 i386"; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
@ -1924,7 +1924,7 @@
COPY_PHASE_STRIP = YES; COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "arm64 i386"; "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;