diff --git a/native/chat/chat-thread-list-item.react.js b/native/chat/chat-thread-list-item.react.js
index bc5fdf8af..56bdcf786 100644
--- a/native/chat/chat-thread-list-item.react.js
+++ b/native/chat/chat-thread-list-item.react.js
@@ -1,214 +1,218 @@
// @flow
import * as React from 'react';
import { Text, View } from 'react-native';
import type { ChatThreadItem } from 'lib/selectors/chat-selectors';
import type { ThreadInfo } from 'lib/types/thread-types';
import type { UserInfo } from 'lib/types/user-types';
import { shortAbsoluteDate } from 'lib/utils/date-utils';
import Button from '../components/button.react';
import ColorSplotch from '../components/color-splotch.react';
import { SingleLine } from '../components/single-line.react';
import ThreadAncestorsLabel from '../components/thread-ancestors-label.react';
import UnreadDot from '../components/unread-dot.react';
import { useColors, useStyles } from '../themes/colors';
import ChatThreadListSeeMoreSidebars from './chat-thread-list-see-more-sidebars.react';
import ChatThreadListSidebar from './chat-thread-list-sidebar.react';
import MessagePreview from './message-preview.react';
import SwipeableThread from './swipeable-thread.react';
type Props = {
+data: ChatThreadItem,
+onPressItem: (
threadInfo: ThreadInfo,
pendingPersonalThreadUserInfo?: UserInfo,
) => void,
+onPressSeeMoreSidebars: (threadInfo: ThreadInfo) => void,
+onSwipeableWillOpen: (threadInfo: ThreadInfo) => void,
+currentlyOpenedSwipeableId: string,
};
function ChatThreadListItem({
data,
onPressItem,
onPressSeeMoreSidebars,
onSwipeableWillOpen,
currentlyOpenedSwipeableId,
}: Props): React.Node {
const styles = useStyles(unboundStyles);
const colors = useColors();
const lastMessage = React.useMemo(() => {
const mostRecentMessageInfo = data.mostRecentMessageInfo;
if (!mostRecentMessageInfo) {
return (
No messages
);
}
return (
);
}, [data.mostRecentMessageInfo, data.threadInfo, styles]);
+ const numOfSidebarsWithExtendedArrow =
+ data.sidebars.filter(sidebarItem => sidebarItem.type === 'sidebar').length -
+ 1;
+
const sidebars = data.sidebars.map((sidebarItem, index) => {
if (sidebarItem.type === 'sidebar') {
const { type, ...sidebarInfo } = sidebarItem;
return (
0}
+ extendArrow={index < numOfSidebarsWithExtendedArrow}
/>
);
} else if (sidebarItem.type === 'seeMore') {
return (
);
} else {
return ;
}
});
const onPress = React.useCallback(() => {
onPressItem(data.threadInfo, data.pendingPersonalThreadUserInfo);
}, [onPressItem, data.threadInfo, data.pendingPersonalThreadUserInfo]);
const threadNameStyle = React.useMemo(() => {
if (!data.threadInfo.currentUser.unread) {
return styles.threadName;
}
return [styles.threadName, styles.unreadThreadName];
}, [
data.threadInfo.currentUser.unread,
styles.threadName,
styles.unreadThreadName,
]);
const lastActivity = shortAbsoluteDate(data.lastUpdatedTime);
const lastActivityStyle = React.useMemo(() => {
if (!data.threadInfo.currentUser.unread) {
return styles.lastActivity;
}
return [styles.lastActivity, styles.unreadLastActivity];
}, [
data.threadInfo.currentUser.unread,
styles.lastActivity,
styles.unreadLastActivity,
]);
return (
<>
{sidebars}
>
);
}
const chatThreadListItemHeight = 70;
const spacerHeight = 6;
const unboundStyles = {
container: {
height: chatThreadListItemHeight,
justifyContent: 'center',
backgroundColor: 'listBackground',
},
content: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
colorSplotch: {
marginLeft: 6,
marginBottom: 12,
},
threadDetails: {
paddingLeft: 12,
paddingRight: 18,
justifyContent: 'center',
flex: 1,
marginTop: 5,
},
lastActivity: {
color: 'listForegroundTertiaryLabel',
fontSize: 14,
marginLeft: 10,
},
unreadLastActivity: {
color: 'listForegroundLabel',
fontWeight: 'bold',
},
noMessages: {
color: 'listForegroundTertiaryLabel',
flex: 1,
fontSize: 14,
fontStyle: 'italic',
},
row: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
threadName: {
color: 'listForegroundSecondaryLabel',
flex: 1,
fontSize: 21,
},
unreadThreadName: {
color: 'listForegroundLabel',
fontWeight: '500',
},
spacer: {
height: spacerHeight,
},
};
export { ChatThreadListItem, chatThreadListItemHeight, spacerHeight };
diff --git a/native/chat/chat-thread-list-sidebar.react.js b/native/chat/chat-thread-list-sidebar.react.js
index c8b9ff510..fee4f1fb2 100644
--- a/native/chat/chat-thread-list-sidebar.react.js
+++ b/native/chat/chat-thread-list-sidebar.react.js
@@ -1,92 +1,117 @@
// @flow
import * as React from 'react';
-import { View, StyleSheet } from 'react-native';
+import { View } from 'react-native';
import type { ThreadInfo, SidebarInfo } from 'lib/types/thread-types';
-import ArrowLong from '../components/arrow-long.react';
+import ExtendedArrow from '../components/arrow-extended.react';
import Arrow from '../components/arrow.react';
+import Button from '../components/button.react';
import UnreadDot from '../components/unread-dot.react';
-import { SidebarItem } from './sidebar-item.react';
+import { useColors, useStyles } from '../themes/colors';
+import { SidebarItem, sidebarHeight } from './sidebar-item.react';
import SwipeableThread from './swipeable-thread.react';
type Props = {
+sidebarInfo: SidebarInfo,
+onPressItem: (threadInfo: ThreadInfo) => void,
+onSwipeableWillOpen: (threadInfo: ThreadInfo) => void,
+currentlyOpenedSwipeableId: string,
+extendArrow: boolean,
};
function ChatThreadListSidebar(props: Props): React.Node {
+ const colors = useColors();
+ const styles = useStyles(unboundStyles);
+
const {
sidebarInfo,
onSwipeableWillOpen,
currentlyOpenedSwipeableId,
onPressItem,
extendArrow = false,
} = props;
let arrow;
if (extendArrow) {
arrow = (
-
-
+
+
);
} else {
arrow = (
);
}
+ const { threadInfo } = sidebarInfo;
+
+ const onPress = React.useCallback(() => onPressItem(threadInfo), [
+ threadInfo,
+ onPressItem,
+ ]);
+
return (
-
+
+
);
}
-const styles = StyleSheet.create({
+const unboundStyles = {
arrow: {
- left: 27.5,
+ left: 28,
position: 'absolute',
top: -12,
},
- chatThreadListContainer: {
- display: 'flex',
- flexDirection: 'row',
- },
- longArrow: {
+ extendedArrow: {
left: 28,
position: 'absolute',
- top: -19,
+ top: -6,
+ },
+ sidebar: {
+ alignItems: 'center',
+ flexDirection: 'row',
+ width: '100%',
+ height: sidebarHeight,
+ paddingLeft: 6,
+ paddingRight: 18,
+ backgroundColor: 'listBackground',
},
swipeableThreadContainer: {
flex: 1,
+ height: '100%',
},
unreadIndicatorContainer: {
- display: 'flex',
- justifyContent: 'center',
+ alignItems: 'center',
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
paddingLeft: 6,
width: 56,
},
-});
+};
export default ChatThreadListSidebar;
diff --git a/native/chat/sidebar-item.react.js b/native/chat/sidebar-item.react.js
index ca799b88e..a7d113f0d 100644
--- a/native/chat/sidebar-item.react.js
+++ b/native/chat/sidebar-item.react.js
@@ -1,84 +1,58 @@
// @flow
import * as React from 'react';
-import { Text } from 'react-native';
+import { Text, View } from 'react-native';
-import type { ThreadInfo, SidebarInfo } from 'lib/types/thread-types';
+import type { SidebarInfo } from 'lib/types/thread-types';
import { shortAbsoluteDate } from 'lib/utils/date-utils';
-import Button from '../components/button.react';
import { SingleLine } from '../components/single-line.react';
-import { useColors, useStyles } from '../themes/colors';
-import type { ViewStyle } from '../types/styles';
+import { useStyles } from '../themes/colors';
type Props = {
+sidebarInfo: SidebarInfo,
- +onPressItem: (threadInfo: ThreadInfo) => void,
- +style?: ?ViewStyle,
};
function SidebarItem(props: Props): React.Node {
const { lastUpdatedTime } = props.sidebarInfo;
const lastActivity = shortAbsoluteDate(lastUpdatedTime);
const { threadInfo } = props.sidebarInfo;
const styles = useStyles(unboundStyles);
const unreadStyle = threadInfo.currentUser.unread ? styles.unread : null;
- const { onPressItem } = props;
- const onPress = React.useCallback(() => onPressItem(threadInfo), [
- threadInfo,
- onPressItem,
- ]);
-
- const colors = useColors();
-
- const sidebarStyle = React.useMemo(() => {
- return [styles.sidebar, props.style];
- }, [props.style, styles.sidebar]);
-
return (
-
+
);
}
const sidebarHeight = 30;
const unboundStyles = {
+ itemContainer: {
+ flexDirection: 'row',
+ height: sidebarHeight,
+ alignItems: 'center',
+ },
unread: {
color: 'listForegroundLabel',
fontWeight: 'bold',
},
- sidebar: {
- height: sidebarHeight,
- flexDirection: 'row',
- display: 'flex',
- paddingLeft: 6,
- paddingRight: 18,
- alignItems: 'center',
- backgroundColor: 'listBackground',
- },
name: {
color: 'listForegroundSecondaryLabel',
flex: 1,
fontSize: 16,
paddingLeft: 3,
paddingBottom: 2,
},
lastActivity: {
color: 'listForegroundTertiaryLabel',
fontSize: 14,
marginLeft: 10,
},
};
export { SidebarItem, sidebarHeight };
diff --git a/native/chat/sidebar-list-modal.react.js b/native/chat/sidebar-list-modal.react.js
index f86a54f60..b32c8589c 100644
--- a/native/chat/sidebar-list-modal.react.js
+++ b/native/chat/sidebar-list-modal.react.js
@@ -1,141 +1,200 @@
// @flow
import * as React from 'react';
-import { TextInput, FlatList, StyleSheet, View } from 'react-native';
+import { TextInput, FlatList, View } from 'react-native';
import { useSearchSidebars } from 'lib/hooks/search-sidebars';
import type { ThreadInfo, SidebarInfo } from 'lib/types/thread-types';
-import ArrowLong from '../components/arrow-long.react';
+import ExtendedArrow from '../components/arrow-extended.react';
+import Arrow from '../components/arrow.react';
+import Button from '../components/button.react';
import Modal from '../components/modal.react';
import Search from '../components/search.react';
import type { RootNavigationProp } from '../navigation/root-navigator.react';
import type { NavigationRoute } from '../navigation/route-names';
-import { useIndicatorStyle } from '../themes/colors';
+import { useColors, useIndicatorStyle, useStyles } from '../themes/colors';
import { waitForModalInputFocus } from '../utils/timers';
import { useNavigateToThread } from './message-list-types';
import { SidebarItem } from './sidebar-item.react';
export type SidebarListModalParams = {
+threadInfo: ThreadInfo,
};
function keyExtractor(sidebarInfo: SidebarInfo) {
return sidebarInfo.threadInfo.id;
}
function getItemLayout(data: ?$ReadOnlyArray, index: number) {
return { length: 24, offset: 24 * index, index };
}
type Props = {
+navigation: RootNavigationProp<'SidebarListModal'>,
+route: NavigationRoute<'SidebarListModal'>,
};
function SidebarListModal(props: Props): React.Node {
const {
listData,
searchState,
setSearchState,
onChangeSearchInputText,
} = useSearchSidebars(props.route.params.threadInfo);
const searchTextInputRef = React.useRef();
const setSearchTextInputRef = React.useCallback(
async (textInput: ?React.ElementRef) => {
searchTextInputRef.current = textInput;
if (!textInput) {
return;
}
await waitForModalInputFocus();
if (searchTextInputRef.current) {
searchTextInputRef.current.focus();
}
},
[],
);
const navigateToThread = useNavigateToThread();
const onPressItem = React.useCallback(
(threadInfo: ThreadInfo) => {
setSearchState({
text: '',
results: new Set(),
});
if (searchTextInputRef.current) {
searchTextInputRef.current.blur();
}
navigateToThread({ threadInfo });
},
[navigateToThread, setSearchState],
);
+ const styles = useStyles(unboundStyles);
+
+ const numOfSidebarsWithExtendedArrow = React.useMemo(
+ () => listData.length - 1,
+ [listData],
+ );
+
const renderItem = React.useCallback(
- (row: { item: SidebarInfo, ... }) => {
+ (row: { item: SidebarInfo, index: number, ... }) => {
+ let extendArrow: boolean = false;
+ if (row.index < numOfSidebarsWithExtendedArrow) {
+ extendArrow = true;
+ }
return (
-
-
-
-
-
-
-
-
-
+
);
},
- [onPressItem],
+ [onPressItem, numOfSidebarsWithExtendedArrow],
);
const indicatorStyle = useIndicatorStyle();
return (
);
}
-const styles = StyleSheet.create({
+function Item(props: {
+ item: SidebarInfo,
+ onPressItem: (threadInfo: ThreadInfo) => void,
+ extendArrow: boolean,
+}): React.Node {
+ const { item, onPressItem, extendArrow } = props;
+ const { threadInfo } = item;
+
+ const onPressButton = React.useCallback(() => onPressItem(threadInfo), [
+ onPressItem,
+ threadInfo,
+ ]);
+
+ const colors = useColors();
+ const styles = useStyles(unboundStyles);
+
+ let arrow;
+ if (extendArrow) {
+ arrow = (
+
+
+
+ );
+ } else {
+ arrow = (
+
+
+
+ );
+ }
+
+ return (
+
+ );
+}
+
+const unboundStyles = {
arrow: {
position: 'absolute',
- top: -19,
+ top: -12,
+ },
+ extendedArrow: {
+ position: 'absolute',
+ top: -6,
},
search: {
marginBottom: 8,
},
sidebar: {
- backgroundColor: 'transparent',
+ backgroundColor: 'listBackground',
paddingLeft: 0,
paddingRight: 5,
},
sidebarItemContainer: {
flex: 1,
},
sidebarRowContainer: {
flex: 1,
flexDirection: 'row',
},
spacer: {
width: 30,
},
-});
+};
export default SidebarListModal;
diff --git a/native/components/arrow-extended.react.js b/native/components/arrow-extended.react.js
new file mode 100644
index 000000000..836aefad9
--- /dev/null
+++ b/native/components/arrow-extended.react.js
@@ -0,0 +1,23 @@
+// @flow
+
+import * as React from 'react';
+import Svg, { Path } from 'react-native-svg';
+
+function ExtendedArrow(): React.Node {
+ return (
+
+ );
+}
+
+export default ExtendedArrow;
diff --git a/native/components/arrow-long.react.js b/native/components/arrow-long.react.js
deleted file mode 100644
index fe5457ca2..000000000
--- a/native/components/arrow-long.react.js
+++ /dev/null
@@ -1,23 +0,0 @@
-// @flow
-
-import * as React from 'react';
-import Svg, { Path } from 'react-native-svg';
-
-function ArrowLong(): React.Node {
- return (
-
- );
-}
-
-export default ArrowLong;