Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 5 additions & 1 deletion examples/SampleApp/src/screens/NewDirectMessagingScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,11 @@ export const NewDirectMessagingScreen: React.FC<NewDirectMessagingScreenProps> =
)}
</View>
<View style={styles.searchContainerRight}>
{selectedUsers.length === 0 ? <User pathFill={grey} /> : <UserAdd pathFill={grey} />}
{selectedUsers.length === 0 ? (
<User pathFill={grey} />
) : (
<UserAdd pathFill={grey} height={20} width={20} />
)}
</View>
</TouchableOpacity>
{focusOnSearchInput && !searchText && selectedUsers.length === 0 && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const ImageOverlaySelectedComponent = ({ index }: { index: number }) => {
check,
]}
>
{index !== -1 ? <BadgeNotification count={index + 1} size='lg' type='primary' /> : null}
{index !== -1 ? <BadgeNotification count={index + 1} size='sm' type='primary' /> : null}
</View>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const SuggestionCommandIcon = ({ name }: { name: CommandVariants }) => {
} else if (name === 'imgur') {
return <Imgur height={20} width={20} />;
} else if (name === 'mute') {
return <Mute height={20} pathFill={semantics.textSecondary} width={20} />;
return <Mute height={20} fill={semantics.textSecondary} width={20} />;
} else if (name === 'unban') {
return <UserAdd height={20} stroke={semantics.textSecondary} width={20} />;
} else if (name === 'unmute') {
Expand Down
9 changes: 6 additions & 3 deletions package/src/components/ChannelList/ChannelList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
} from '../../contexts/channelsContext/ChannelsContext';
import { useChatContext } from '../../contexts/chatContext/ChatContext';
import type { ChannelListEventListenerOptions } from '../../types/types';
import { ChannelPreviewMessenger } from '../ChannelPreview/ChannelPreviewMessenger';
import { ChannelPreview } from '../ChannelPreview/ChannelPreview';
import { EmptyStateIndicator as EmptyStateIndicatorDefault } from '../Indicators/EmptyStateIndicator';
import { LoadingErrorIndicator as LoadingErrorIndicatorDefault } from '../Indicators/LoadingErrorIndicator';

