[Gekidou] Add empty unreads placeholder (#6350)

* Add empty unreads placeholder

* ux feedback

* Use SidebarText witn 0.12 opacity as the button bg color

* Set tertiary button helper to use theme.sidebarText color
This commit is contained in:
Elias Nahum
2022-06-06 15:03:29 -04:00
committed by GitHub
parent 2f07b7afc8
commit a7c49100ab
9 changed files with 190 additions and 14 deletions

View File

@@ -102,7 +102,17 @@ const Categories = ({categories, onlyUnreads, unreadsOnTop}: Props) => {
return (
<>
{!switchingTeam && !initiaLoad && (
{!switchingTeam && !initiaLoad && onlyUnreads &&
<View style={styles.mainList}>
<UnreadCategories
currentTeamId={teamId}
isTablet={isTablet}
onChannelSwitch={onChannelSwitch}
onlyUnreads={onlyUnreads}
/>
</View>
}
{!switchingTeam && !initiaLoad && !onlyUnreads && (
<FlatList
data={categoriesToShow}
ref={listRef}

View File

@@ -0,0 +1,47 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import * as React from 'react';
import Svg, {Ellipse, Path} from 'react-native-svg';
import {useTheme} from '@context/theme';
function SvgComponent() {
const theme = useTheme();
return (
<Svg
width={140}
height={140}
viewBox='0 0 140 140'
fill='none'
>
<Ellipse
cx={70}
cy={114}
rx={45}
ry={3}
fill='#000'
fillOpacity={0.08}
/>
<Path
d='M107.838 26.436h-75.65a10.577 10.577 0 00-7.475 3.06 10.533 10.533 0 00-3.117 7.44v47.977a10.516 10.516 0 003.117 7.44 10.56 10.56 0 007.475 3.06h11.165v17.959l16.746-17.96h47.712a10.576 10.576 0 007.476-3.06 10.52 10.52 0 003.117-7.439V36.937a10.521 10.521 0 00-3.108-7.43 10.567 10.567 0 00-7.458-3.07z'
fill='#FFBC1F'
/>
<Path
d='M60.1 95.413h47.711a10.576 10.576 0 007.476-3.06 10.52 10.52 0 003.117-7.439V55.785s-3.331 26.935-3.93 29.306c-.598 2.37-1.786 5.918-7.413 6.506-5.627.588-46.962 3.815-46.962 3.815z'
fill='#CC8F00'
/>
<Path
d='M29.447 46.526a21.375 21.375 0 013.823-7.464 21.426 21.426 0 016.403-5.424.74.74 0 00-.303-1.4c-4.77-.285-14.398.731-11.38 14.217a.749.749 0 001.171.469.748.748 0 00.286-.398z'
fill='#FFD470'
/>
<Path
d='M86.565 44.167L66.313 66.44l-5.878-4.455h-3.268l9.146 14.848 23.52-32.664h-3.268z'
fill={theme.sidebarBg}
/>
</Svg>
);
}
export default SvgComponent;

View File

@@ -0,0 +1,88 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {useMemo} from 'react';
import {View} from 'react-native';
import {showUnreadChannelsOnly} from '@actions/local/channel';
import FormattedText from '@components/formatted_text';
import TouchableWithFeedback from '@components/touchable_with_feedback';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
import EmptyIllustration from './empty_unreads';
type Props = {
onlyUnreads: boolean;
}
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
button: {
marginTop: 24,
},
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 40,
maxWidth: 480,
top: -20,
},
title: {
color: theme.sidebarText,
textAlign: 'center',
...typography('Heading', 400, 'SemiBold'),
},
paragraph: {
marginTop: 8,
textAlign: 'center',
color: changeOpacity(theme.sidebarText, 0.72),
...typography('Body', 200),
},
}));
function EmptyUnreads({onlyUnreads}: Props) {
const theme = useTheme();
const serverUrl = useServerUrl();
const styles = getStyleSheet(theme);
const buttonStyle = useMemo(() => [buttonBackgroundStyle(theme, 'lg', 'tertiary', 'inverted'), styles.button],
[theme]);
const onPress = () => {
showUnreadChannelsOnly(serverUrl, !onlyUnreads);
};
return (
<View style={styles.container}>
<EmptyIllustration/>
<FormattedText
defaultMessage='No more unreads'
id='unreads.empty.title'
style={styles.title}
testID='unreads.empty.title'
/>
<FormattedText
defaultMessage={'Turn off the unread filter to show all your channels.'}
id='unreads.empty.paragraph'
style={styles.paragraph}
testID='unreads.empty.paragraph'
/>
<TouchableWithFeedback
style={buttonStyle}
onPress={onPress}
type={'opacity'}
>
<FormattedText
id='unreads.empty.show_all'
defaultMessage='Show all'
style={buttonTextStyle(theme, 'lg', 'tertiary', 'inverted')}
/>
</TouchableWithFeedback>
</View>
);
}
export default EmptyUnreads;

View File

@@ -9,7 +9,7 @@ import {combineLatestWith, map, switchMap} from 'rxjs/operators';
import {Preferences} from '@constants';
import {getPreferenceAsBool} from '@helpers/api/preference';
import {filterAndSortMyChannels, makeChannelsMap} from '@helpers/database';
import {getChannelById, observeChannelsByLastPostAt, observeNotifyPropsByChannels, queryMyChannelUnreads} from '@queries/servers/channel';
import {getChannelById, observeChannelsByLastPostAt, observeCurrentChannel, observeNotifyPropsByChannels, queryMyChannelUnreads} from '@queries/servers/channel';
import {queryPreferencesByCategoryAndName} from '@queries/servers/preference';
import {observeLastUnreadChannelId} from '@queries/servers/system';
@@ -45,7 +45,10 @@ const enhanced = withObservables(['currentTeamId', 'isTablet', 'onlyUnreads'], (
const unreadChannels = unreadsOnTop.pipe(switchMap((gU) => {
if (gU || onlyUnreads) {
const lastUnread = isTablet ? observeLastUnreadChannelId(database).pipe(switchMap(getC)) : of$(undefined);
const lastUnread = isTablet ? observeLastUnreadChannelId(database).pipe(
switchMap(getC),
switchMap((ch) => (ch ? of$(ch) : observeCurrentChannel(database))),
) : of$(undefined);
const myUnreadChannels = queryMyChannelUnreads(database, currentTeamId).observeWithColumns(['last_post_at']);
const notifyProps = myUnreadChannels.pipe(switchMap((cs) => observeNotifyPropsByChannels(database, cs)));
const channels = myUnreadChannels.pipe(switchMap((myChannels) => observeChannelsByLastPostAt(database, myChannels)));

View File

@@ -22,6 +22,7 @@ describe('components/channel_list/categories/body', () => {
<UnreadsCategory
unreadChannels={[]}
onChannelSwitch={() => undefined}
onlyUnreads={false}
/>,
{database},
);

View File

@@ -7,12 +7,20 @@ import {FlatList, Text} from 'react-native';
import ChannelItem from '@components/channel_item';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
import Empty from './empty_state';
import type ChannelModel from '@typings/database/models/servers/channel';
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
empty: {
alignItems: 'center',
flexGrow: 1,
justifyContent: 'center',
},
heading: {
color: changeOpacity(theme.sidebarText, 0.64),
...typography('Heading', 75),
@@ -24,16 +32,18 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
}));
type UnreadCategoriesProps = {
unreadChannels: ChannelModel[];
onChannelSwitch: (channelId: string) => void;
onlyUnreads: boolean;
unreadChannels: ChannelModel[];
}
const extractKey = (item: ChannelModel) => item.id;
const UnreadCategories = ({onChannelSwitch, unreadChannels}: UnreadCategoriesProps) => {
const theme = useTheme();
const styles = getStyleSheet(theme);
const UnreadCategories = ({onChannelSwitch, onlyUnreads, unreadChannels}: UnreadCategoriesProps) => {
const intl = useIntl();
const theme = useTheme();
const isTablet = useIsTablet();
const styles = getStyleSheet(theme);
const renderItem = useCallback(({item}: {item: ChannelModel}) => {
return (
@@ -44,20 +54,31 @@ const UnreadCategories = ({onChannelSwitch, unreadChannels}: UnreadCategoriesPro
);
}, [onChannelSwitch]);
if (!unreadChannels.length) {
const showEmptyState = onlyUnreads && !unreadChannels.length;
const showTitle = !onlyUnreads || (onlyUnreads && !showEmptyState);
const EmptyState = showEmptyState && !isTablet ? (
<Empty onlyUnreads={onlyUnreads}/>
) : undefined;
if (!unreadChannels.length && !onlyUnreads) {
return null;
}
return (
<>
{showTitle &&
<Text
style={styles.heading}
>
{intl.formatMessage({id: 'mobile.channel_list.unreads', defaultMessage: 'UNREADS'})}
</Text>
}
<FlatList
contentContainerStyle={showEmptyState && !isTablet && styles.empty}
data={unreadChannels}
renderItem={renderItem}
keyExtractor={extractKey}
ListEmptyComponent={EmptyState}
removeClippedSubviews={true}
/>
</>

View File

@@ -81,7 +81,7 @@ const ThreadsButton = ({currentChannelId, onlyUnreads, unreadsAndMentions}: Prop
return [container, icon, text];
}, [customStyles, isActive, styles, unreads]);
if (onlyUnreads && !unreads && !mentions) {
if (onlyUnreads && !isActive && !unreads && !mentions) {
return null;
}

View File

@@ -241,16 +241,16 @@ export const buttonBackgroundStyle = (
},
inverted: {
default: {
backgroundColor: changeOpacity('#FFFFFF', 0.12),
backgroundColor: changeOpacity(theme.sidebarText, 0.12),
},
hover: {
backgroundColor: changeOpacity('#FFFFFF', 0.16),
backgroundColor: changeOpacity(theme.sidebarText, 0.16),
},
active: {
backgroundColor: changeOpacity('#FFFFFF', 0.24),
backgroundColor: changeOpacity(theme.sidebarText, 0.24),
},
focus: {
backgroundColor: changeOpacity('#FFFFFF', 0.08),
backgroundColor: changeOpacity(theme.sidebarText, 0.08),
borderColor: theme.sidebarTextActiveBorder, // @to-do; needs 32% white?
borderWidth: 2,
},
@@ -388,6 +388,10 @@ export const buttonTextStyle = (
color = theme.buttonBg;
}
if (type === 'inverted' && emphasis === 'tertiary') {
color = theme.sidebarText;
}
const styles = StyleSheet.create({
main: {
fontFamily: 'OpenSans-SemiBold',

View File

@@ -555,7 +555,6 @@
"notification_settings.auto_responder.default_message": "Hello, I am out of office and unable to respond to messages.",
"notification_settings.auto_responder.enabled": "Enabled",
"notification_settings.auto_responder.footer_message": "Set a custom message that will be automatically sent in response to Direct Messages. Mentions in Public and Private Channels will not trigger the automated reply. Enabling Automatic Replies sets your status to Out of Office and disables email and push notifications.",
"notification_settings.auto_responder.message_placeholder": "Message",
"notification_settings.mention.reply": "Send reply notifications for",
"notification_settings.mentions": "Mentions",
"notification_settings.mentions_replies": "Mentions and Replies",
@@ -701,6 +700,9 @@
"threads.replies": "{count} {count, plural, one {reply} other {replies}}",
"threads.unfollowMessage": "Unfollow Message",
"threads.unfollowThread": "Unfollow Thread",
"unreads.empty.paragraph": "Turn off the unread filter to show all your channels.",
"unreads.empty.show_all": "Show all",
"unreads.empty.title": "No more unreads",
"user.edit_profile.email.auth_service": "Login occurs through {service}. Email cannot be updated. Email address used for notifications is {email}.",
"user.edit_profile.email.web_client": "Email must be updated using a web client or desktop application.",
"user.settings.general.email": "Email",