From d70aa55a783c9a2aaa28c7daa8d953a39a973f7b Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Tue, 13 Aug 2024 16:34:42 -0700 Subject: [PATCH] New styles for various empty states --- _locales/en/messages.json | 46 +++++++++++++++-- images/icons/v3/phone/phone-plus-bold.svg | 4 ++ .../icons/v3/stories/stories-display-bold.svg | 4 ++ images/icons/v3/stories/stories-display.svg | 1 - stylesheets/_modules.scss | 40 ++------------- stylesheets/components/CallsTab.scss | 26 +++++++++- stylesheets/components/Inbox.scss | 21 ++++++++ stylesheets/components/NavSidebar.scss | 38 ++++++++++++++ stylesheets/components/Stories.scss | 32 +++++++++--- ts/components/CallsList.tsx | 50 +++++++++++++------ ts/components/CallsTab.tsx | 22 +++++++- ts/components/ChatsTab.tsx | 7 +-- ts/components/LeftPane.tsx | 5 ++ ts/components/NavSidebar.tsx | 17 +++++++ ts/components/StoriesPane.tsx | 13 ++--- ts/components/StoriesTab.tsx | 30 ++++++++++- ts/components/leftPane/LeftPaneHelper.tsx | 8 +++ .../leftPane/LeftPaneInboxHelper.tsx | 26 +++------- 18 files changed, 295 insertions(+), 95 deletions(-) create mode 100644 images/icons/v3/phone/phone-plus-bold.svg create mode 100644 images/icons/v3/stories/stories-display-bold.svg delete mode 100644 images/icons/v3/stories/stories-display.svg diff --git a/_locales/en/messages.json b/_locales/en/messages.json index 5937a1ee4..b277f074c 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -5257,11 +5257,19 @@ }, "icu:emptyInboxMessage": { "messageformat": "Click the {composeIcon} above and search for your contacts or groups to message.", - "description": "Shown in the left-pane when the inbox is empty" + "description": "(Deleted 2024/08/05) Shown in the left-pane when the inbox is empty" }, "icu:composeIcon": { "messageformat": "compose button", - "description": "Shown in the left-pane when the inbox is empty. Describes the button that composes a new message." + "description": "(Deleted 2024/08/05) Shown in the left-pane when the inbox is empty. Describes the button that composes a new message." + }, + "icu:emptyInbox__title": { + "messageformat": "No chats", + "description": "Shown as a title in the left-pane when the inbox is empty" + }, + "icu:emptyInbox__subtitle": { + "messageformat": "Recent chats will appear here.", + "description": "Shown as a subtitle in the left-pane when the inbox is empty" }, "icu:ForwardMessageModal__title": { "messageformat": "Forward To", @@ -6445,6 +6453,14 @@ "messageformat": "No recent stories to show right now", "description": "Description for when there are no stories to show" }, + "icu:Stories__list__empty--title": { + "messageformat": "No stories", + "description": "Message title for when there are no stories to show" + }, + "icu:Stories__list__empty--subtitle": { + "messageformat": "New updates will appear here.", + "description": "Message subtitle for when there are no stories to show" + }, "icu:Stories__list--sending": { "messageformat": "Sending...", "description": "Pending text for story being sent in list view" @@ -6465,6 +6481,10 @@ "messageformat": "Click to view a story", "description": "Placeholder label for the story view" }, + "icu:Stories__placeholder-with-icon--text": { + "messageformat": "Click Add a story to add an update.", + "description": "Placeholder label for the story view" + }, "icu:Stories__from-to-group": { "messageformat": "{name} to {group}", "description": "Title for someone sending a story to a group" @@ -7183,6 +7203,10 @@ }, "icu:CallsTab__EmptyStateText": { "messageformat": "Click to view or start a call", + "description": "(Deleted 2024/08/12) Calls Tab > When no call is selected > Empty state > Call to action text" + }, + "icu:CallsTab__EmptyStateText--with-icon": { + "messageformat": "Click New Call to start a new voice or video call.", "description": "Calls Tab > When no call is selected > Empty state > Call to action text" }, "icu:CallsList__SearchInputPlaceholder": { @@ -7199,7 +7223,23 @@ }, "icu:CallsList__EmptyState--noQuery": { "messageformat": "No recent calls. Get started by calling a friend.", - "description": "Calls Tab > Calls List > When no results found > With no search query" + "description": "(Deleted 2024/08/12) Calls Tab > Calls List > When no results found > With no search query" + }, + "icu:CallsList__EmptyState--noQuery__title": { + "messageformat": "No calls", + "description": "Calls Tab > Calls List > When no results found > With no search query. Message title" + }, + "icu:CallsList__EmptyState--noQuery__subtitle": { + "messageformat": "Recent calls will appear here.", + "description": "Calls Tab > Calls List > When no results found > With no search query. Message subtitle" + }, + "icu:CallsList__EmptyState--noQuery--missed__title": { + "messageformat": "No missed calls", + "description": "Calls Tab > Calls List > When no missed calls found > With no search query. Message title" + }, + "icu:CallsList__EmptyState--noQuery--missed__subtitle": { + "messageformat": "Missed calls will appear here.", + "description": "Calls Tab > Calls List > When no missed calls found > With no search query. Message subtitle" }, "icu:CallsList__EmptyState--hasQuery": { "messageformat": "No results for “{query}”", diff --git a/images/icons/v3/phone/phone-plus-bold.svg b/images/icons/v3/phone/phone-plus-bold.svg new file mode 100644 index 000000000..99f0e5e41 --- /dev/null +++ b/images/icons/v3/phone/phone-plus-bold.svg @@ -0,0 +1,4 @@ + + + + diff --git a/images/icons/v3/stories/stories-display-bold.svg b/images/icons/v3/stories/stories-display-bold.svg new file mode 100644 index 000000000..ca92503ca --- /dev/null +++ b/images/icons/v3/stories/stories-display-bold.svg @@ -0,0 +1,4 @@ + + + + diff --git a/images/icons/v3/stories/stories-display.svg b/images/icons/v3/stories/stories-display.svg deleted file mode 100644 index dc1bfeff8..000000000 --- a/images/icons/v3/stories/stories-display.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/stylesheets/_modules.scss b/stylesheets/_modules.scss index f13c900a8..580c437d0 100644 --- a/stylesheets/_modules.scss +++ b/stylesheets/_modules.scss @@ -58,6 +58,11 @@ width: 128px; } + &.module-img--80 { + height: 80px; + width: 80px; + } + &.module-logo-blue { background-color: $color-ultramarine-icon; } @@ -5312,41 +5317,6 @@ button.module-calling-participants-list__contact { } } -.module-left-pane__empty { - align-items: center; - display: flex; - height: 100%; - justify-content: center; - padding-block: 0; - padding-inline: 32px; - text-align: center; - - .module-left-pane--width-narrow & { - display: none; - } - - &--composer_icon { - align-items: center; - background-color: $color-gray-05; - border-radius: 100%; - display: inline-flex; - height: 28px; - justify-content: center; - margin-bottom: -2px; - margin-inline-start: 4px; - vertical-align: bottom; - width: 28px; - - &--icon { - $icon: '../images/icons/v3/compose/compose.svg'; - @include color-svg($icon, $color-gray-90); - display: inline-block; - height: 16px; - width: 16px; - } - } -} - .module-left-pane__header { flex-grow: 0; flex-shrink: 0; diff --git a/stylesheets/components/CallsTab.scss b/stylesheets/components/CallsTab.scss index 873bf5b06..b7a29a782 100644 --- a/stylesheets/components/CallsTab.scss +++ b/stylesheets/components/CallsTab.scss @@ -44,11 +44,12 @@ align-items: center; justify-content: center; flex-direction: column; + padding-block: 78px 28px; } .CallsTab__EmptyStateIcon { - width: 56px; - height: 56px; + width: 40px; + height: 40px; opacity: 0.7; @include light-theme { @include color-svg('../images/icons/v3/phone/phone.svg', $color-gray-60); @@ -62,6 +63,7 @@ margin-block: 12px 0; margin-inline: 0; opacity: 0.7; + text-align: center; @include light-theme { color: $color-gray-60; } @@ -70,6 +72,26 @@ } } +.CallsTab__EmptyState__ActionIcon { + vertical-align: text-top; + display: inline-block; + + width: 16px; + height: 16px; + @include light-theme { + @include color-svg( + '../images/icons/v3/phone/phone-plus-bold.svg', + $color-gray-60 + ); + } + @include dark-theme { + @include color-svg( + '../images/icons/v3/phone/phone-plus-bold.svg', + $color-gray-25 + ); + } +} + .CallsTab__ConversationCallDetails { display: block; overflow: auto; diff --git a/stylesheets/components/Inbox.scss b/stylesheets/components/Inbox.scss index 15b4bdc5d..30e0b075f 100644 --- a/stylesheets/components/Inbox.scss +++ b/stylesheets/components/Inbox.scss @@ -35,6 +35,10 @@ } } +.Inbox__no-conversation-open .module-splash-screen__logo { + margin: 0; +} + .Inbox__logo { flex-shrink: 0; @@ -78,3 +82,20 @@ opacity: 1; } } + +.Inbox__welcome { + margin-block: 20px 6px; + + @include font-title-medium; + line-height: 24px; +} + +.Inbox__whatsnew { + margin: 0; +} + +// To center the content relative left pane +.Inbox__padding { + flex-grow: 1; + max-height: 28px; +} diff --git a/stylesheets/components/NavSidebar.scss b/stylesheets/components/NavSidebar.scss index 5d289a9ae..494eb1aec 100644 --- a/stylesheets/components/NavSidebar.scss +++ b/stylesheets/components/NavSidebar.scss @@ -222,3 +222,41 @@ margin-bottom: 8px; gap: 12px; } + +.NavSidebarEmpty { + position: absolute; + top: 0; + inset-inline-start: 0; + pointer-events: none; // since this is going to overlap with the navbar header + // we don't want it to capture any clicks. + + align-items: center; + display: flex; + width: 100%; + height: 100%; + justify-content: center; + padding-block: 0; + padding-inline: 32px; + text-align: center; + + .module-left-pane--width-narrow & { + display: none; + } +} + +.NavSidebarEmpty__inner { + padding-block: 100px 28px; +} + +.NavSidebarEmpty__title { + margin-block: 0px 6px; + + @include font-title-medium; + color: $color-gray-45; +} + +.NavSidebarEmpty__subtitle { + margin-block: 0; + + color: $color-gray-45; +} diff --git a/stylesheets/components/Stories.scss b/stylesheets/components/Stories.scss index 494fdeb87..6a42a50a8 100644 --- a/stylesheets/components/Stories.scss +++ b/stylesheets/components/Stories.scss @@ -123,6 +123,8 @@ flex: 1; justify-content: center; opacity: 0.7; + padding-block: 78px 28px; + @include light-theme { color: $color-gray-60; } @@ -130,23 +132,41 @@ color: $color-gray-25; } - &__stories { - height: 56px; - margin-bottom: 22px; - width: 56px; + &__icon { + margin-bottom: 12px; + width: 40px; + height: 40px; @include light-theme { @include color-svg( - '../images/icons/v3/stories/stories-display.svg', + '../images/icons/v3/stories/stories-display-bold.svg', $color-gray-60 ); } @include dark-theme { @include color-svg( - '../images/icons/v3/stories/stories-display.svg', + '../images/icons/v3/stories/stories-display-bold.svg', $color-gray-25 ); } } + + &__text { + text-align: center; + } + + &__text__action { + vertical-align: text-top; + display: inline-block; + + width: 16px; + height: 16px; + @include light-theme { + @include color-svg('../images/icons/v3/plus/plus.svg', $color-gray-60); + } + @include dark-theme { + @include color-svg('../images/icons/v3/plus/plus.svg', $color-gray-25); + } + } } &__hidden-stories { diff --git a/ts/components/CallsList.tsx b/ts/components/CallsList.tsx index 07724e651..3e9f8c016 100644 --- a/ts/components/CallsList.tsx +++ b/ts/components/CallsList.tsx @@ -38,7 +38,7 @@ import { drop } from '../util/drop'; import { strictAssert } from '../util/assert'; import { UserText } from './UserText'; import { I18n } from './I18n'; -import { NavSidebarSearchHeader } from './NavSidebar'; +import { NavSidebarSearchHeader, NavSidebarEmpty } from './NavSidebar'; import { SizeObserver } from '../hooks/useSizeObserver'; import { formatCallHistoryGroup, @@ -205,21 +205,27 @@ export function CallsList({ const searchStateQuery = searchState.options?.query ?? ''; const searchStateStatus = searchState.options?.status ?? CallHistoryFilterStatus.All; + const hasSearchStateQuery = searchStateQuery !== ''; const searchFiltering = - searchStateQuery !== '' || - searchStateStatus !== CallHistoryFilterStatus.All; + hasSearchStateQuery || searchStateStatus !== CallHistoryFilterStatus.All; const searchPending = searchState.state === 'pending'; + const isEmpty = !searchState.results?.items?.length; const rows = useMemo(() => { let results: ReadonlyArray = searchState.results?.items ?? []; - if (results.length === 0) { + if (results.length === 0 && hasSearchStateQuery) { results = ['EmptyState']; } if (!searchFiltering && canCreateCallLinks) { results = ['CreateCallLink', ...results]; } return results; - }, [searchState.results?.items, searchFiltering, canCreateCallLinks]); + }, [ + searchState.results?.items, + hasSearchStateQuery, + searchFiltering, + canCreateCallLinks, + ]); const rowCount = rows.length; @@ -698,17 +704,13 @@ export function CallsList({ if (item === 'EmptyState') { return (
- {searchStateQuery === '' ? ( - i18n('icu:CallsList__EmptyState--noQuery') - ) : ( - , - }} - /> - )} + , + }} + />
); } @@ -930,6 +932,22 @@ export function CallsList({ return ( <> + {isEmpty && !searchFiltering && ( + + )} + {isEmpty && + statusInput === CallHistoryFilterStatus.Missed && + !hasSearchStateQuery && ( + + )}

- {i18n('icu:CallsTab__EmptyStateText')} + { + let label: string | undefined; + const first = children[0]; + if (typeof first === 'string') { + label = first; + } + return ( + + ); + }, + }} + />

) : ( diff --git a/ts/components/ChatsTab.tsx b/ts/components/ChatsTab.tsx index 4ba91a8eb..198007e53 100644 --- a/ts/components/ChatsTab.tsx +++ b/ts/components/ChatsTab.tsx @@ -58,15 +58,16 @@ export function ChatsTab({ ) : (
{renderMiniPlayer({ shouldFlow: false })} -
-

+
+

{getEnvironment() !== Environment.Staging ? i18n('icu:welcomeToSignal') : 'THIS IS A STAGING DESKTOP'}

-

+

+
)}
diff --git a/ts/components/LeftPane.tsx b/ts/components/LeftPane.tsx index 30b840686..6ff881713 100644 --- a/ts/components/LeftPane.tsx +++ b/ts/components/LeftPane.tsx @@ -472,6 +472,10 @@ export function LeftPane({ startSearch, ]); + const backgroundNode = helper.getBackgroundNode({ + i18n, + }); + const preRowsNode = helper.getPreRowsNode({ clearConversationSearch, clearGroupCreationError, @@ -686,6 +690,7 @@ export function LeftPane({ } > + {backgroundNode}

; } + +export function NavSidebarEmpty({ + title, + subtitle, +}: { + title: string; + subtitle: string; +}): JSX.Element { + return ( +
+
+

{title}

+

{subtitle}

+
+
+ ); +} diff --git a/ts/components/StoriesPane.tsx b/ts/components/StoriesPane.tsx index bbb614a69..f672c8ae8 100644 --- a/ts/components/StoriesPane.tsx +++ b/ts/components/StoriesPane.tsx @@ -18,7 +18,7 @@ import { MyStoryButton } from './MyStoryButton'; import { SearchInput } from './SearchInput'; import { StoryListItem } from './StoryListItem'; import { isNotNil } from '../util/isNotNil'; -import { NavSidebarSearchHeader } from './NavSidebar'; +import { NavSidebarSearchHeader, NavSidebarEmpty } from './NavSidebar'; const FUSE_OPTIONS: Fuse.IFuseOptions = { getFn: (story, path) => { @@ -104,6 +104,12 @@ export function StoriesPane({ }, [searchTerm, stories]); return ( <> + {!stories.length && ( + + )} )} - {!stories.length && ( -
- {i18n('icu:Stories__list-empty')} -
- )}
); diff --git a/ts/components/StoriesTab.tsx b/ts/components/StoriesTab.tsx index 960cdba4a..6860ada8e 100644 --- a/ts/components/StoriesTab.tsx +++ b/ts/components/StoriesTab.tsx @@ -24,6 +24,7 @@ import { StoriesPane } from './StoriesPane'; import { NavSidebar, NavSidebarActionButton } from './NavSidebar'; import { StoriesAddStoryButton } from './StoriesAddStoryButton'; import { ContextMenu } from './ContextMenu'; +import { I18n } from './I18n'; import type { WidthBreakpoint } from './_util'; import type { UnreadStats } from '../util/countUnreadStats'; @@ -217,8 +218,33 @@ export function StoriesTab({ )}
-
- {i18n('icu:Stories__placeholder--text')} +
+
+ {stories.length ? ( + i18n('icu:Stories__placeholder--text') + ) : ( + { + let label: string | undefined; + const first = children[0]; + if (typeof first === 'string') { + label = first; + } + return ( + + ); + }, + }} + /> + )} +
); diff --git a/ts/components/leftPane/LeftPaneHelper.tsx b/ts/components/leftPane/LeftPaneHelper.tsx index 790952ae3..135d83499 100644 --- a/ts/components/leftPane/LeftPaneHelper.tsx +++ b/ts/components/leftPane/LeftPaneHelper.tsx @@ -66,6 +66,14 @@ export abstract class LeftPaneHelper { return undefined; } + getBackgroundNode( + _: Readonly<{ + i18n: LocalizerType; + }> + ): null | ReactChild { + return null; + } + getPreRowsNode( _: Readonly<{ clearConversationSearch: () => unknown; diff --git a/ts/components/leftPane/LeftPaneInboxHelper.tsx b/ts/components/leftPane/LeftPaneInboxHelper.tsx index dd7208aab..90d182366 100644 --- a/ts/components/leftPane/LeftPaneInboxHelper.tsx +++ b/ts/components/leftPane/LeftPaneInboxHelper.tsx @@ -5,7 +5,6 @@ import { last } from 'lodash'; import type { ReactChild } from 'react'; import React from 'react'; -import { I18n } from '../I18n'; import type { ToFindType } from './LeftPaneHelper'; import type { ConversationType, @@ -15,6 +14,7 @@ import { LeftPaneHelper } from './LeftPaneHelper'; import { getConversationInDirection } from './getConversationInDirection'; import type { Row } from '../ConversationList'; import { RowType } from '../ConversationList'; +import { NavSidebarEmpty } from '../NavSidebar'; import type { PropsData as ConversationListItemPropsType } from '../conversationList/ConversationListItem'; import type { LocalizerType } from '../../types/Util'; import { handleKeydownForSearch } from './handleKeydownForSearch'; @@ -121,31 +121,17 @@ export class LeftPaneInboxHelper extends LeftPaneHelper ); } - override getPreRowsNode({ + override getBackgroundNode({ i18n, }: Readonly<{ i18n: LocalizerType; }>): ReactChild | null { if (this.getRowCount() === 0) { return ( -
-
- - {i18n('icu:composeIcon')} - - - - - ), - }} - /> -
-
+ ); }