Add DiscussionDetails and Item for DiscussionsView; update ThreadDetails, BackgroundContainer and DiscussionsView
This commit is contained in:
parent
2b7f589847
commit
dc70bf617b
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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 }}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue