forked from Ivasoft/mattermost-mobile
Moves collapse animation to FlatList, updates timings (#6220)
* Moves collapse animation to FlatList, updates timings * dev review * filters unreads from other categories & removes duplicate Co-authored-by: Elias Nahum <nahumhbl@gmail.com> Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
@@ -2,129 +2,109 @@
|
|||||||
|
|
||||||
exports[`components/channel_list/categories/body/channel_item should match snapshot 1`] = `
|
exports[`components/channel_list/categories/body/channel_item should match snapshot 1`] = `
|
||||||
<View
|
<View
|
||||||
animatedStyle={
|
accessible={true}
|
||||||
Object {
|
|
||||||
"value": Object {
|
|
||||||
"height": 40,
|
|
||||||
"marginVertical": 2,
|
|
||||||
"opacity": 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
collapsable={false}
|
collapsable={false}
|
||||||
|
focusable={true}
|
||||||
|
onClick={[Function]}
|
||||||
|
onResponderGrant={[Function]}
|
||||||
|
onResponderMove={[Function]}
|
||||||
|
onResponderRelease={[Function]}
|
||||||
|
onResponderTerminate={[Function]}
|
||||||
|
onResponderTerminationRequest={[Function]}
|
||||||
|
onStartShouldSetResponder={[Function]}
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"height": 40,
|
|
||||||
"marginVertical": 2,
|
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
accessible={true}
|
|
||||||
collapsable={false}
|
|
||||||
focusable={true}
|
|
||||||
onClick={[Function]}
|
|
||||||
onResponderGrant={[Function]}
|
|
||||||
onResponderMove={[Function]}
|
|
||||||
onResponderRelease={[Function]}
|
|
||||||
onResponderTerminate={[Function]}
|
|
||||||
onResponderTerminationRequest={[Function]}
|
|
||||||
onStartShouldSetResponder={[Function]}
|
|
||||||
style={
|
style={
|
||||||
Object {
|
Array [
|
||||||
"opacity": 1,
|
Object {
|
||||||
}
|
"alignItems": "center",
|
||||||
|
"flexDirection": "row",
|
||||||
|
"minHeight": 40,
|
||||||
|
"paddingHorizontal": 20,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
undefined,
|
||||||
|
Object {
|
||||||
|
"minHeight": 40,
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
testID="channel_list_item.hello.collapsed.true"
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
style={
|
style={
|
||||||
Array [
|
Object {
|
||||||
Object {
|
"flex": 1,
|
||||||
"alignItems": "center",
|
"flexDirection": "row",
|
||||||
"flexDirection": "row",
|
}
|
||||||
"minHeight": 40,
|
|
||||||
"paddingHorizontal": 20,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
undefined,
|
|
||||||
Object {
|
|
||||||
"minHeight": 40,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
testID="channel_list_item.hello.collapsed.false"
|
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
style={
|
style={
|
||||||
Object {
|
Array [
|
||||||
"flex": 1,
|
Object {
|
||||||
"flexDirection": "row",
|
"alignItems": "center",
|
||||||
}
|
"justifyContent": "center",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"height": 24,
|
||||||
|
"width": 24,
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<View
|
<Icon
|
||||||
|
name="globe"
|
||||||
style={
|
style={
|
||||||
Array [
|
Array [
|
||||||
Object {
|
Object {
|
||||||
"alignItems": "center",
|
"color": "rgba(255,255,255,0.4)",
|
||||||
"justifyContent": "center",
|
|
||||||
},
|
},
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
Object {
|
Object {
|
||||||
"height": 24,
|
"fontSize": 24,
|
||||||
"width": 24,
|
"left": 1,
|
||||||
},
|
},
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
testID="undefined.public"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Text
|
||||||
|
ellipsizeMode="tail"
|
||||||
|
numberOfLines={1}
|
||||||
|
style={
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"fontFamily": "OpenSans",
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontWeight": "400",
|
||||||
|
"lineHeight": 24,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"color": "rgba(255,255,255,0.72)",
|
||||||
|
"marginTop": -1,
|
||||||
|
"paddingLeft": 12,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
testID="channel_list_item.hello.display_name"
|
||||||
>
|
>
|
||||||
<Icon
|
Hello!
|
||||||
name="globe"
|
</Text>
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"color": "rgba(255,255,255,0.4)",
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
Object {
|
|
||||||
"fontSize": 24,
|
|
||||||
"left": 1,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="undefined.public"
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View>
|
|
||||||
<Text
|
|
||||||
ellipsizeMode="tail"
|
|
||||||
numberOfLines={1}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"fontFamily": "OpenSans",
|
|
||||||
"fontSize": 16,
|
|
||||||
"fontWeight": "400",
|
|
||||||
"lineHeight": 24,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"color": "rgba(255,255,255,0.72)",
|
|
||||||
"marginTop": -1,
|
|
||||||
"paddingLeft": 12,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="channel_list_item.hello.display_name"
|
|
||||||
>
|
|
||||||
Hello!
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -133,129 +113,109 @@ exports[`components/channel_list/categories/body/channel_item should match snaps
|
|||||||
|
|
||||||
exports[`components/channel_list/categories/body/channel_item should match snapshot when it has a draft 1`] = `
|
exports[`components/channel_list/categories/body/channel_item should match snapshot when it has a draft 1`] = `
|
||||||
<View
|
<View
|
||||||
animatedStyle={
|
accessible={true}
|
||||||
Object {
|
|
||||||
"value": Object {
|
|
||||||
"height": 40,
|
|
||||||
"marginVertical": 2,
|
|
||||||
"opacity": 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
collapsable={false}
|
collapsable={false}
|
||||||
|
focusable={true}
|
||||||
|
onClick={[Function]}
|
||||||
|
onResponderGrant={[Function]}
|
||||||
|
onResponderMove={[Function]}
|
||||||
|
onResponderRelease={[Function]}
|
||||||
|
onResponderTerminate={[Function]}
|
||||||
|
onResponderTerminationRequest={[Function]}
|
||||||
|
onStartShouldSetResponder={[Function]}
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"height": 40,
|
|
||||||
"marginVertical": 2,
|
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
accessible={true}
|
|
||||||
collapsable={false}
|
|
||||||
focusable={true}
|
|
||||||
onClick={[Function]}
|
|
||||||
onResponderGrant={[Function]}
|
|
||||||
onResponderMove={[Function]}
|
|
||||||
onResponderRelease={[Function]}
|
|
||||||
onResponderTerminate={[Function]}
|
|
||||||
onResponderTerminationRequest={[Function]}
|
|
||||||
onStartShouldSetResponder={[Function]}
|
|
||||||
style={
|
style={
|
||||||
Object {
|
Array [
|
||||||
"opacity": 1,
|
Object {
|
||||||
}
|
"alignItems": "center",
|
||||||
|
"flexDirection": "row",
|
||||||
|
"minHeight": 40,
|
||||||
|
"paddingHorizontal": 20,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
undefined,
|
||||||
|
Object {
|
||||||
|
"minHeight": 40,
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
testID="channel_list_item.hello.collapsed.true"
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
style={
|
style={
|
||||||
Array [
|
Object {
|
||||||
Object {
|
"flex": 1,
|
||||||
"alignItems": "center",
|
"flexDirection": "row",
|
||||||
"flexDirection": "row",
|
}
|
||||||
"minHeight": 40,
|
|
||||||
"paddingHorizontal": 20,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
undefined,
|
|
||||||
Object {
|
|
||||||
"minHeight": 40,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
testID="channel_list_item.hello.collapsed.false"
|
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
style={
|
style={
|
||||||
Object {
|
Array [
|
||||||
"flex": 1,
|
Object {
|
||||||
"flexDirection": "row",
|
"alignItems": "center",
|
||||||
}
|
"justifyContent": "center",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"height": 24,
|
||||||
|
"width": 24,
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
|
]
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<View
|
<Icon
|
||||||
|
name="pencil-outline"
|
||||||
style={
|
style={
|
||||||
Array [
|
Array [
|
||||||
Object {
|
Object {
|
||||||
"alignItems": "center",
|
"color": "rgba(255,255,255,0.4)",
|
||||||
"justifyContent": "center",
|
|
||||||
},
|
},
|
||||||
|
undefined,
|
||||||
|
undefined,
|
||||||
Object {
|
Object {
|
||||||
"height": 24,
|
"fontSize": 24,
|
||||||
"width": 24,
|
"left": 2,
|
||||||
},
|
},
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
testID="undefined.draft"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View>
|
||||||
|
<Text
|
||||||
|
ellipsizeMode="tail"
|
||||||
|
numberOfLines={1}
|
||||||
|
style={
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"fontFamily": "OpenSans",
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontWeight": "400",
|
||||||
|
"lineHeight": 24,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"color": "rgba(255,255,255,0.72)",
|
||||||
|
"marginTop": -1,
|
||||||
|
"paddingLeft": 12,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
testID="channel_list_item.hello.display_name"
|
||||||
>
|
>
|
||||||
<Icon
|
Hello!
|
||||||
name="pencil-outline"
|
</Text>
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"color": "rgba(255,255,255,0.4)",
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
Object {
|
|
||||||
"fontSize": 24,
|
|
||||||
"left": 2,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="undefined.draft"
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View>
|
|
||||||
<Text
|
|
||||||
ellipsizeMode="tail"
|
|
||||||
numberOfLines={1}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"fontFamily": "OpenSans",
|
|
||||||
"fontSize": 16,
|
|
||||||
"fontWeight": "400",
|
|
||||||
"lineHeight": 24,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"color": "rgba(255,255,255,0.72)",
|
|
||||||
"marginTop": -1,
|
|
||||||
"paddingLeft": 12,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="channel_list_item.hello.display_name"
|
|
||||||
>
|
|
||||||
Hello!
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -37,10 +37,8 @@ describe('components/channel_list/categories/body/channel_item', () => {
|
|||||||
membersCount={0}
|
membersCount={0}
|
||||||
myChannel={myChannel}
|
myChannel={myChannel}
|
||||||
isMuted={false}
|
isMuted={false}
|
||||||
collapsed={false}
|
|
||||||
currentUserId={'id'}
|
currentUserId={'id'}
|
||||||
testID='channel_list_item'
|
testID='channel_list_item'
|
||||||
isVisible={true}
|
|
||||||
onPress={() => undefined}
|
onPress={() => undefined}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
@@ -57,10 +55,8 @@ describe('components/channel_list/categories/body/channel_item', () => {
|
|||||||
membersCount={3}
|
membersCount={3}
|
||||||
myChannel={myChannel}
|
myChannel={myChannel}
|
||||||
isMuted={false}
|
isMuted={false}
|
||||||
collapsed={false}
|
|
||||||
currentUserId={'id'}
|
currentUserId={'id'}
|
||||||
testID='channel_list_item'
|
testID='channel_list_item'
|
||||||
isVisible={true}
|
|
||||||
onPress={() => undefined}
|
onPress={() => undefined}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React, {useCallback, useEffect, useMemo} from 'react';
|
import React, {useCallback, useMemo} from 'react';
|
||||||
import {useIntl} from 'react-intl';
|
import {useIntl} from 'react-intl';
|
||||||
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
|
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
|
||||||
import Animated, {Easing, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
|
|
||||||
|
|
||||||
import Badge from '@components/badge';
|
import Badge from '@components/badge';
|
||||||
import ChannelIcon from '@components/channel_icon';
|
import ChannelIcon from '@components/channel_icon';
|
||||||
@@ -22,13 +21,11 @@ import type MyChannelModel from '@typings/database/models/servers/my_channel';
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
channel: ChannelModel;
|
channel: ChannelModel;
|
||||||
collapsed: boolean;
|
|
||||||
currentUserId: string;
|
currentUserId: string;
|
||||||
hasDraft: boolean;
|
hasDraft: boolean;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
isInfo?: boolean;
|
isInfo?: boolean;
|
||||||
isMuted: boolean;
|
isMuted: boolean;
|
||||||
isVisible: boolean;
|
|
||||||
membersCount: number;
|
membersCount: number;
|
||||||
myChannel?: MyChannelModel;
|
myChannel?: MyChannelModel;
|
||||||
onPress: (channelId: string) => void;
|
onPress: (channelId: string) => void;
|
||||||
@@ -115,8 +112,8 @@ export const textStyle = StyleSheet.create({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const ChannelListItem = ({
|
const ChannelListItem = ({
|
||||||
channel, collapsed, currentUserId, hasDraft,
|
channel, currentUserId, hasDraft,
|
||||||
isActive, isInfo, isMuted, isVisible, membersCount,
|
isActive, isInfo, isMuted, membersCount,
|
||||||
myChannel, onPress, teamDisplayName, testID}: Props) => {
|
myChannel, onPress, teamDisplayName, testID}: Props) => {
|
||||||
const {formatMessage} = useIntl();
|
const {formatMessage} = useIntl();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@@ -126,8 +123,6 @@ const ChannelListItem = ({
|
|||||||
// Make it brighter if it's not muted, and highlighted or has unreads
|
// Make it brighter if it's not muted, and highlighted or has unreads
|
||||||
const isBright = !isMuted && (myChannel && (myChannel.isUnread || myChannel.mentionsCount > 0));
|
const isBright = !isMuted && (myChannel && (myChannel.isUnread || myChannel.mentionsCount > 0));
|
||||||
|
|
||||||
const shouldCollapse = (collapsed && !isBright) && !isActive;
|
|
||||||
const sharedValue = useSharedValue(shouldCollapse);
|
|
||||||
const height = useMemo(() => {
|
const height = useMemo(() => {
|
||||||
let h = 40;
|
let h = 40;
|
||||||
if (isInfo) {
|
if (isInfo) {
|
||||||
@@ -136,18 +131,6 @@ const ChannelListItem = ({
|
|||||||
return h;
|
return h;
|
||||||
}, [teamDisplayName, isInfo, isTablet]);
|
}, [teamDisplayName, isInfo, isTablet]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
sharedValue.value = shouldCollapse;
|
|
||||||
}, [shouldCollapse]);
|
|
||||||
|
|
||||||
const animatedStyle = useAnimatedStyle(() => {
|
|
||||||
return {
|
|
||||||
marginVertical: withTiming(sharedValue.value ? 0 : 2, {duration: 500}),
|
|
||||||
height: withTiming(sharedValue.value ? 0 : height, {duration: 500}),
|
|
||||||
opacity: withTiming(sharedValue.value ? 0 : 1, {duration: 500, easing: Easing.inOut(Easing.exp)}),
|
|
||||||
};
|
|
||||||
}, [height]);
|
|
||||||
|
|
||||||
const handleOnPress = useCallback(() => {
|
const handleOnPress = useCallback(() => {
|
||||||
onPress(myChannel?.id || channel.id);
|
onPress(myChannel?.id || channel.id);
|
||||||
}, [channel.id, myChannel?.id]);
|
}, [channel.id, myChannel?.id]);
|
||||||
@@ -169,7 +152,7 @@ const ChannelListItem = ({
|
|||||||
],
|
],
|
||||||
[height, isActive, isInfo, styles]);
|
[height, isActive, isInfo, styles]);
|
||||||
|
|
||||||
if ((!isInfo && (channel.deleteAt > 0 && !isActive)) || !myChannel || !isVisible) {
|
if (!myChannel) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,37 +165,36 @@ const ChannelListItem = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Animated.View style={animatedStyle}>
|
<TouchableOpacity onPress={handleOnPress}>
|
||||||
<TouchableOpacity onPress={handleOnPress}>
|
<>
|
||||||
<>
|
<View
|
||||||
<View
|
style={containerStyle}
|
||||||
style={containerStyle}
|
testID={`${testID}.${channel.name}.collapsed.${!isActive}`}
|
||||||
testID={`${testID}.${channel.name}.collapsed.${collapsed && !isActive}`}
|
>
|
||||||
>
|
<View style={styles.wrapper}>
|
||||||
<View style={styles.wrapper}>
|
<ChannelIcon
|
||||||
<ChannelIcon
|
hasDraft={hasDraft}
|
||||||
hasDraft={hasDraft}
|
isActive={isInfo ? false : isActive}
|
||||||
isActive={isInfo ? false : isActive}
|
isInfo={isInfo}
|
||||||
isInfo={isInfo}
|
isUnread={isBright}
|
||||||
isUnread={isBright}
|
isArchived={channel.deleteAt > 0}
|
||||||
isArchived={channel.deleteAt > 0}
|
membersCount={membersCount}
|
||||||
membersCount={membersCount}
|
name={channel.name}
|
||||||
name={channel.name}
|
shared={channel.shared}
|
||||||
shared={channel.shared}
|
size={24}
|
||||||
size={24}
|
type={channel.type}
|
||||||
type={channel.type}
|
isMuted={isMuted}
|
||||||
isMuted={isMuted}
|
/>
|
||||||
/>
|
<View>
|
||||||
<View>
|
<Text
|
||||||
<Text
|
ellipsizeMode='tail'
|
||||||
ellipsizeMode='tail'
|
numberOfLines={1}
|
||||||
numberOfLines={1}
|
style={textStyles}
|
||||||
style={textStyles}
|
testID={`${testID}.${channel.name}.display_name`}
|
||||||
testID={`${testID}.${channel.name}.display_name`}
|
>
|
||||||
>
|
{displayName}
|
||||||
{displayName}
|
</Text>
|
||||||
</Text>
|
{isInfo && Boolean(teamDisplayName) && !isTablet &&
|
||||||
{isInfo && Boolean(teamDisplayName) && !isTablet &&
|
|
||||||
<Text
|
<Text
|
||||||
ellipsizeMode='tail'
|
ellipsizeMode='tail'
|
||||||
numberOfLines={1}
|
numberOfLines={1}
|
||||||
@@ -221,34 +203,33 @@ const ChannelListItem = ({
|
|||||||
>
|
>
|
||||||
{teamDisplayName}
|
{teamDisplayName}
|
||||||
</Text>
|
</Text>
|
||||||
}
|
|
||||||
</View>
|
|
||||||
{Boolean(teammateId) &&
|
|
||||||
<CustomStatus
|
|
||||||
isInfo={isInfo}
|
|
||||||
userId={teammateId!}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
{isInfo && Boolean(teamDisplayName) && isTablet &&
|
|
||||||
<Text
|
|
||||||
ellipsizeMode='tail'
|
|
||||||
numberOfLines={1}
|
|
||||||
testID={`${testID}.${teamDisplayName}.display_name`}
|
|
||||||
style={[styles.teamName, styles.teamNameTablet]}
|
|
||||||
>
|
|
||||||
{teamDisplayName}
|
|
||||||
</Text>
|
|
||||||
}
|
}
|
||||||
</View>
|
</View>
|
||||||
<Badge
|
{Boolean(teammateId) &&
|
||||||
visible={myChannel.mentionsCount > 0}
|
<CustomStatus
|
||||||
value={myChannel.mentionsCount}
|
isInfo={isInfo}
|
||||||
style={[styles.badge, isMuted && styles.mutedBadge, isInfo && styles.infoBadge]}
|
userId={teammateId!}
|
||||||
/>
|
/>
|
||||||
|
}
|
||||||
|
{isInfo && Boolean(teamDisplayName) && isTablet &&
|
||||||
|
<Text
|
||||||
|
ellipsizeMode='tail'
|
||||||
|
numberOfLines={1}
|
||||||
|
testID={`${testID}.${teamDisplayName}.display_name`}
|
||||||
|
style={[styles.teamName, styles.teamNameTablet]}
|
||||||
|
>
|
||||||
|
{teamDisplayName}
|
||||||
|
</Text>
|
||||||
|
}
|
||||||
</View>
|
</View>
|
||||||
</>
|
<Badge
|
||||||
</TouchableOpacity>
|
visible={myChannel.mentionsCount > 0}
|
||||||
</Animated.View>
|
value={myChannel.mentionsCount}
|
||||||
|
style={[styles.badge, isMuted && styles.mutedBadge, isInfo && styles.infoBadge]}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</>
|
||||||
|
</TouchableOpacity>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,12 @@
|
|||||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||||
import withObservables from '@nozbe/with-observables';
|
import withObservables from '@nozbe/with-observables';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {of as of$, combineLatest} from 'rxjs';
|
import {of as of$} from 'rxjs';
|
||||||
import {switchMap, distinctUntilChanged} from 'rxjs/operators';
|
import {switchMap, distinctUntilChanged} from 'rxjs/operators';
|
||||||
|
|
||||||
import {General, Preferences} from '@constants';
|
import {General} from '@constants';
|
||||||
import {getPreferenceAsBool} from '@helpers/api/preference';
|
|
||||||
import {observeMyChannel} from '@queries/servers/channel';
|
import {observeMyChannel} from '@queries/servers/channel';
|
||||||
import {queryDraft} from '@queries/servers/drafts';
|
import {queryDraft} from '@queries/servers/drafts';
|
||||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
|
||||||
import {observeCurrentChannelId, observeCurrentUserId} from '@queries/servers/system';
|
import {observeCurrentChannelId, observeCurrentUserId} from '@queries/servers/system';
|
||||||
import ChannelModel from '@typings/database/models/servers/channel';
|
import ChannelModel from '@typings/database/models/servers/channel';
|
||||||
import MyChannelModel from '@typings/database/models/servers/my_channel';
|
import MyChannelModel from '@typings/database/models/servers/my_channel';
|
||||||
@@ -19,18 +17,15 @@ import MyChannelModel from '@typings/database/models/servers/my_channel';
|
|||||||
import ChannelItem from './channel_item';
|
import ChannelItem from './channel_item';
|
||||||
|
|
||||||
import type {WithDatabaseArgs} from '@typings/database/database';
|
import type {WithDatabaseArgs} from '@typings/database/database';
|
||||||
import type PreferenceModel from '@typings/database/models/servers/preference';
|
|
||||||
|
|
||||||
type EnhanceProps = WithDatabaseArgs & {
|
type EnhanceProps = WithDatabaseArgs & {
|
||||||
channel: ChannelModel;
|
channel: ChannelModel;
|
||||||
isInfo?: boolean;
|
|
||||||
isUnreads?: boolean;
|
|
||||||
showTeamName?: boolean;
|
showTeamName?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const observeIsMutedSetting = (mc: MyChannelModel) => mc.settings.observe().pipe(switchMap((s) => of$(s?.notifyProps?.mark_unread === 'mention')));
|
const observeIsMutedSetting = (mc: MyChannelModel) => mc.settings.observe().pipe(switchMap((s) => of$(s?.notifyProps?.mark_unread === 'mention')));
|
||||||
|
|
||||||
const enhance = withObservables(['channel', 'isUnreads', 'showTeamName'], ({channel, database, isInfo, isUnreads, showTeamName}: EnhanceProps) => {
|
const enhance = withObservables(['channel', 'showTeamName'], ({channel, database, showTeamName}: EnhanceProps) => {
|
||||||
const currentUserId = observeCurrentUserId(database);
|
const currentUserId = observeCurrentUserId(database);
|
||||||
const myChannel = observeMyChannel(database, channel.id);
|
const myChannel = observeMyChannel(database, channel.id);
|
||||||
|
|
||||||
@@ -39,29 +34,6 @@ const enhance = withObservables(['channel', 'isUnreads', 'showTeamName'], ({chan
|
|||||||
);
|
);
|
||||||
|
|
||||||
const isActive = observeCurrentChannelId(database).pipe(switchMap((id) => of$(id ? id === channel.id : false)), distinctUntilChanged());
|
const isActive = observeCurrentChannelId(database).pipe(switchMap((id) => of$(id ? id === channel.id : false)), distinctUntilChanged());
|
||||||
const unreadsOnTop = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS).
|
|
||||||
observeWithColumns(['value']).
|
|
||||||
pipe(
|
|
||||||
switchMap((prefs: PreferenceModel[]) => of$(getPreferenceAsBool(prefs, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS, false))),
|
|
||||||
);
|
|
||||||
|
|
||||||
const isVisible = combineLatest([myChannel, unreadsOnTop]).pipe(
|
|
||||||
switchMap(([mc, u]) => {
|
|
||||||
if (!mc) {
|
|
||||||
return of$(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isInfo) {
|
|
||||||
return of$(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isUnreads) {
|
|
||||||
return of$(u);
|
|
||||||
}
|
|
||||||
|
|
||||||
return u ? of$(!mc.isUnread || !mc.mentionsCount) : of$(true);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
const isMuted = myChannel.pipe(
|
const isMuted = myChannel.pipe(
|
||||||
switchMap((mc) => {
|
switchMap((mc) => {
|
||||||
@@ -90,7 +62,6 @@ const enhance = withObservables(['channel', 'isUnreads', 'showTeamName'], ({chan
|
|||||||
hasDraft,
|
hasDraft,
|
||||||
isActive,
|
isActive,
|
||||||
isMuted,
|
isMuted,
|
||||||
isVisible,
|
|
||||||
membersCount,
|
membersCount,
|
||||||
myChannel,
|
myChannel,
|
||||||
teamDisplayName,
|
teamDisplayName,
|
||||||
|
|||||||
@@ -170,7 +170,6 @@ const FilteredList = ({
|
|||||||
}, {displayName}),
|
}, {displayName}),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await close();
|
await close();
|
||||||
@@ -209,7 +208,6 @@ const FilteredList = ({
|
|||||||
return (
|
return (
|
||||||
<ChannelItem
|
<ChannelItem
|
||||||
channel={item}
|
channel={item}
|
||||||
collapsed={false}
|
|
||||||
isInfo={true}
|
isInfo={true}
|
||||||
onPress={onSwitchToChannel}
|
onPress={onSwitchToChannel}
|
||||||
showTeamName={showTeamName}
|
showTeamName={showTeamName}
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ const UnfilteredList = ({close, keyboardHeight, recentChannels, showTeamName, un
|
|||||||
return (
|
return (
|
||||||
<ChannelItem
|
<ChannelItem
|
||||||
channel={item}
|
channel={item}
|
||||||
collapsed={false}
|
|
||||||
isInfo={true}
|
isInfo={true}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
showTeamName={showTeamName}
|
showTeamName={showTeamName}
|
||||||
|
|||||||
@@ -1,167 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`components/channel_list/categories/body should match snapshot 1`] = `
|
|
||||||
Object {
|
|
||||||
"children": Array [
|
|
||||||
<View>
|
|
||||||
<View
|
|
||||||
onLayout={[Function]}
|
|
||||||
style={null}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
animatedStyle={
|
|
||||||
Object {
|
|
||||||
"value": Object {
|
|
||||||
"height": 40,
|
|
||||||
"marginVertical": 2,
|
|
||||||
"opacity": 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
collapsable={false}
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"height": 40,
|
|
||||||
"marginVertical": 2,
|
|
||||||
"opacity": 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
accessible={true}
|
|
||||||
collapsable={false}
|
|
||||||
focusable={true}
|
|
||||||
onClick={[Function]}
|
|
||||||
onResponderGrant={[Function]}
|
|
||||||
onResponderMove={[Function]}
|
|
||||||
onResponderRelease={[Function]}
|
|
||||||
onResponderTerminate={[Function]}
|
|
||||||
onResponderTerminationRequest={[Function]}
|
|
||||||
onStartShouldSetResponder={[Function]}
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"opacity": 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"alignItems": "center",
|
|
||||||
"flexDirection": "row",
|
|
||||||
"minHeight": 40,
|
|
||||||
"paddingHorizontal": 20,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
undefined,
|
|
||||||
Object {
|
|
||||||
"minHeight": 40,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="category.test_category.channel_list_item.channel.collapsed.false"
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"flex": 1,
|
|
||||||
"flexDirection": "row",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"alignItems": "center",
|
|
||||||
"justifyContent": "center",
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"height": 24,
|
|
||||||
"width": 24,
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
name="globe"
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"color": "rgba(255,255,255,0.4)",
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
Object {
|
|
||||||
"fontSize": 24,
|
|
||||||
"left": 1,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="undefined.public"
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<View>
|
|
||||||
<Text
|
|
||||||
ellipsizeMode="tail"
|
|
||||||
numberOfLines={1}
|
|
||||||
style={
|
|
||||||
Array [
|
|
||||||
Object {
|
|
||||||
"fontFamily": "OpenSans",
|
|
||||||
"fontSize": 16,
|
|
||||||
"fontWeight": "400",
|
|
||||||
"lineHeight": 24,
|
|
||||||
},
|
|
||||||
Object {
|
|
||||||
"color": "rgba(255,255,255,0.72)",
|
|
||||||
"marginTop": -1,
|
|
||||||
"paddingLeft": 12,
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
testID="category.test_category.channel_list_item.channel.display_name"
|
|
||||||
>
|
|
||||||
Channel
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</View>,
|
|
||||||
],
|
|
||||||
"props": Object {
|
|
||||||
"data": Anything,
|
|
||||||
"getItem": [Function],
|
|
||||||
"getItemCount": [Function],
|
|
||||||
"initialNumToRender": 20,
|
|
||||||
"invertStickyHeaders": undefined,
|
|
||||||
"keyExtractor": [Function],
|
|
||||||
"onContentSizeChange": [Function],
|
|
||||||
"onLayout": [Function],
|
|
||||||
"onMomentumScrollBegin": [Function],
|
|
||||||
"onMomentumScrollEnd": [Function],
|
|
||||||
"onScroll": [Function],
|
|
||||||
"onScrollBeginDrag": [Function],
|
|
||||||
"onScrollEndDrag": [Function],
|
|
||||||
"removeClippedSubviews": true,
|
|
||||||
"renderItem": [Function],
|
|
||||||
"scrollEventThrottle": 50,
|
|
||||||
"stickyHeaderIndices": Array [],
|
|
||||||
"style": undefined,
|
|
||||||
"updateCellsBatchingPeriod": 10,
|
|
||||||
"viewabilityConfigCallbackPairs": Array [],
|
|
||||||
"windowSize": 15,
|
|
||||||
},
|
|
||||||
"type": "RCTScrollView",
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
@@ -35,15 +35,14 @@ describe('components/channel_list/categories/body', () => {
|
|||||||
<CategoryBody
|
<CategoryBody
|
||||||
category={category}
|
category={category}
|
||||||
locale={DEFAULT_LOCALE}
|
locale={DEFAULT_LOCALE}
|
||||||
|
isTablet={false}
|
||||||
onChannelSwitch={() => undefined}
|
onChannelSwitch={() => undefined}
|
||||||
/>,
|
/>,
|
||||||
{database},
|
{database},
|
||||||
);
|
);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
expect(wrapper.toJSON()).toMatchSnapshot({
|
expect(wrapper.toJSON()).toBeTruthy();
|
||||||
props: {data: expect.anything()},
|
|
||||||
});
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
import React, {useCallback, useMemo} from 'react';
|
import React, {useCallback, useEffect, useMemo} from 'react';
|
||||||
import {FlatList} from 'react-native';
|
import {FlatList} from 'react-native';
|
||||||
|
import Animated, {Easing, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
|
||||||
|
|
||||||
import ChannelItem from '@components/channel_item';
|
import ChannelItem from '@components/channel_item';
|
||||||
import {DMS_CATEGORY} from '@constants/categories';
|
import {DMS_CATEGORY} from '@constants/categories';
|
||||||
@@ -39,26 +40,40 @@ const CategoryBody = ({sortedChannels, category, hiddenChannelIds, limit, onChan
|
|||||||
return (
|
return (
|
||||||
<ChannelItem
|
<ChannelItem
|
||||||
channel={item}
|
channel={item}
|
||||||
collapsed={category.collapsed}
|
|
||||||
testID={`category.${category.displayName.replace(/ /g, '_').toLocaleLowerCase()}.channel_list_item`}
|
testID={`category.${category.displayName.replace(/ /g, '_').toLocaleLowerCase()}.channel_list_item`}
|
||||||
onPress={onChannelSwitch}
|
onPress={onChannelSwitch}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, [category.collapsed, onChannelSwitch]);
|
}, [onChannelSwitch]);
|
||||||
|
|
||||||
|
const sharedValue = useSharedValue(category.collapsed);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
sharedValue.value = category.collapsed;
|
||||||
|
}, [category.collapsed]);
|
||||||
|
|
||||||
|
const height = ids.length ? ids.length * 40 : 0;
|
||||||
|
|
||||||
|
const animatedStyle = useAnimatedStyle(() => {
|
||||||
|
return {
|
||||||
|
height: withTiming(sharedValue.value ? 1 : height, {duration: 300}),
|
||||||
|
opacity: withTiming(sharedValue.value ? 0 : 1, {duration: sharedValue.value ? 200 : 300, easing: Easing.inOut(Easing.exp)}),
|
||||||
|
};
|
||||||
|
}, [height]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FlatList
|
<Animated.View
|
||||||
data={ids}
|
style={animatedStyle}
|
||||||
renderItem={renderItem}
|
>
|
||||||
keyExtractor={extractKey}
|
<FlatList
|
||||||
removeClippedSubviews={true}
|
data={ids}
|
||||||
initialNumToRender={20}
|
renderItem={renderItem}
|
||||||
windowSize={15}
|
keyExtractor={extractKey}
|
||||||
updateCellsBatchingPeriod={10}
|
|
||||||
|
|
||||||
// @ts-expect-error strictMode not exposed on the types
|
// @ts-expect-error strictMode not exposed on the types
|
||||||
strictMode={true}
|
strictMode={true}
|
||||||
/>
|
/>
|
||||||
|
</Animated.View>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,14 @@ import {Database} from '@nozbe/watermelondb';
|
|||||||
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
import {withDatabase} from '@nozbe/watermelondb/DatabaseProvider';
|
||||||
import withObservables from '@nozbe/with-observables';
|
import withObservables from '@nozbe/with-observables';
|
||||||
import {combineLatest, of as of$} from 'rxjs';
|
import {combineLatest, of as of$} from 'rxjs';
|
||||||
import {map, switchMap, concatAll} from 'rxjs/operators';
|
import {map, switchMap, concatAll, combineLatestWith} from 'rxjs/operators';
|
||||||
|
|
||||||
import {General, Preferences} from '@constants';
|
import {General, Preferences} from '@constants';
|
||||||
import {DMS_CATEGORY} from '@constants/categories';
|
import {DMS_CATEGORY} from '@constants/categories';
|
||||||
import {queryChannelsByNames, queryMyChannelSettingsByIds} from '@queries/servers/channel';
|
import {getPreferenceAsBool} from '@helpers/api/preference';
|
||||||
|
import {observeAllMyChannelNotifyProps, queryChannelsByNames, queryMyChannelSettingsByIds} from '@queries/servers/channel';
|
||||||
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
|
||||||
import {observeCurrentUserId} from '@queries/servers/system';
|
import {observeCurrentChannelId, observeCurrentUserId, observeLastUnreadChannelId} from '@queries/servers/system';
|
||||||
import {WithDatabaseArgs} from '@typings/database/database';
|
import {WithDatabaseArgs} from '@typings/database/database';
|
||||||
import {getDirectChannelName} from '@utils/channel';
|
import {getDirectChannelName} from '@utils/channel';
|
||||||
|
|
||||||
@@ -104,11 +105,12 @@ type EnhanceProps = {
|
|||||||
category: CategoryModel;
|
category: CategoryModel;
|
||||||
locale: string;
|
locale: string;
|
||||||
currentUserId: string;
|
currentUserId: string;
|
||||||
|
isTablet: boolean;
|
||||||
} & WithDatabaseArgs
|
} & WithDatabaseArgs
|
||||||
|
|
||||||
const withUserId = withObservables([], ({database}: WithDatabaseArgs) => ({currentUserId: observeCurrentUserId(database)}));
|
const withUserId = withObservables([], ({database}: WithDatabaseArgs) => ({currentUserId: observeCurrentUserId(database)}));
|
||||||
|
|
||||||
const enhance = withObservables(['category', 'locale'], ({category, locale, database, currentUserId}: EnhanceProps) => {
|
const enhance = withObservables(['category', 'isTablet', 'locale'], ({category, locale, isTablet, database, currentUserId}: EnhanceProps) => {
|
||||||
const observedCategory = category.observe();
|
const observedCategory = category.observe();
|
||||||
const sortedChannels = observedCategory.pipe(
|
const sortedChannels = observedCategory.pipe(
|
||||||
switchMap((c) => getSortedChannels(database, c, locale)),
|
switchMap((c) => getSortedChannels(database, c, locale)),
|
||||||
@@ -145,10 +147,42 @@ const enhance = withObservables(['category', 'locale'], ({category, locale, data
|
|||||||
([a, b]) => of$(new Set(a.concat(b))),
|
([a, b]) => of$(new Set(a.concat(b))),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
const unreadsOnTop = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS).
|
||||||
|
observeWithColumns(['value']).
|
||||||
|
pipe(
|
||||||
|
switchMap((prefs: PreferenceModel[]) => of$(getPreferenceAsBool(prefs, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS, false))),
|
||||||
|
);
|
||||||
|
|
||||||
|
const notifyProps = observeAllMyChannelNotifyProps(database);
|
||||||
|
const lastUnreadId = isTablet ? observeLastUnreadChannelId(database) : of$(undefined);
|
||||||
|
const unreadChannelIds = category.myChannels.observeWithColumns(['mentions_count', 'is_unread']).pipe(
|
||||||
|
combineLatestWith(unreadsOnTop, notifyProps, lastUnreadId),
|
||||||
|
map(([my, unreadTop, settings, lastUnread]) => {
|
||||||
|
if (!unreadTop) {
|
||||||
|
return new Set();
|
||||||
|
}
|
||||||
|
return my.reduce<Set<string>>((set, m) => {
|
||||||
|
const isMuted = settings[m.id]?.mark_unread === 'mention';
|
||||||
|
if ((isMuted && m.mentionsCount) || (!isMuted && m.isUnread) || m.id === lastUnread) {
|
||||||
|
set.add(m.id);
|
||||||
|
}
|
||||||
|
return set;
|
||||||
|
}, new Set());
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentChannelId = observeCurrentChannelId(database);
|
||||||
|
const filtered = sortedChannels.pipe(
|
||||||
|
combineLatestWith(currentChannelId, unreadChannelIds),
|
||||||
|
map(([channels, ccId, unreadIds]) => {
|
||||||
|
return channels.filter((c) => c && ((c.deleteAt > 0 && c.id === ccId) || !c.deleteAt) && !unreadIds.has(c.id));
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
limit,
|
limit,
|
||||||
hiddenChannelIds,
|
hiddenChannelIds,
|
||||||
sortedChannels,
|
sortedChannels: filtered,
|
||||||
category: observedCategory,
|
category: observedCategory,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {FlatList, StyleSheet} from 'react-native';
|
|||||||
|
|
||||||
import {switchToChannelById} from '@actions/remote/channel';
|
import {switchToChannelById} from '@actions/remote/channel';
|
||||||
import {useServerUrl} from '@context/server';
|
import {useServerUrl} from '@context/server';
|
||||||
|
import {useIsTablet} from '@hooks/device';
|
||||||
|
|
||||||
import CategoryBody from './body';
|
import CategoryBody from './body';
|
||||||
import LoadCategoriesError from './error';
|
import LoadCategoriesError from './error';
|
||||||
@@ -35,6 +36,7 @@ const Categories = ({categories, currentTeamId, unreadsOnTop}: Props) => {
|
|||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const listRef = useRef<FlatList>(null);
|
const listRef = useRef<FlatList>(null);
|
||||||
const serverUrl = useServerUrl();
|
const serverUrl = useServerUrl();
|
||||||
|
const isTablet = useIsTablet();
|
||||||
|
|
||||||
const onChannelSwitch = useCallback(async (channelId: string) => {
|
const onChannelSwitch = useCallback(async (channelId: string) => {
|
||||||
switchToChannelById(serverUrl, channelId);
|
switchToChannelById(serverUrl, channelId);
|
||||||
@@ -45,6 +47,7 @@ const Categories = ({categories, currentTeamId, unreadsOnTop}: Props) => {
|
|||||||
return (
|
return (
|
||||||
<UnreadCategories
|
<UnreadCategories
|
||||||
currentTeamId={currentTeamId}
|
currentTeamId={currentTeamId}
|
||||||
|
isTablet={isTablet}
|
||||||
onChannelSwitch={onChannelSwitch}
|
onChannelSwitch={onChannelSwitch}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -54,12 +57,13 @@ const Categories = ({categories, currentTeamId, unreadsOnTop}: Props) => {
|
|||||||
<CategoryHeader category={data.item}/>
|
<CategoryHeader category={data.item}/>
|
||||||
<CategoryBody
|
<CategoryBody
|
||||||
category={data.item}
|
category={data.item}
|
||||||
|
isTablet={isTablet}
|
||||||
locale={intl.locale}
|
locale={intl.locale}
|
||||||
onChannelSwitch={onChannelSwitch}
|
onChannelSwitch={onChannelSwitch}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}, [currentTeamId, intl.locale, onChannelSwitch]);
|
}, [currentTeamId, intl.locale, isTablet, onChannelSwitch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
listRef.current?.scrollToOffset({animated: false, offset: 0});
|
listRef.current?.scrollToOffset({animated: false, offset: 0});
|
||||||
@@ -87,11 +91,7 @@ const Categories = ({categories, currentTeamId, unreadsOnTop}: Props) => {
|
|||||||
showsHorizontalScrollIndicator={false}
|
showsHorizontalScrollIndicator={false}
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
keyExtractor={extractKey}
|
keyExtractor={extractKey}
|
||||||
removeClippedSubviews={true}
|
initialNumToRender={categoriesToShow.length}
|
||||||
initialNumToRender={5}
|
|
||||||
windowSize={15}
|
|
||||||
updateCellsBatchingPeriod={10}
|
|
||||||
maxToRenderPerBatch={5}
|
|
||||||
|
|
||||||
// @ts-expect-error strictMode not included in the types
|
// @ts-expect-error strictMode not included in the types
|
||||||
strictMode={true}
|
strictMode={true}
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ import type {WithDatabaseArgs} from '@typings/database/database';
|
|||||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||||
import type PreferenceModel from '@typings/database/models/servers/preference';
|
import type PreferenceModel from '@typings/database/models/servers/preference';
|
||||||
|
|
||||||
type WithDatabaseProps = { currentTeamId: string } & WithDatabaseArgs
|
type WithDatabaseProps = WithDatabaseArgs & {
|
||||||
|
currentTeamId: string;
|
||||||
|
isTablet: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
type CA = [
|
type CA = [
|
||||||
a: Array<ChannelModel | null>,
|
a: Array<ChannelModel | null>,
|
||||||
@@ -42,7 +45,7 @@ const filterMutedFromMyChannels = ([myChannels, notifyProps]: [MyChannelModel[],
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const enhanced = withObservables(['currentTeamId'], ({currentTeamId, database}: WithDatabaseProps) => {
|
const enhanced = withObservables(['currentTeamId', 'isTablet'], ({currentTeamId, isTablet, database}: WithDatabaseProps) => {
|
||||||
const unreadsOnTop = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS).
|
const unreadsOnTop = queryPreferencesByCategoryAndName(database, Preferences.CATEGORY_SIDEBAR_SETTINGS, Preferences.CHANNEL_SIDEBAR_GROUP_UNREADS).
|
||||||
observeWithColumns(['value']).
|
observeWithColumns(['value']).
|
||||||
pipe(
|
pipe(
|
||||||
@@ -53,9 +56,9 @@ const enhanced = withObservables(['currentTeamId'], ({currentTeamId, database}:
|
|||||||
|
|
||||||
const unreadChannels = unreadsOnTop.pipe(switchMap((gU) => {
|
const unreadChannels = unreadsOnTop.pipe(switchMap((gU) => {
|
||||||
if (gU) {
|
if (gU) {
|
||||||
const lastUnread = observeLastUnreadChannelId(database).pipe(
|
const lastUnread = isTablet ? observeLastUnreadChannelId(database).pipe(
|
||||||
switchMap(getC),
|
switchMap(getC),
|
||||||
);
|
) : of$('');
|
||||||
const notifyProps = observeAllMyChannelNotifyProps(database);
|
const notifyProps = observeAllMyChannelNotifyProps(database);
|
||||||
|
|
||||||
const unreads = queryMyChannelUnreads(database, currentTeamId).observe().pipe(
|
const unreads = queryMyChannelUnreads(database, currentTeamId).observe().pipe(
|
||||||
|
|||||||
@@ -36,8 +36,6 @@ const UnreadCategories = ({onChannelSwitch, unreadChannels}: UnreadCategoriesPro
|
|||||||
return (
|
return (
|
||||||
<ChannelItem
|
<ChannelItem
|
||||||
channel={item}
|
channel={item}
|
||||||
collapsed={false}
|
|
||||||
isUnreads={true}
|
|
||||||
onPress={onChannelSwitch}
|
onPress={onChannelSwitch}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user