From a7c49100abfa857ced81228fb2ef870799512d90 Mon Sep 17 00:00:00 2001 From: Elias Nahum Date: Mon, 6 Jun 2022 15:03:29 -0400 Subject: [PATCH] [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 --- .../categories_list/categories/categories.tsx | 12 ++- .../unreads/empty_state/empty_unreads.tsx | 47 ++++++++++ .../categories/unreads/empty_state/index.tsx | 88 +++++++++++++++++++ .../categories/unreads/index.ts | 7 +- .../categories/unreads/unreads.test.tsx | 1 + .../categories/unreads/unreads.tsx | 31 +++++-- .../threads_button/threads_button.tsx | 2 +- app/utils/buttonStyles.ts | 12 ++- assets/base/i18n/en.json | 4 +- 9 files changed, 190 insertions(+), 14 deletions(-) create mode 100644 app/screens/home/channel_list/categories_list/categories/unreads/empty_state/empty_unreads.tsx create mode 100644 app/screens/home/channel_list/categories_list/categories/unreads/empty_state/index.tsx diff --git a/app/screens/home/channel_list/categories_list/categories/categories.tsx b/app/screens/home/channel_list/categories_list/categories/categories.tsx index 9297c38a79..a27043dc7f 100644 --- a/app/screens/home/channel_list/categories_list/categories/categories.tsx +++ b/app/screens/home/channel_list/categories_list/categories/categories.tsx @@ -102,7 +102,17 @@ const Categories = ({categories, onlyUnreads, unreadsOnTop}: Props) => { return ( <> - {!switchingTeam && !initiaLoad && ( + {!switchingTeam && !initiaLoad && onlyUnreads && + + + + } + {!switchingTeam && !initiaLoad && !onlyUnreads && ( + + + + + + + ); +} + +export default SvgComponent; diff --git a/app/screens/home/channel_list/categories_list/categories/unreads/empty_state/index.tsx b/app/screens/home/channel_list/categories_list/categories/unreads/empty_state/index.tsx new file mode 100644 index 0000000000..203936a4c8 --- /dev/null +++ b/app/screens/home/channel_list/categories_list/categories/unreads/empty_state/index.tsx @@ -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 ( + + + + + + + + + ); +} + +export default EmptyUnreads; diff --git a/app/screens/home/channel_list/categories_list/categories/unreads/index.ts b/app/screens/home/channel_list/categories_list/categories/unreads/index.ts index bd470a14bb..c83f160d2d 100644 --- a/app/screens/home/channel_list/categories_list/categories/unreads/index.ts +++ b/app/screens/home/channel_list/categories_list/categories/unreads/index.ts @@ -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))); diff --git a/app/screens/home/channel_list/categories_list/categories/unreads/unreads.test.tsx b/app/screens/home/channel_list/categories_list/categories/unreads/unreads.test.tsx index cc22fdb883..25e061c65a 100644 --- a/app/screens/home/channel_list/categories_list/categories/unreads/unreads.test.tsx +++ b/app/screens/home/channel_list/categories_list/categories/unreads/unreads.test.tsx @@ -22,6 +22,7 @@ describe('components/channel_list/categories/body', () => { undefined} + onlyUnreads={false} />, {database}, ); diff --git a/app/screens/home/channel_list/categories_list/categories/unreads/unreads.tsx b/app/screens/home/channel_list/categories_list/categories/unreads/unreads.tsx index 7f6b391d5a..343af45fc1 100644 --- a/app/screens/home/channel_list/categories_list/categories/unreads/unreads.tsx +++ b/app/screens/home/channel_list/categories_list/categories/unreads/unreads.tsx @@ -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 ? ( + + ) : undefined; + + if (!unreadChannels.length && !onlyUnreads) { return null; } + return ( <> + {showTitle && {intl.formatMessage({id: 'mobile.channel_list.unreads', defaultMessage: 'UNREADS'})} + } diff --git a/app/screens/home/channel_list/categories_list/threads_button/threads_button.tsx b/app/screens/home/channel_list/categories_list/threads_button/threads_button.tsx index 7131dd710f..51444a4024 100644 --- a/app/screens/home/channel_list/categories_list/threads_button/threads_button.tsx +++ b/app/screens/home/channel_list/categories_list/threads_button/threads_button.tsx @@ -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; } diff --git a/app/utils/buttonStyles.ts b/app/utils/buttonStyles.ts index 2e8c962e43..f77ef25c96 100644 --- a/app/utils/buttonStyles.ts +++ b/app/utils/buttonStyles.ts @@ -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', diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index a16bd92475..befede8672 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -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",