Expand Down Expand Up @@ -55,6 +55,7 @@ export type ChannelListProps = Partial<
| 'Skeleton'
| 'maxUnreadCount'
| 'numberOfSkeletons'
| 'mutedStatusPosition'
>
> & {
/** Optional function to filter channels prior to rendering the list. Do not use any complex logic that would delay the loading of the ChannelList. We recommend using a pure function with array methods like filter/sort/reduce. */
Expand Down Expand Up @@ -261,7 +262,7 @@ export const ChannelList = (props: ChannelListProps) => {
loadMoreThreshold = 0.1,
lockChannelOrder = false,
maxUnreadCount = 255,
numberOfSkeletons = 6,
numberOfSkeletons = 8,
onAddedToChannel,
onChannelDeleted,
onChannelHidden,
Expand All @@ -274,7 +275,7 @@ export const ChannelList = (props: ChannelListProps) => {
onRemovedFromChannel,
onSelect,
options = DEFAULT_OPTIONS,
Preview = ChannelPreviewMessenger,
Preview = ChannelPreview,
PreviewAvatar,
PreviewMessage,
PreviewMutedStatus,
Expand All @@ -285,6 +286,7 @@ export const ChannelList = (props: ChannelListProps) => {
Skeleton = SkeletonDefault,
sort = DEFAULT_SORT,
queryChannelsOverride,
mutedStatusPosition = 'inlineTitle',
} = props;

const [forceUpdate, setForceUpdate] = useState(0);
Expand Down Expand Up @@ -416,6 +418,7 @@ export const ChannelList = (props: ChannelListProps) => {
}
},
Skeleton,
mutedStatusPosition,
});

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,12 @@ export const ChannelListLoadingIndicator = () => {
const {
theme: {
channelListLoadingIndicator: { container },
colors: { white_snow },
},
} = useTheme();
const { numberOfSkeletons, Skeleton } = useChannelsContext();

return (
<View
style={[styles.container, { backgroundColor: white_snow }, container]}
testID='channel-list-loading-indicator'
>
<View style={[styles.container, container]} testID='channel-list-loading-indicator'>
{Array.from(Array(numberOfSkeletons)).map((_, index) => (
<Skeleton key={`skeleton_${index}`} />
))}
Expand Down
226 changes: 119 additions & 107 deletions package/src/components/ChannelList/Skeleton.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,24 @@
import React, { useEffect } from 'react';
import React, { useEffect, useMemo } from 'react';
import { StyleSheet, useWindowDimensions, View } from 'react-native';
import Animated, {
Easing,
useAnimatedProps,
useAnimatedStyle,
useDerivedValue,
useSharedValue,
withRepeat,
withTiming,
} from 'react-native-reanimated';
import { Defs, LinearGradient, Path, Rect, Stop, Svg } from 'react-native-svg';
import Svg, { Path, Rect, Defs, LinearGradient, Stop, ClipPath, G, Mask } from 'react-native-svg';

import { useTheme } from '../../contexts/themeContext/ThemeContext';

const paddingLarge = 16;
const paddingMedium = 12;
const paddingSmall = 8;

const AnimatedPath = Animated.createAnimatedComponent(Path);

const styles = StyleSheet.create({
background: {
height: 64,
position: 'absolute',
width: '100%',
},
container: {
borderBottomWidth: 1,
flexDirection: 'row',
},
});

export const Skeleton = () => {
const width = useWindowDimensions().width;
const startOffset = useSharedValue(-width);
const styles = useStyles();

const {
theme: {
channelListSkeleton: {
animationTime = 1800,
background,
container,
gradientStart,
gradientStop,
height = 64,
maskFillColor,
},
colors: { grey_gainsboro, white_snow },
channelListSkeleton: { animationTime = 1500, container, height = 80 },
semantics,
},
} = useTheme();
Expand All @@ -69,92 +41,132 @@ export const Skeleton = () => {
[],
);

const d = useDerivedValue(() => {
const useableHeight = height - paddingMedium * 2;
const boneHeight = (useableHeight - 8) / 2;
const boneRadius = boneHeight / 2;
const circleRadius = useableHeight / 2;
const avatarBoneWidth = circleRadius * 2 + paddingSmall * 2;
const detailsBonesWidth = width - avatarBoneWidth;

return `M0 0 h${width} v${height} h-${width}z M${paddingSmall} ${
height / 2
} a${circleRadius} ${circleRadius} 0 1 0 ${
circleRadius * 2
} 0 a${circleRadius} ${circleRadius} 0 1 0 -${circleRadius * 2} 0z M${
avatarBoneWidth + boneRadius
} ${paddingMedium} a${boneRadius} ${boneRadius} 0 1 0 0 ${boneHeight}z M${
avatarBoneWidth - boneRadius + detailsBonesWidth * 0.25
} ${paddingMedium} h-${detailsBonesWidth * 0.25 - boneRadius * 2} v${boneHeight} h${
detailsBonesWidth * 0.25 - boneRadius * 2
}z M${avatarBoneWidth - boneRadius + detailsBonesWidth * 0.25} ${
paddingMedium + boneHeight
} a${boneRadius} ${boneRadius} 0 1 0 0 -${boneHeight}z M${avatarBoneWidth + boneRadius} ${
paddingMedium + boneHeight + paddingSmall
} a${boneRadius} ${boneRadius} 0 1 0 0 ${boneHeight}z M${
avatarBoneWidth + detailsBonesWidth * 0.8 - boneRadius
} ${paddingMedium + boneHeight + paddingSmall} h-${
detailsBonesWidth * 0.8 - boneRadius * 2
} v${boneHeight} h${detailsBonesWidth * 0.8 - boneRadius * 2}z M${
avatarBoneWidth + detailsBonesWidth * 0.8 - boneRadius
} ${height - paddingMedium} a${boneRadius} ${boneRadius} 0 1 0 0 -${boneHeight}z M${
avatarBoneWidth + detailsBonesWidth * 0.8 + boneRadius + paddingLarge
} ${
paddingMedium + boneHeight + paddingSmall
} a${boneRadius} ${boneRadius} 0 1 0 0 ${boneHeight}z M${width - paddingSmall - boneRadius} ${
paddingMedium + boneHeight + paddingSmall
} h-${
width -
paddingSmall -
boneRadius -
(avatarBoneWidth + detailsBonesWidth * 0.8 + boneRadius + paddingLarge)
} v${boneHeight} h${
width -
paddingSmall -
boneRadius -
(avatarBoneWidth + detailsBonesWidth * 0.8 + boneRadius + paddingLarge)
}z M${width - paddingSmall * 2} ${
height - paddingMedium
} a${boneRadius} ${boneRadius} 0 1 0 0 -${boneHeight}z`;
}, []);

const svgAnimatedProps = useAnimatedProps(
() => ({
d: d.value,
}),
[d],
);

return (
<View
style={[styles.container, { borderBottomColor: semantics.borderCoreDefault }, container]}
testID='channel-preview-skeleton'
>
<View style={[styles.background, { backgroundColor: white_snow }, background]} />
<Animated.View style={[animatedStyle, styles.background]}>
<Svg height={height} width={width}>
<Rect fill='url(#gradient)' height={height} width={width} x={0} y={0} />
<View style={[styles.container, container]} testID='channel-preview-skeleton'>
<Animated.View style={[animatedStyle]}>
<Svg width={width} height={height} viewBox='0 0 402 80' fill='none'>
{/* Mask */}
<Defs>
<Mask id='path-1-inside-1_5596_231135'>
<Path d='M0 0H402V80H0V0Z' fill='white' />
</Mask>

{/* Gradients */}
<LinearGradient
id='paint0_linear_5596_231135'
x1='48'
y1='24'
x2='0'
y2='24'
gradientUnits='userSpaceOnUse'
>
<Stop stopColor='white' />
<Stop offset='1' stopColor='white' stopOpacity='0' />
</LinearGradient>

<LinearGradient
id='paint1_linear_5596_231135'
x1='242'
y1='8'
x2='0'
y2='8'
gradientUnits='userSpaceOnUse'
id='gradient'
x1={0}
x2={width}
y1={0}
y2={0}
>
<Stop offset={1} stopColor={grey_gainsboro} {...gradientStart} />
<Stop offset={0.5} stopColor={grey_gainsboro} {...gradientStop} />
<Stop offset={0} stopColor={grey_gainsboro} {...gradientStart} />
<Stop stopColor='white' />
<Stop offset='1' stopColor='white' stopOpacity='0' />
</LinearGradient>

<LinearGradient
id='paint2_linear_5596_231135'
x1='48'
y1='8'
x2='0'
y2='8'
gradientUnits='userSpaceOnUse'
>
<Stop stopColor='white' />
<Stop offset='1' stopColor='white' stopOpacity='0' />
</LinearGradient>

<LinearGradient
id='paint3_linear_5596_231135'
x1='199'
y1='8'
x2='0'
y2='8'
gradientUnits='userSpaceOnUse'
>
<Stop stopColor='white' />
<Stop offset='1' stopColor='white' stopOpacity='0' />
</LinearGradient>

{/* ClipPaths */}
<ClipPath id='clip0_5596_231135'>
<Path d='M16 40C16 26.7452 26.7452 16 40 16C53.2548 16 64 26.7452 64 40C64 53.2548 53.2548 64 40 64C26.7452 64 16 53.2548 16 40Z' />
</ClipPath>

<ClipPath id='clip1_5596_231135'>
<Path d='M80 28C80 23.5817 83.5817 20 88 20H314C318.418 20 322 23.5817 322 28C322 32.4183 318.418 36 314 36H88C83.5817 60 80 56.4183 80 52Z' />
</ClipPath>

<ClipPath id='clip2_5596_231135'>
<Path d='M338 28C338 23.5817 341.582 20 346 20H378C382.418 20 386 23.5817 386 28C386 32.4183 382.418 36 378 36H346C341.582 36 338 32.4183 338 28Z' />
</ClipPath>
</Defs>

{/* Avatar */}
<G clipPath='url(#clip0_5596_231135)'>
<Path
d='M16 40C16 26.7452 26.7452 16 40 16C53.2548 16 64 26.7452 64 40C64 53.2548 53.2548 64 40 64C26.7452 64 16 53.2548 16 40Z'
fill={semantics.backgroundCoreSurface}
/>
<Rect width='48' height='48' x='16' y='16' fill='url(#paint0_linear_5596_231135)' />
</G>

{/* Title */}
<G clipPath='url(#clip1_5596_231135)'>
<Path
d='M80 28C80 23.5817 83.5817 20 88 20H314C318.418 20 322 23.5817 322 28C322 32.4183 318.418 36 314 36H88C83.5817 36 80 32.4183 80 28Z'
fill={semantics.backgroundCoreSurface}
/>
<Rect width='242' height='16' x='80' y='20' fill='url(#paint1_linear_5596_231135)' />
</G>

{/* Badge */}
<G clipPath='url(#clip2_5596_231135)'>
<Path
d='M338 28C338 23.5817 341.582 20 346 20H378C382.418 20 386 23.5817 386 28C386 32.4183 382.418 36 378 36H346C341.582 36 338 32.4183 338 28Z'
fill={semantics.backgroundCoreSurface}
/>
<Rect width='48' height='16' x='338' y='20' fill='url(#paint2_linear_5596_231135)' />
</G>

{/* Subtitle */}
<Path
d='M80 52C80 47.5817 83.5817 44 88 44H272C276.418 44 280 47.5817 280 52C280 56.4183 276.418 60 272 60H88C83.5817 60 80 56.4183 80 52Z'
fill={semantics.backgroundCoreSurface}
/>
<Rect width='199' height='16' x='80' y='44' fill='url(#paint3_linear_5596_231135)' />
</Svg>
</Animated.View>
<Svg height={height} width={width}>
<AnimatedPath animatedProps={svgAnimatedProps} fill={maskFillColor || white_snow} />
</Svg>
</View>
);
};

Skeleton.displayName = 'Skeleton{channelListSkeleton}';

const useStyles = () => {
const {
theme: { semantics },
} = useTheme();

return useMemo(() => {
return StyleSheet.create({
container: {
borderBottomWidth: 1,
flexDirection: 'row',
borderBottomColor: semantics.borderCoreDefault,
},
});
}, [semantics]);
};
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const useCreateChannelsContext = ({
reloadList,
setFlatListRef,
Skeleton,
mutedStatusPosition,
}: ChannelsContextValue) => {
const channelValueString = channels
?.map(
Expand Down Expand Up @@ -80,6 +81,7 @@ export const useCreateChannelsContext = ({
reloadList,
setFlatListRef,
Skeleton,
mutedStatusPosition,
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[
Expand All @@ -91,6 +93,7 @@ export const useCreateChannelsContext = ({
loadingNextPage,
channelListInitialized,
refreshing,
mutedStatusPosition,
],
);

Expand Down
Loading