Update call tab design based on feedback
This commit is contained in:
parent
ce28993c78
commit
3268d3e6eb
32 changed files with 601 additions and 289 deletions
|
@ -6684,7 +6684,7 @@
|
|||
"description": "Calls Tab > Calls List > Search Input > Placeholder"
|
||||
},
|
||||
"icu:CallsList__ToggleFilterByMissedLabel": {
|
||||
"messageformat": "Toggle filter by missed",
|
||||
"messageformat": "Filter by missed",
|
||||
"description": "Calls Tab > Calls List > Toggle search filter by missed > Accessibility label"
|
||||
},
|
||||
"icu:CallsList__ToggleFilterByMissed__RoleDescription": {
|
||||
|
|
|
@ -868,6 +868,7 @@ $rtl-icon-map: (
|
|||
}
|
||||
|
||||
@mixin NavTabs__Scroller {
|
||||
padding-bottom: 8px;
|
||||
@include scrollbar;
|
||||
&::-webkit-scrollbar-thumb {
|
||||
@include light-theme {
|
||||
|
|
|
@ -6974,7 +6974,8 @@ button.module-image__border-overlay:focus {
|
|||
content: '';
|
||||
display: block;
|
||||
height: 0;
|
||||
margin-inline-start: -6px;
|
||||
/* stylelint-disable-next-line liberty/use-logical-spec */
|
||||
margin-left: -6px;
|
||||
margin-top: -6px;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
|
@ -6993,12 +6994,15 @@ button.module-image__border-overlay:focus {
|
|||
|
||||
&[data-placement='right'] {
|
||||
.module-tooltip-arrow {
|
||||
inset-inline-start: 0;
|
||||
/* stylelint-disable-next-line liberty/use-logical-spec */
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.module-tooltip-arrow::after {
|
||||
inset-inline-start: -6px;
|
||||
border-inline-end-color: var(--tooltip-background-color);
|
||||
/* stylelint-disable-next-line liberty/use-logical-spec */
|
||||
left: -6px;
|
||||
/* stylelint-disable-next-line liberty/use-logical-spec */
|
||||
border-right-color: var(--tooltip-background-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7015,12 +7019,15 @@ button.module-image__border-overlay:focus {
|
|||
|
||||
&[data-placement='left'] {
|
||||
.module-tooltip-arrow {
|
||||
inset-inline-end: 0;
|
||||
/* stylelint-disable-next-line liberty/use-logical-spec */
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.module-tooltip-arrow::after {
|
||||
inset-inline-end: -12px;
|
||||
border-inline-start-color: var(--tooltip-background-color);
|
||||
/* stylelint-disable-next-line liberty/use-logical-spec */
|
||||
right: -12px;
|
||||
/* stylelint-disable-next-line liberty/use-logical-spec */
|
||||
border-left-color: var(--tooltip-background-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -286,4 +286,5 @@ $z-index-above-context-menu: 126;
|
|||
$NavTabs__width: 80px;
|
||||
// These values are 'block' specific to coordinate with the NavSidebar__Header
|
||||
$NavTabs__Item__blockPadding: 2px;
|
||||
$NavTabs__Toggle__blockPadding: 8px;
|
||||
$NavTabs__ItemButton__blockPadding: 10px;
|
||||
|
|
|
@ -52,6 +52,8 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
padding-block: 80px;
|
||||
padding-inline: 24px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.CallsTab__ClearCallHistoryIcon {
|
||||
|
@ -183,8 +185,8 @@
|
|||
|
||||
.CallsList__LoadingAvatar {
|
||||
display: block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
|
@ -206,10 +208,20 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.CallsList__ItemCallInfo--missed {
|
||||
.CallsList__ItemCallInfo {
|
||||
@include font-body-1;
|
||||
}
|
||||
|
||||
// Override .ListTile__subtitle so ellipsis is correct color
|
||||
.CallsList__Item--missed .ListTile__subtitle {
|
||||
color: $color-accent-red;
|
||||
}
|
||||
|
||||
// Override .ListTile
|
||||
.ListTile.CallsList__ItemTile {
|
||||
padding-block: 12px;
|
||||
}
|
||||
|
||||
.CallsList__Item--selected .CallsList__ItemTile {
|
||||
@include light-theme {
|
||||
background-color: $color-gray-15;
|
||||
|
|
|
@ -99,7 +99,6 @@
|
|||
|
||||
&__popper--single-item &__option {
|
||||
padding-block: 12px;
|
||||
padding-inline: 6px;
|
||||
}
|
||||
|
||||
&__divider {
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
justify-content: center;
|
||||
padding-bottom: 8px;
|
||||
padding-top: 12px;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
|
@ -41,6 +42,11 @@
|
|||
@include dark-theme {
|
||||
color: $color-gray-25;
|
||||
}
|
||||
|
||||
&__about,
|
||||
&__phone-number {
|
||||
user-select: text;
|
||||
}
|
||||
}
|
||||
|
||||
&__root--editable &__title {
|
||||
|
@ -513,11 +519,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.ConversationDetails__CallHistoryGroup__header {
|
||||
@include font-title-2;
|
||||
margin-block: 24px 16px;
|
||||
}
|
||||
|
||||
.ConversationDetails__CallHistoryGroup__List {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
&__body {
|
||||
margin-top: calc(#{$header-height} + var(--title-bar-drag-area-height));
|
||||
padding-inline: 24px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
-webkit-line-clamp: 2;
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
user-select: text;
|
||||
|
||||
a {
|
||||
@include light-theme {
|
||||
|
|
|
@ -125,6 +125,11 @@
|
|||
@include sr-only;
|
||||
}
|
||||
|
||||
.NavSidebar .module-SearchInput__container {
|
||||
// override .module-SearchInput__container
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.NavSidebar__Content {
|
||||
flex: 1 1 0%;
|
||||
min-height: 0;
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
// Copyright 2023 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
$NavTabs__ToggleButton__blockPadding: 4px;
|
||||
$NavTabs__ItemIcon__size: 20px;
|
||||
$NavTabs__ProfileAvatar__size: 28px;
|
||||
|
||||
// This effectively wraps the entire app
|
||||
.NavTabs__Container {
|
||||
position: relative;
|
||||
|
@ -14,6 +18,7 @@
|
|||
display: flex;
|
||||
flex-shrink: 0;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: $NavTabs__width;
|
||||
height: 100%;
|
||||
padding-top: var(--title-bar-drag-area-height);
|
||||
|
@ -43,6 +48,12 @@
|
|||
// Handled by .NavTabs__ItemButton
|
||||
outline: none;
|
||||
}
|
||||
&.NavTabs__Toggle {
|
||||
padding-block: calc(
|
||||
$NavTabs__Item__blockPadding + $NavTabs__ItemButton__blockPadding -
|
||||
$NavTabs__ToggleButton__blockPadding
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.NavTabs__ItemButton {
|
||||
|
@ -50,7 +61,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: $NavTabs__ItemButton__blockPadding;
|
||||
padding-block: $NavTabs__ItemButton__blockPadding;
|
||||
border-radius: 8px;
|
||||
.NavTabs__Item:hover &,
|
||||
.NavTabs__Item:focus-visible & {
|
||||
|
@ -73,6 +84,21 @@
|
|||
background: $color-gray-62;
|
||||
}
|
||||
}
|
||||
|
||||
.NavTabs__Toggle & {
|
||||
width: fit-content;
|
||||
padding: $NavTabs__ToggleButton__blockPadding;
|
||||
margin-block: 0;
|
||||
margin-inline: auto;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.NavTabs__Item--Profile & {
|
||||
// Normalize for the size difference of the avatar vs sidebar icons
|
||||
padding-block: calc(
|
||||
$NavTabs__ItemButton__blockPadding -
|
||||
(($NavTabs__ProfileAvatar__size - $NavTabs__ItemIcon__size) / 2)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.NavTabs__ItemContent {
|
||||
|
@ -107,8 +133,8 @@
|
|||
|
||||
.NavTabs__ItemIcon {
|
||||
display: block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
width: $NavTabs__ItemIcon__size;
|
||||
height: $NavTabs__ItemIcon__size;
|
||||
}
|
||||
|
||||
@mixin NavTabs__Icon($icon) {
|
||||
|
@ -157,10 +183,18 @@
|
|||
}
|
||||
|
||||
.NavTabs__TabList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.NavTabs__Misc {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
&__container {
|
||||
position: relative;
|
||||
flex: 1 0 0;
|
||||
margin-inline: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
|
|
|
@ -83,7 +83,6 @@
|
|||
flex-direction: column;
|
||||
flex: 1;
|
||||
overflow-y: overlay;
|
||||
padding-block: 0;
|
||||
padding-inline: 16px;
|
||||
|
||||
&--empty {
|
||||
|
|
|
@ -45,7 +45,13 @@
|
|||
&::before {
|
||||
@include rounded-corners;
|
||||
background: inherit;
|
||||
border: 1.5px solid $color-gray-60;
|
||||
border: 1.5px solid;
|
||||
@include light-theme {
|
||||
border-color: $color-gray-25;
|
||||
}
|
||||
@include dark-theme {
|
||||
border-color: $color-gray-60;
|
||||
}
|
||||
content: '';
|
||||
display: block;
|
||||
height: 20px;
|
||||
|
@ -80,7 +86,12 @@
|
|||
&__viewers {
|
||||
display: flex;
|
||||
@include font-body-2;
|
||||
color: $color-gray-25;
|
||||
@include light-theme {
|
||||
color: $color-gray-60;
|
||||
}
|
||||
@include dark-theme {
|
||||
color: $color-gray-25;
|
||||
}
|
||||
}
|
||||
|
||||
&__left {
|
||||
|
@ -96,13 +107,23 @@
|
|||
align-items: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: $color-gray-75;
|
||||
@include light-theme {
|
||||
background: $color-gray-15;
|
||||
}
|
||||
@include dark-theme {
|
||||
background: $color-gray-75;
|
||||
}
|
||||
|
||||
&::after {
|
||||
@include color-svg($svg, $color-white);
|
||||
content: '';
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
@include light-theme {
|
||||
@include color-svg($svg, $color-black);
|
||||
}
|
||||
@include dark-theme {
|
||||
@include color-svg($svg, $color-white);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,10 +147,22 @@
|
|||
|
||||
&__delete {
|
||||
@include button-reset;
|
||||
@include color-svg('../images/icons/v3/trash/trash.svg', $color-gray-25);
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
visibility: hidden;
|
||||
|
||||
@include light-theme {
|
||||
@include color-svg(
|
||||
'../images/icons/v3/trash/trash.svg',
|
||||
$color-gray-45
|
||||
);
|
||||
}
|
||||
@include dark-theme {
|
||||
@include color-svg(
|
||||
'../images/icons/v3/trash/trash.svg',
|
||||
$color-gray-25
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover &__delete {
|
||||
|
@ -139,8 +172,14 @@
|
|||
|
||||
&__divider {
|
||||
width: 100%;
|
||||
border: 0 solid $color-gray-65;
|
||||
border: 0 solid;
|
||||
border-top-width: 1px;
|
||||
@include light-theme {
|
||||
border-color: $color-gray-15;
|
||||
}
|
||||
@include dark-theme {
|
||||
border-color: $color-gray-65;
|
||||
}
|
||||
}
|
||||
|
||||
&__input__container {
|
||||
|
@ -150,8 +189,13 @@
|
|||
|
||||
&__visibility {
|
||||
@include font-subtitle;
|
||||
color: $color-gray-25;
|
||||
margin-top: 10px;
|
||||
@include light-theme {
|
||||
color: $color-gray-60;
|
||||
}
|
||||
@include dark-theme {
|
||||
color: $color-gray-25;
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
|
@ -161,9 +205,14 @@
|
|||
|
||||
&__description {
|
||||
@include font-subtitle;
|
||||
color: $color-gray-25;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 16px;
|
||||
@include light-theme {
|
||||
color: $color-gray-60;
|
||||
}
|
||||
@include dark-theme {
|
||||
color: $color-gray-25;
|
||||
}
|
||||
}
|
||||
|
||||
&__listHeader {
|
||||
|
@ -215,7 +264,12 @@
|
|||
}
|
||||
|
||||
&__checkbox-description {
|
||||
color: $color-gray-25;
|
||||
@include light-theme {
|
||||
color: $color-gray-60;
|
||||
}
|
||||
@include dark-theme {
|
||||
color: $color-gray-25;
|
||||
}
|
||||
}
|
||||
|
||||
&__conversation-list {
|
||||
|
@ -226,11 +280,21 @@
|
|||
|
||||
&__disclaimer {
|
||||
@include font-subtitle;
|
||||
color: $color-gray-25;
|
||||
@include light-theme {
|
||||
color: $color-gray-60;
|
||||
}
|
||||
@include dark-theme {
|
||||
color: $color-gray-25;
|
||||
}
|
||||
|
||||
&__learn-more {
|
||||
@include button-reset;
|
||||
color: $color-gray-05;
|
||||
@include light-theme {
|
||||
color: $color-gray-90;
|
||||
}
|
||||
@include dark-theme {
|
||||
color: $color-gray-05;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,8 +306,13 @@
|
|||
|
||||
&__stories-off-text {
|
||||
flex: 1;
|
||||
color: $color-gray-25;
|
||||
@include font-subtitle;
|
||||
@include light-theme {
|
||||
color: $color-gray-60;
|
||||
}
|
||||
@include dark-theme {
|
||||
color: $color-gray-25;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -271,7 +340,12 @@
|
|||
|
||||
&__members_help {
|
||||
@include font-body-2;
|
||||
color: $color-gray-25;
|
||||
@include light-theme {
|
||||
color: $color-gray-60;
|
||||
}
|
||||
@include dark-theme {
|
||||
color: $color-gray-25;
|
||||
}
|
||||
}
|
||||
|
||||
&__remove_group {
|
||||
|
|
|
@ -167,16 +167,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__icon {
|
||||
@mixin StoryListItem__Icon($path) {
|
||||
@include light-theme {
|
||||
@include color-svg($path, $color-black);
|
||||
}
|
||||
@include dark-theme {
|
||||
@include color-svg($path, $color-white);
|
||||
}
|
||||
@mixin StoryListItem__Icon($path) {
|
||||
@include light-theme {
|
||||
@include color-svg($path, $color-black);
|
||||
}
|
||||
@include dark-theme {
|
||||
@include color-svg($path, $color-white);
|
||||
}
|
||||
}
|
||||
|
||||
&__icon {
|
||||
&--chat {
|
||||
@include StoryListItem__Icon('../images/icons/v3/open/open-compact.svg');
|
||||
}
|
||||
|
@ -203,9 +203,8 @@
|
|||
margin-inline-start: 2px;
|
||||
vertical-align: middle;
|
||||
width: 16px;
|
||||
@include color-svg(
|
||||
'../images/icons/v3/chevron/chevron-right-compact-bold.svg',
|
||||
$color-white
|
||||
@include StoryListItem__Icon(
|
||||
'../images/icons/v3/chevron/chevron-right-compact-bold.svg'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ export enum AvatarSize {
|
|||
TWENTY = 20,
|
||||
TWENTY_EIGHT = 28,
|
||||
THIRTY_TWO = 32,
|
||||
THIRTY_SIX = 36,
|
||||
FORTY_EIGHT = 48,
|
||||
FIFTY_TWO = 52,
|
||||
EIGHTY = 80,
|
||||
|
|
|
@ -40,6 +40,9 @@ import { Intl } from './Intl';
|
|||
import { NavSidebarSearchHeader } from './NavSidebar';
|
||||
import { SizeObserver } from '../hooks/useSizeObserver';
|
||||
import { formatCallHistoryGroup } from '../util/callDisposition';
|
||||
import { CallsNewCallButton } from './CallsNewCall';
|
||||
import { Tooltip, TooltipPlacement } from './Tooltip';
|
||||
import { Theme } from '../util/theme';
|
||||
|
||||
function Timestamp({
|
||||
i18n,
|
||||
|
@ -100,6 +103,7 @@ const defaultPendingState: SearchState = {
|
|||
};
|
||||
|
||||
type CallsListProps = Readonly<{
|
||||
hasActiveCall: boolean;
|
||||
getCallHistoryGroupsCount: (
|
||||
options: CallHistoryFilterOptions
|
||||
) => Promise<number>;
|
||||
|
@ -110,6 +114,8 @@ type CallsListProps = Readonly<{
|
|||
getConversation: (id: string) => ConversationType | void;
|
||||
i18n: LocalizerType;
|
||||
selectedCallHistoryGroup: CallHistoryGroup | null;
|
||||
onOutgoingAudioCallInConversation: (conversationId: string) => void;
|
||||
onOutgoingVideoCallInConversation: (conversationId: string) => void;
|
||||
onSelectCallHistoryGroup: (
|
||||
conversationId: string,
|
||||
selectedCallHistoryGroup: CallHistoryGroup
|
||||
|
@ -117,15 +123,18 @@ type CallsListProps = Readonly<{
|
|||
}>;
|
||||
|
||||
function rowHeight() {
|
||||
return ListTile.heightCompact;
|
||||
return ListTile.heightFull;
|
||||
}
|
||||
|
||||
export function CallsList({
|
||||
hasActiveCall,
|
||||
getCallHistoryGroupsCount,
|
||||
getCallHistoryGroups,
|
||||
getConversation,
|
||||
i18n,
|
||||
selectedCallHistoryGroup,
|
||||
onOutgoingAudioCallInConversation,
|
||||
onOutgoingVideoCallInConversation,
|
||||
onSelectCallHistoryGroup,
|
||||
}: CallsListProps): JSX.Element {
|
||||
const infiniteLoaderRef = useRef<InfiniteLoader>(null);
|
||||
|
@ -270,6 +279,7 @@ export function CallsList({
|
|||
title={
|
||||
<span className="CallsList__LoadingText CallsList__LoadingText--title" />
|
||||
}
|
||||
subtitleMaxLines={1}
|
||||
subtitle={
|
||||
<span className="CallsList__LoadingText CallsList__LoadingText--subtitle" />
|
||||
}
|
||||
|
@ -306,6 +316,7 @@ export function CallsList({
|
|||
style={style}
|
||||
className={classNames('CallsList__Item', {
|
||||
'CallsList__Item--selected': isSelected,
|
||||
'CallsList__Item--missed': wasMissed,
|
||||
})}
|
||||
>
|
||||
<ListTile
|
||||
|
@ -320,17 +331,22 @@ export function CallsList({
|
|||
isMe={false}
|
||||
title={conversation.title}
|
||||
sharedGroupNames={[]}
|
||||
size={AvatarSize.THIRTY_TWO}
|
||||
size={AvatarSize.THIRTY_SIX}
|
||||
badge={undefined}
|
||||
className="CallsList__ItemAvatar"
|
||||
/>
|
||||
}
|
||||
trailing={
|
||||
<span
|
||||
className={classNames('CallsList__ItemIcon', {
|
||||
'CallsList__ItemIcon--Phone': item.type === CallType.Audio,
|
||||
'CallsList__ItemIcon--Video': item.type !== CallType.Audio,
|
||||
})}
|
||||
<CallsNewCallButton
|
||||
callType={item.type}
|
||||
hasActiveCall={hasActiveCall}
|
||||
onClick={() => {
|
||||
if (item.type === CallType.Audio) {
|
||||
onOutgoingAudioCallInConversation(conversation.id);
|
||||
} else {
|
||||
onOutgoingVideoCallInConversation(conversation.id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
}
|
||||
title={
|
||||
|
@ -341,12 +357,9 @@ export function CallsList({
|
|||
<UserText text={conversation.title} />
|
||||
</span>
|
||||
}
|
||||
subtitleMaxLines={1}
|
||||
subtitle={
|
||||
<span
|
||||
className={classNames('CallsList__ItemCallInfo', {
|
||||
'CallsList__ItemCallInfo--missed': wasMissed,
|
||||
})}
|
||||
>
|
||||
<span className="CallsList__ItemCallInfo">
|
||||
{item.children.length > 1 ? `(${item.children.length}) ` : ''}
|
||||
{statusText} ·{' '}
|
||||
<Timestamp i18n={i18n} timestamp={item.timestamp} />
|
||||
|
@ -360,10 +373,13 @@ export function CallsList({
|
|||
);
|
||||
},
|
||||
[
|
||||
hasActiveCall,
|
||||
searchState,
|
||||
getConversation,
|
||||
selectedCallHistoryGroup,
|
||||
onSelectCallHistoryGroup,
|
||||
onOutgoingAudioCallInConversation,
|
||||
onOutgoingVideoCallInConversation,
|
||||
i18n,
|
||||
]
|
||||
);
|
||||
|
@ -402,21 +418,28 @@ export function CallsList({
|
|||
onClear={handleSearchInputClear}
|
||||
value={queryInput}
|
||||
/>
|
||||
<button
|
||||
className={classNames('CallsList__ToggleFilterByMissed', {
|
||||
'CallsList__ToggleFilterByMissed--pressed': filteringByMissed,
|
||||
})}
|
||||
type="button"
|
||||
aria-pressed={filteringByMissed}
|
||||
aria-roledescription={i18n(
|
||||
'icu:CallsList__ToggleFilterByMissed__RoleDescription'
|
||||
)}
|
||||
onClick={handleStatusToggle}
|
||||
<Tooltip
|
||||
direction={TooltipPlacement.Bottom}
|
||||
content={i18n('icu:CallsList__ToggleFilterByMissedLabel')}
|
||||
theme={Theme.Dark}
|
||||
delay={600}
|
||||
>
|
||||
<span className="CallsList__ToggleFilterByMissedLabel">
|
||||
{i18n('icu:CallsList__ToggleFilterByMissedLabel')}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className={classNames('CallsList__ToggleFilterByMissed', {
|
||||
'CallsList__ToggleFilterByMissed--pressed': filteringByMissed,
|
||||
})}
|
||||
type="button"
|
||||
aria-pressed={filteringByMissed}
|
||||
aria-roledescription={i18n(
|
||||
'icu:CallsList__ToggleFilterByMissed__RoleDescription'
|
||||
)}
|
||||
onClick={handleStatusToggle}
|
||||
>
|
||||
<span className="CallsList__ToggleFilterByMissedLabel">
|
||||
{i18n('icu:CallsList__ToggleFilterByMissedLabel')}
|
||||
</span>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</NavSidebarSearchHeader>
|
||||
|
||||
{hasEmptyResults && (
|
||||
|
|
|
@ -16,11 +16,11 @@ import { strictAssert } from '../util/assert';
|
|||
import { UserText } from './UserText';
|
||||
import { Avatar, AvatarSize } from './Avatar';
|
||||
import { Intl } from './Intl';
|
||||
import type { ActiveCallStateType } from '../state/ducks/calling';
|
||||
import { SizeObserver } from '../hooks/useSizeObserver';
|
||||
import { CallType } from '../types/CallDisposition';
|
||||
|
||||
type CallsNewCallProps = Readonly<{
|
||||
activeCall: ActiveCallStateType | undefined;
|
||||
hasActiveCall: boolean;
|
||||
allConversations: ReadonlyArray<ConversationType>;
|
||||
i18n: LocalizerType;
|
||||
onSelectConversation: (conversationId: string) => void;
|
||||
|
@ -33,8 +33,39 @@ type Row =
|
|||
| { kind: 'header'; title: string }
|
||||
| { kind: 'conversation'; conversation: ConversationType };
|
||||
|
||||
export function CallsNewCallButton({
|
||||
callType,
|
||||
hasActiveCall,
|
||||
onClick,
|
||||
}: {
|
||||
callType: CallType;
|
||||
hasActiveCall: boolean;
|
||||
onClick: () => void;
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="CallsNewCall__ItemActionButton"
|
||||
aria-disabled={hasActiveCall}
|
||||
onClick={event => {
|
||||
event.stopPropagation();
|
||||
if (!hasActiveCall) {
|
||||
onClick();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{callType === CallType.Audio && (
|
||||
<span className="CallsNewCall__ItemIcon CallsNewCall__ItemIcon--Phone" />
|
||||
)}
|
||||
{callType !== CallType.Audio && (
|
||||
<span className="CallsNewCall__ItemIcon CallsNewCall__ItemIcon--Video" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export function CallsNewCall({
|
||||
activeCall,
|
||||
hasActiveCall,
|
||||
allConversations,
|
||||
i18n,
|
||||
onSelectConversation,
|
||||
|
@ -146,8 +177,6 @@ export function CallsNewCall({
|
|||
);
|
||||
}
|
||||
|
||||
const callButtonsDisabled = activeCall != null;
|
||||
|
||||
return (
|
||||
<div key={key} style={style}>
|
||||
<ListTile
|
||||
|
@ -168,33 +197,22 @@ export function CallsNewCall({
|
|||
trailing={
|
||||
<div className="CallsNewCall__ItemActions">
|
||||
{item.conversation.type === 'direct' && (
|
||||
<button
|
||||
type="button"
|
||||
className="CallsNewCall__ItemActionButton"
|
||||
aria-disabled={callButtonsDisabled}
|
||||
onClick={event => {
|
||||
event.stopPropagation();
|
||||
if (!callButtonsDisabled) {
|
||||
onOutgoingAudioCallInConversation(item.conversation.id);
|
||||
}
|
||||
<CallsNewCallButton
|
||||
callType={CallType.Audio}
|
||||
hasActiveCall={hasActiveCall}
|
||||
onClick={() => {
|
||||
onOutgoingAudioCallInConversation(item.conversation.id);
|
||||
}}
|
||||
>
|
||||
<span className="CallsNewCall__ItemIcon CallsNewCall__ItemIcon--Phone" />
|
||||
</button>
|
||||
/>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
className="CallsNewCall__ItemActionButton"
|
||||
aria-disabled={callButtonsDisabled}
|
||||
onClick={event => {
|
||||
event.stopPropagation();
|
||||
if (!callButtonsDisabled) {
|
||||
onOutgoingVideoCallInConversation(item.conversation.id);
|
||||
}
|
||||
<CallsNewCallButton
|
||||
// It's okay if this is a group
|
||||
callType={CallType.Video}
|
||||
hasActiveCall={hasActiveCall}
|
||||
onClick={() => {
|
||||
onOutgoingVideoCallInConversation(item.conversation.id);
|
||||
}}
|
||||
>
|
||||
<span className="CallsNewCall__ItemIcon CallsNewCall__ItemIcon--Video" />
|
||||
</button>
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
onClick={() => {
|
||||
|
@ -207,7 +225,7 @@ export function CallsNewCall({
|
|||
[
|
||||
rows,
|
||||
i18n,
|
||||
activeCall,
|
||||
hasActiveCall,
|
||||
onSelectConversation,
|
||||
onOutgoingAudioCallInConversation,
|
||||
onOutgoingVideoCallInConversation,
|
||||
|
|
|
@ -198,18 +198,25 @@ export function CallsTab({
|
|||
{sidebarView === CallsTabSidebarView.CallsListView && (
|
||||
<CallsList
|
||||
key={CallsTabSidebarView.CallsListView}
|
||||
hasActiveCall={activeCall != null}
|
||||
getCallHistoryGroupsCount={getCallHistoryGroupsCount}
|
||||
getCallHistoryGroups={getCallHistoryGroups}
|
||||
getConversation={getConversation}
|
||||
i18n={i18n}
|
||||
selectedCallHistoryGroup={selected?.callHistoryGroup ?? null}
|
||||
onSelectCallHistoryGroup={handleSelectCallHistoryGroup}
|
||||
onOutgoingAudioCallInConversation={
|
||||
handleOutgoingAudioCallInConversation
|
||||
}
|
||||
onOutgoingVideoCallInConversation={
|
||||
handleOutgoingVideoCallInConversation
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{sidebarView === CallsTabSidebarView.NewCallView && (
|
||||
<CallsNewCall
|
||||
key={CallsTabSidebarView.NewCallView}
|
||||
activeCall={activeCall}
|
||||
hasActiveCall={activeCall != null}
|
||||
allConversations={allConversations}
|
||||
i18n={i18n}
|
||||
regionCode={regionCode}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2019 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React, { useEffect, useCallback, useMemo } from 'react';
|
||||
import React, { useEffect, useCallback, useMemo, useRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { isNumber } from 'lodash';
|
||||
|
||||
|
@ -26,8 +26,7 @@ import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
|||
import { usePrevious } from '../hooks/usePrevious';
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
import type { DurationInSeconds } from '../util/durations';
|
||||
import type { WidthBreakpoint } from './_util';
|
||||
import { getNavSidebarWidthBreakpoint } from './_util';
|
||||
import { WidthBreakpoint, getNavSidebarWidthBreakpoint } from './_util';
|
||||
import * as KeyboardLayout from '../services/keyboardLayout';
|
||||
import type { LookupConversationWithoutServiceIdActionsType } from '../util/lookupConversationWithoutServiceId';
|
||||
import type { ShowConversationType } from '../state/ducks/conversations';
|
||||
|
@ -42,7 +41,7 @@ import type {
|
|||
ReplaceAvatarActionType,
|
||||
SaveAvatarToDiskActionType,
|
||||
} from '../types/Avatar';
|
||||
import { SizeObserver } from '../hooks/useSizeObserver';
|
||||
import { useSizeObserver } from '../hooks/useSizeObserver';
|
||||
import {
|
||||
NavSidebar,
|
||||
NavSidebarActionButton,
|
||||
|
@ -479,7 +478,12 @@ export function LeftPane({
|
|||
// It also ensures that we scroll to the top when switching views.
|
||||
const listKey = preRowsNode ? 1 : 0;
|
||||
|
||||
const widthBreakpoint = getNavSidebarWidthBreakpoint(300);
|
||||
const measureRef = useRef<HTMLDivElement>(null);
|
||||
const measureSize = useSizeObserver(measureRef);
|
||||
|
||||
const widthBreakpoint = getNavSidebarWidthBreakpoint(
|
||||
measureSize?.width ?? preferredWidthFromStorage
|
||||
);
|
||||
|
||||
const commonDialogProps = {
|
||||
i18n,
|
||||
|
@ -548,7 +552,7 @@ export function LeftPane({
|
|||
navTabsCollapsed={navTabsCollapsed}
|
||||
onToggleNavTabsCollapse={toggleNavTabsCollapse}
|
||||
preferredLeftPaneWidth={preferredWidthFromStorage}
|
||||
requiresFullWidth={false}
|
||||
requiresFullWidth={modeSpecificProps.mode !== LeftPaneMode.Inbox}
|
||||
savePreferredLeftPaneWidth={savePreferredLeftPaneWidth}
|
||||
actions={
|
||||
<>
|
||||
|
@ -603,91 +607,88 @@ export function LeftPane({
|
|||
showChooseGroupMembers,
|
||||
})}
|
||||
</div>
|
||||
<NavSidebarSearchHeader>
|
||||
{helper.getSearchInput({
|
||||
clearConversationSearch,
|
||||
clearSearch,
|
||||
i18n,
|
||||
onChangeComposeSearchTerm: event => {
|
||||
setComposeSearchTerm(event.target.value);
|
||||
},
|
||||
updateSearchTerm,
|
||||
showConversation,
|
||||
})}
|
||||
</NavSidebarSearchHeader>
|
||||
{(widthBreakpoint === WidthBreakpoint.Wide ||
|
||||
modeSpecificProps.mode !== LeftPaneMode.Inbox) && (
|
||||
<NavSidebarSearchHeader>
|
||||
{helper.getSearchInput({
|
||||
clearConversationSearch,
|
||||
clearSearch,
|
||||
i18n,
|
||||
onChangeComposeSearchTerm: event => {
|
||||
setComposeSearchTerm(event.target.value);
|
||||
},
|
||||
updateSearchTerm,
|
||||
showConversation,
|
||||
})}
|
||||
</NavSidebarSearchHeader>
|
||||
)}
|
||||
<div className="module-left-pane__dialogs">
|
||||
{dialogs.map(({ key, dialog }) => (
|
||||
<React.Fragment key={key}>{dialog}</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
{preRowsNode && <React.Fragment key={0}>{preRowsNode}</React.Fragment>}
|
||||
<SizeObserver>
|
||||
{(ref, size) => (
|
||||
<div className="module-left-pane__list--measure" ref={ref}>
|
||||
<div className="module-left-pane__list--wrapper">
|
||||
<div
|
||||
aria-live="polite"
|
||||
className="module-left-pane__list"
|
||||
data-supertab
|
||||
key={listKey}
|
||||
role="presentation"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<ConversationList
|
||||
dimensions={size ?? undefined}
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
getRow={getRow}
|
||||
i18n={i18n}
|
||||
onClickArchiveButton={showArchivedConversations}
|
||||
onClickContactCheckbox={(
|
||||
conversationId: string,
|
||||
disabledReason: undefined | ContactCheckboxDisabledReason
|
||||
) => {
|
||||
switch (disabledReason) {
|
||||
case undefined:
|
||||
toggleConversationInChooseMembers(conversationId);
|
||||
break;
|
||||
case ContactCheckboxDisabledReason.AlreadyAdded:
|
||||
case ContactCheckboxDisabledReason.MaximumContactsSelected:
|
||||
// These are no-ops.
|
||||
break;
|
||||
default:
|
||||
throw missingCaseError(disabledReason);
|
||||
}
|
||||
}}
|
||||
showUserNotFoundModal={showUserNotFoundModal}
|
||||
setIsFetchingUUID={setIsFetchingUUID}
|
||||
lookupConversationWithoutServiceId={
|
||||
lookupConversationWithoutServiceId
|
||||
}
|
||||
showConversation={showConversation}
|
||||
blockConversation={blockConversation}
|
||||
onSelectConversation={onSelectConversation}
|
||||
onOutgoingAudioCallInConversation={
|
||||
onOutgoingAudioCallInConversation
|
||||
}
|
||||
onOutgoingVideoCallInConversation={
|
||||
onOutgoingVideoCallInConversation
|
||||
}
|
||||
removeConversation={
|
||||
isContactManagementEnabled
|
||||
? removeConversation
|
||||
: undefined
|
||||
}
|
||||
renderMessageSearchResult={renderMessageSearchResult}
|
||||
rowCount={helper.getRowCount()}
|
||||
scrollBehavior={scrollBehavior}
|
||||
scrollToRowIndex={rowIndexToScrollTo}
|
||||
scrollable={isScrollable}
|
||||
shouldRecomputeRowHeights={shouldRecomputeRowHeights}
|
||||
showChooseGroupMembers={showChooseGroupMembers}
|
||||
theme={theme}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="module-left-pane__list--measure" ref={measureRef}>
|
||||
<div className="module-left-pane__list--wrapper">
|
||||
<div
|
||||
aria-live="polite"
|
||||
className="module-left-pane__list"
|
||||
data-supertab
|
||||
key={listKey}
|
||||
role="presentation"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<ConversationList
|
||||
dimensions={measureSize ?? undefined}
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
getRow={getRow}
|
||||
i18n={i18n}
|
||||
onClickArchiveButton={showArchivedConversations}
|
||||
onClickContactCheckbox={(
|
||||
conversationId: string,
|
||||
disabledReason: undefined | ContactCheckboxDisabledReason
|
||||
) => {
|
||||
switch (disabledReason) {
|
||||
case undefined:
|
||||
toggleConversationInChooseMembers(conversationId);
|
||||
break;
|
||||
case ContactCheckboxDisabledReason.AlreadyAdded:
|
||||
case ContactCheckboxDisabledReason.MaximumContactsSelected:
|
||||
// These are no-ops.
|
||||
break;
|
||||
default:
|
||||
throw missingCaseError(disabledReason);
|
||||
}
|
||||
}}
|
||||
showUserNotFoundModal={showUserNotFoundModal}
|
||||
setIsFetchingUUID={setIsFetchingUUID}
|
||||
lookupConversationWithoutServiceId={
|
||||
lookupConversationWithoutServiceId
|
||||
}
|
||||
showConversation={showConversation}
|
||||
blockConversation={blockConversation}
|
||||
onSelectConversation={onSelectConversation}
|
||||
onOutgoingAudioCallInConversation={
|
||||
onOutgoingAudioCallInConversation
|
||||
}
|
||||
onOutgoingVideoCallInConversation={
|
||||
onOutgoingVideoCallInConversation
|
||||
}
|
||||
removeConversation={
|
||||
isContactManagementEnabled ? removeConversation : undefined
|
||||
}
|
||||
renderMessageSearchResult={renderMessageSearchResult}
|
||||
rowCount={helper.getRowCount()}
|
||||
scrollBehavior={scrollBehavior}
|
||||
scrollToRowIndex={rowIndexToScrollTo}
|
||||
scrollable={isScrollable}
|
||||
shouldRecomputeRowHeights={shouldRecomputeRowHeights}
|
||||
showChooseGroupMembers={showChooseGroupMembers}
|
||||
theme={theme}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</SizeObserver>
|
||||
</div>
|
||||
</div>
|
||||
{footerContents && (
|
||||
<div className="module-left-pane__footer">{footerContents}</div>
|
||||
)}
|
||||
|
|
|
@ -15,27 +15,38 @@ import { AvatarPopup } from './AvatarPopup';
|
|||
import { handleOutsideClick } from '../util/handleOutsideClick';
|
||||
import type { UnreadStats } from '../state/selectors/conversations';
|
||||
import { NavTab } from '../state/ducks/nav';
|
||||
import { Tooltip, TooltipPlacement } from './Tooltip';
|
||||
import { Theme } from '../util/theme';
|
||||
|
||||
type NavTabProps = Readonly<{
|
||||
i18n: LocalizerType;
|
||||
badge?: ReactNode;
|
||||
iconClassName: string;
|
||||
id: NavTab;
|
||||
label: string;
|
||||
}>;
|
||||
|
||||
function NavTabsItem({ badge, iconClassName, id, label }: NavTabProps) {
|
||||
function NavTabsItem({ i18n, badge, iconClassName, id, label }: NavTabProps) {
|
||||
const isRTL = i18n.getLocaleDirection() === 'rtl';
|
||||
return (
|
||||
<Tab id={id} data-testid={`NavTabsItem--${id}`} className="NavTabs__Item">
|
||||
<span className="NavTabs__ItemLabel">{label}</span>
|
||||
<span className="NavTabs__ItemButton">
|
||||
<span className="NavTabs__ItemContent">
|
||||
<span
|
||||
role="presentation"
|
||||
className={`NavTabs__ItemIcon ${iconClassName}`}
|
||||
/>
|
||||
{badge && <span className="NavTabs__ItemBadge">{badge}</span>}
|
||||
<Tooltip
|
||||
content={label}
|
||||
theme={Theme.Dark}
|
||||
direction={isRTL ? TooltipPlacement.Left : TooltipPlacement.Right}
|
||||
delay={600}
|
||||
>
|
||||
<span className="NavTabs__ItemButton">
|
||||
<span className="NavTabs__ItemContent">
|
||||
<span
|
||||
role="presentation"
|
||||
className={`NavTabs__ItemIcon ${iconClassName}`}
|
||||
/>
|
||||
{badge && <span className="NavTabs__ItemBadge">{badge}</span>}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</Tab>
|
||||
);
|
||||
}
|
||||
|
@ -59,23 +70,30 @@ export function NavTabsToggle({
|
|||
function handleToggle() {
|
||||
onToggleNavTabsCollapse(!navTabsCollapsed);
|
||||
}
|
||||
const label = navTabsCollapsed
|
||||
? i18n('icu:NavTabsToggle__showTabs')
|
||||
: i18n('icu:NavTabsToggle__hideTabs');
|
||||
const isRTL = i18n.getLocaleDirection() === 'rtl';
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="NavTabs__Item NavTabs__Toggle"
|
||||
onClick={handleToggle}
|
||||
>
|
||||
<span className="NavTabs__ItemButton">
|
||||
<span
|
||||
role="presentation"
|
||||
className="NavTabs__ItemIcon NavTabs__ItemIcon--Menu"
|
||||
/>
|
||||
<span className="NavTabs__ItemLabel">
|
||||
{navTabsCollapsed
|
||||
? i18n('icu:NavTabsToggle__showTabs')
|
||||
: i18n('icu:NavTabsToggle__hideTabs')}
|
||||
<Tooltip
|
||||
content={label}
|
||||
theme={Theme.Dark}
|
||||
direction={isRTL ? TooltipPlacement.Left : TooltipPlacement.Right}
|
||||
delay={600}
|
||||
>
|
||||
<span className="NavTabs__ItemButton">
|
||||
<span
|
||||
role="presentation"
|
||||
className="NavTabs__ItemIcon NavTabs__ItemIcon--Menu"
|
||||
/>
|
||||
<span className="NavTabs__ItemLabel">{label}</span>
|
||||
</span>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
@ -127,6 +145,8 @@ export function NavTabs({
|
|||
onNavTabSelected(key as NavTab);
|
||||
}
|
||||
|
||||
const isRTL = i18n.getLocaleDirection() === 'rtl';
|
||||
|
||||
const [targetElement, setTargetElement] = useState<HTMLElement | null>(null);
|
||||
const [popperElement, setPopperElement] = useState<HTMLElement | null>(null);
|
||||
const [portalElement, setPortalElement] = useState<HTMLElement | null>(null);
|
||||
|
@ -202,6 +222,7 @@ export function NavTabs({
|
|||
onSelectionChange={handleSelectionChange}
|
||||
>
|
||||
<NavTabsItem
|
||||
i18n={i18n}
|
||||
id={NavTab.Chats}
|
||||
label="Chats"
|
||||
iconClassName="NavTabs__ItemIcon--Chats"
|
||||
|
@ -226,12 +247,14 @@ export function NavTabs({
|
|||
}
|
||||
/>
|
||||
<NavTabsItem
|
||||
i18n={i18n}
|
||||
id={NavTab.Calls}
|
||||
label="Calls"
|
||||
iconClassName="NavTabs__ItemIcon--Calls"
|
||||
/>
|
||||
{storiesEnabled && (
|
||||
<NavTabsItem
|
||||
i18n={i18n}
|
||||
id={NavTab.Stories}
|
||||
label="Stories"
|
||||
iconClassName="NavTabs__ItemIcon--Stories"
|
||||
|
@ -252,49 +275,63 @@ export function NavTabs({
|
|||
className="NavTabs__Item"
|
||||
onClick={onShowSettings}
|
||||
>
|
||||
<span className="NavTabs__ItemButton">
|
||||
<span
|
||||
role="presentation"
|
||||
className="NavTabs__ItemIcon NavTabs__ItemIcon--Settings"
|
||||
/>
|
||||
<span className="NavTabs__ItemLabel">
|
||||
{i18n('icu:NavTabs__ItemLabel--Settings')}
|
||||
<Tooltip
|
||||
content={i18n('icu:NavTabs__ItemLabel--Settings')}
|
||||
theme={Theme.Dark}
|
||||
direction={TooltipPlacement.Right}
|
||||
delay={600}
|
||||
>
|
||||
<span className="NavTabs__ItemButton">
|
||||
<span
|
||||
role="presentation"
|
||||
className="NavTabs__ItemIcon NavTabs__ItemIcon--Settings"
|
||||
/>
|
||||
<span className="NavTabs__ItemLabel">
|
||||
{i18n('icu:NavTabs__ItemLabel--Settings')}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="NavTabs__Item"
|
||||
className="NavTabs__Item NavTabs__Item--Profile"
|
||||
data-supertab
|
||||
onClick={() => {
|
||||
setShowAvatarPopup(true);
|
||||
}}
|
||||
aria-label={i18n('icu:NavTabs__ItemLabel--Profile')}
|
||||
>
|
||||
<span className="NavTabs__ItemButton" ref={setTargetElement}>
|
||||
<span className="NavTabs__ItemContent">
|
||||
<Avatar
|
||||
acceptedMessageRequest
|
||||
avatarPath={me.avatarPath}
|
||||
badge={badge}
|
||||
className="module-main-header__avatar"
|
||||
color={me.color}
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
isMe
|
||||
phoneNumber={me.phoneNumber}
|
||||
profileName={me.profileName}
|
||||
theme={theme}
|
||||
title={me.title}
|
||||
// `sharedGroupNames` makes no sense for yourself, but
|
||||
// `<Avatar>` needs it to determine blurring.
|
||||
sharedGroupNames={[]}
|
||||
size={AvatarSize.TWENTY_EIGHT}
|
||||
/>
|
||||
{hasPendingUpdate && <div className="NavTabs__AvatarBadge" />}
|
||||
<Tooltip
|
||||
content={i18n('icu:NavTabs__ItemLabel--Profile')}
|
||||
theme={Theme.Dark}
|
||||
direction={isRTL ? TooltipPlacement.Left : TooltipPlacement.Right}
|
||||
delay={600}
|
||||
>
|
||||
<span className="NavTabs__ItemButton" ref={setTargetElement}>
|
||||
<span className="NavTabs__ItemContent">
|
||||
<Avatar
|
||||
acceptedMessageRequest
|
||||
avatarPath={me.avatarPath}
|
||||
badge={badge}
|
||||
className="module-main-header__avatar"
|
||||
color={me.color}
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
isMe
|
||||
phoneNumber={me.phoneNumber}
|
||||
profileName={me.profileName}
|
||||
theme={theme}
|
||||
title={me.title}
|
||||
// `sharedGroupNames` makes no sense for yourself, but
|
||||
// `<Avatar>` needs it to determine blurring.
|
||||
sharedGroupNames={[]}
|
||||
size={AvatarSize.TWENTY_EIGHT}
|
||||
/>
|
||||
{hasPendingUpdate && <div className="NavTabs__AvatarBadge" />}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</Tooltip>
|
||||
</button>
|
||||
{showAvatarPopup &&
|
||||
portalElement != null &&
|
||||
|
|
|
@ -10,6 +10,7 @@ import { filterAndSortConversationsByRecent } from '../util/filterAndSortConvers
|
|||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type { ConversationWithStoriesType } from '../state/selectors/conversations';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { ThemeType } from '../types/Util';
|
||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||
import type { PropsType as StoriesSettingsModalPropsType } from './StoriesSettingsModal';
|
||||
import {
|
||||
|
@ -34,7 +35,6 @@ import { MY_STORY_ID, getStoryDistributionListName } from '../types/Stories';
|
|||
import type { RenderModalPage, ModalPropsType } from './Modal';
|
||||
import { PagedModal, ModalPage } from './Modal';
|
||||
import { StoryDistributionListName } from './StoryDistributionListName';
|
||||
import { Theme } from '../util/theme';
|
||||
import { isNotNil } from '../util/isNotNil';
|
||||
import { StoryImage } from './StoryImage';
|
||||
import type { AttachmentType } from '../types/Attachment';
|
||||
|
@ -42,6 +42,7 @@ import { useConfirmDiscard } from '../hooks/useConfirmDiscard';
|
|||
import { getStoryBackground } from '../util/getStoryBackground';
|
||||
import { makeObjectUrl, revokeObjectUrl } from '../types/VisualAttachment';
|
||||
import { UserText } from './UserText';
|
||||
import { Theme } from '../util/theme';
|
||||
|
||||
export type PropsType = {
|
||||
draftAttachment: AttachmentType;
|
||||
|
@ -70,6 +71,7 @@ export type PropsType = {
|
|||
conversationIds: Array<string>
|
||||
) => unknown;
|
||||
signalConnections: Array<ConversationType>;
|
||||
theme: ThemeType;
|
||||
toggleGroupsForStorySend: (cids: Array<string>) => Promise<void>;
|
||||
mostRecentActiveStoryTimestampByGroupOrDistributionList: Record<
|
||||
string,
|
||||
|
@ -141,6 +143,7 @@ export function SendStoryModal({
|
|||
onViewersUpdated,
|
||||
setMyStoriesToAllSignalConnections,
|
||||
signalConnections,
|
||||
theme,
|
||||
toggleGroupsForStorySend,
|
||||
mostRecentActiveStoryTimestampByGroupOrDistributionList,
|
||||
toggleSignalConnectionsModal,
|
||||
|
@ -402,6 +405,7 @@ export function SendStoryModal({
|
|||
setPage={setPage}
|
||||
setSelectedContacts={setSelectedContacts}
|
||||
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
||||
theme={theme}
|
||||
onBackButtonClick={() =>
|
||||
confirmDiscardIf(selectedContacts.length > 0, () =>
|
||||
setListIdToEdit(undefined)
|
||||
|
@ -485,6 +489,7 @@ export function SendStoryModal({
|
|||
}
|
||||
selectedContacts={selectedContacts}
|
||||
setSelectedContacts={setSelectedContacts}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
} else if (page === Page.ChooseGroups) {
|
||||
|
@ -700,7 +705,7 @@ export function SendStoryModal({
|
|||
placement: 'bottom',
|
||||
strategy: 'absolute',
|
||||
}}
|
||||
theme={Theme.Dark}
|
||||
theme={theme === ThemeType.dark ? Theme.Dark : Theme.Light}
|
||||
>
|
||||
<label
|
||||
className="SendStoryModal__distribution-list__label"
|
||||
|
@ -816,7 +821,7 @@ export function SendStoryModal({
|
|||
placement: 'bottom',
|
||||
strategy: 'absolute',
|
||||
}}
|
||||
theme={Theme.Dark}
|
||||
theme={theme === ThemeType.dark ? Theme.Dark : Theme.Light}
|
||||
>
|
||||
<label
|
||||
className="SendStoryModal__distribution-list__label"
|
||||
|
@ -913,7 +918,7 @@ export function SendStoryModal({
|
|||
placement: 'bottom',
|
||||
strategy: 'absolute',
|
||||
}}
|
||||
theme={Theme.Dark}
|
||||
theme={theme === ThemeType.dark ? Theme.Dark : Theme.Light}
|
||||
>
|
||||
{({ openMenu, onKeyDown, ref, menuNode }) => (
|
||||
<div>
|
||||
|
@ -947,7 +952,7 @@ export function SendStoryModal({
|
|||
{!confirmDiscardModal && (
|
||||
<PagedModal
|
||||
modalName="SendStoryModal"
|
||||
theme={Theme.Dark}
|
||||
theme={theme === ThemeType.dark ? Theme.Dark : Theme.Light}
|
||||
onClose={() => confirmDiscardIf(selectedContacts.length > 0, onClose)}
|
||||
>
|
||||
{modal}
|
||||
|
@ -958,7 +963,7 @@ export function SendStoryModal({
|
|||
body={i18n('icu:SendStoryModal__announcements-only')}
|
||||
i18n={i18n}
|
||||
onClose={() => setHasAnnouncementsOnlyAlert(false)}
|
||||
theme={Theme.Dark}
|
||||
theme={theme === ThemeType.dark ? Theme.Dark : Theme.Light}
|
||||
/>
|
||||
)}
|
||||
{confirmRemoveGroupId && (
|
||||
|
@ -978,7 +983,7 @@ export function SendStoryModal({
|
|||
onClose={() => {
|
||||
setConfirmRemoveGroupId(undefined);
|
||||
}}
|
||||
theme={Theme.Dark}
|
||||
theme={theme === ThemeType.dark ? Theme.Dark : Theme.Light}
|
||||
>
|
||||
{i18n('icu:SendStoryModal__confirm-remove-group')}
|
||||
</ConfirmationDialog>
|
||||
|
@ -1000,7 +1005,7 @@ export function SendStoryModal({
|
|||
onClose={() => {
|
||||
setConfirmDeleteList(undefined);
|
||||
}}
|
||||
theme={Theme.Dark}
|
||||
theme={theme === ThemeType.dark ? Theme.Dark : Theme.Light}
|
||||
>
|
||||
{i18n('icu:StoriesSettings__delete-list--confirm', {
|
||||
name: confirmDeleteList.name,
|
||||
|
|
|
@ -7,7 +7,7 @@ import { noop } from 'lodash';
|
|||
|
||||
import type { ConversationType } from '../state/ducks/conversations';
|
||||
import type { ConversationWithStoriesType } from '../state/selectors/conversations';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { LocalizerType, ThemeType } from '../types/Util';
|
||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||
import type { Row } from './ConversationList';
|
||||
import type { StoryDistributionListWithMembersDataType } from '../types/Stories';
|
||||
|
@ -27,8 +27,6 @@ import { MY_STORY_ID, getStoryDistributionListName } from '../types/Stories';
|
|||
import { PagedModal, ModalPage } from './Modal';
|
||||
import { SearchInput } from './SearchInput';
|
||||
import { StoryDistributionListName } from './StoryDistributionListName';
|
||||
import { Theme } from '../util/theme';
|
||||
import { ThemeType } from '../types/Util';
|
||||
import { filterAndSortConversationsByRecent } from '../util/filterAndSortConversations';
|
||||
import { isNotNil } from '../util/isNotNil';
|
||||
import {
|
||||
|
@ -68,6 +66,7 @@ export type PropsType = {
|
|||
) => unknown;
|
||||
setMyStoriesToAllSignalConnections: () => unknown;
|
||||
storyViewReceiptsEnabled: boolean;
|
||||
theme: ThemeType;
|
||||
toggleSignalConnectionsModal: () => unknown;
|
||||
setStoriesDisabled: (value: boolean) => void;
|
||||
getConversationByUuid: (
|
||||
|
@ -257,6 +256,7 @@ export function StoriesSettingsModal({
|
|||
setMyStoriesToAllSignalConnections,
|
||||
storyViewReceiptsEnabled,
|
||||
toggleSignalConnectionsModal,
|
||||
theme,
|
||||
setStoriesDisabled,
|
||||
getConversationByUuid,
|
||||
}: PropsType): JSX.Element {
|
||||
|
@ -347,6 +347,7 @@ export function StoriesSettingsModal({
|
|||
}}
|
||||
selectedContacts={selectedContacts}
|
||||
setSelectedContacts={setSelectedContacts}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
} else if (listToEdit) {
|
||||
|
@ -364,6 +365,7 @@ export function StoriesSettingsModal({
|
|||
setPage={setPage}
|
||||
setSelectedContacts={setSelectedContacts}
|
||||
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
||||
theme={theme}
|
||||
onBackButtonClick={() => setListToEditId(undefined)}
|
||||
onClose={handleClose}
|
||||
/>
|
||||
|
@ -479,7 +481,6 @@ export function StoriesSettingsModal({
|
|||
<PagedModal
|
||||
modalName="StoriesSettingsModal"
|
||||
moduleClassName="StoriesSettingsModal"
|
||||
theme={Theme.Dark}
|
||||
onClose={() =>
|
||||
confirmDiscardIf(selectedContacts.length > 0, hideStoriesSettings)
|
||||
}
|
||||
|
@ -504,7 +505,6 @@ export function StoriesSettingsModal({
|
|||
onClose={() => {
|
||||
setConfirmDeleteList(undefined);
|
||||
}}
|
||||
theme={Theme.Dark}
|
||||
>
|
||||
{i18n('icu:StoriesSettings__delete-list--confirm', {
|
||||
name: confirmDeleteList.name,
|
||||
|
@ -529,7 +529,6 @@ export function StoriesSettingsModal({
|
|||
onClose={() => {
|
||||
setConfirmRemoveGroup(null);
|
||||
}}
|
||||
theme={Theme.Dark}
|
||||
>
|
||||
{i18n('icu:StoriesSettings__remove_group--confirm', {
|
||||
groupTitle: confirmRemoveGroup.title,
|
||||
|
@ -551,6 +550,7 @@ type DistributionListSettingsModalPropsType = {
|
|||
}) => unknown;
|
||||
setPage: (page: Page) => unknown;
|
||||
setSelectedContacts: (contacts: Array<ConversationType>) => unknown;
|
||||
theme: ThemeType;
|
||||
onBackButtonClick: (() => void) | undefined;
|
||||
onClose: () => void;
|
||||
} & Pick<
|
||||
|
@ -574,6 +574,7 @@ export function DistributionListSettingsModal({
|
|||
setMyStoriesToAllSignalConnections,
|
||||
setPage,
|
||||
setSelectedContacts,
|
||||
theme,
|
||||
toggleSignalConnectionsModal,
|
||||
signalConnectionsCount,
|
||||
}: DistributionListSettingsModalPropsType): JSX.Element {
|
||||
|
@ -680,7 +681,7 @@ export function DistributionListSettingsModal({
|
|||
isMe
|
||||
sharedGroupNames={member.sharedGroupNames}
|
||||
size={AvatarSize.THIRTY_TWO}
|
||||
theme={ThemeType.dark}
|
||||
theme={theme}
|
||||
title={member.title}
|
||||
/>
|
||||
<span className="StoriesSettingsModal__list__title">
|
||||
|
@ -756,7 +757,6 @@ export function DistributionListSettingsModal({
|
|||
onClose={() => {
|
||||
setConfirmRemoveMember(undefined);
|
||||
}}
|
||||
theme={Theme.Dark}
|
||||
title={i18n('icu:StoriesSettings__remove--title', {
|
||||
title: confirmRemoveMember.title,
|
||||
})}
|
||||
|
@ -960,6 +960,7 @@ type EditDistributionListModalPropsType = {
|
|||
selectedContacts: Array<ConversationType>;
|
||||
onClose: () => unknown;
|
||||
setSelectedContacts: (contacts: Array<ConversationType>) => unknown;
|
||||
theme: ThemeType;
|
||||
onBackButtonClick: () => void;
|
||||
} & Pick<PropsType, 'candidateConversations' | 'getPreferredBadge' | 'i18n'>;
|
||||
|
||||
|
@ -973,6 +974,7 @@ export function EditDistributionListModal({
|
|||
onClose,
|
||||
selectedContacts,
|
||||
setSelectedContacts,
|
||||
theme,
|
||||
onBackButtonClick,
|
||||
}: EditDistributionListModalPropsType): JSX.Element {
|
||||
const [storyName, setStoryName] = useState('');
|
||||
|
@ -1090,7 +1092,7 @@ export function EditDistributionListModal({
|
|||
isMe
|
||||
sharedGroupNames={contact.sharedGroupNames}
|
||||
size={AvatarSize.THIRTY_TWO}
|
||||
theme={ThemeType.dark}
|
||||
theme={theme}
|
||||
title={contact.title}
|
||||
/>
|
||||
<span className="StoriesSettingsModal__list__title">
|
||||
|
@ -1222,7 +1224,7 @@ export function EditDistributionListModal({
|
|||
showChooseGroupMembers={shouldNeverBeCalled}
|
||||
showConversation={shouldNeverBeCalled}
|
||||
showUserNotFoundModal={shouldNeverBeCalled}
|
||||
theme={ThemeType.dark}
|
||||
theme={theme}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -11,7 +11,7 @@ import type {
|
|||
} from '../types/Attachment';
|
||||
import type { LinkPreviewSourceType } from '../types/LinkPreview';
|
||||
import type { LinkPreviewType } from '../types/message/LinkPreviews';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { LocalizerType, ThemeType } from '../types/Util';
|
||||
import type { Props as StickerButtonProps } from './stickers/StickerButton';
|
||||
import type { PropsType as SendStoryModalPropsType } from './SendStoryModal';
|
||||
import type { StoryDistributionIdString } from '../types/StoryDistributionId';
|
||||
|
@ -67,6 +67,7 @@ export type PropsType = {
|
|||
props: SmartCompositionTextAreaProps
|
||||
) => JSX.Element;
|
||||
sendStoryModalOpenStateChanged: (isOpen: boolean) => unknown;
|
||||
theme: ThemeType;
|
||||
} & Pick<StickerButtonProps, 'installedPacks' | 'recentStickers'> &
|
||||
Pick<
|
||||
SendStoryModalPropsType,
|
||||
|
@ -134,6 +135,7 @@ export function StoryCreator({
|
|||
setMyStoriesToAllSignalConnections,
|
||||
signalConnections,
|
||||
skinTone,
|
||||
theme,
|
||||
toggleGroupsForStorySend,
|
||||
toggleSignalConnectionsModal,
|
||||
}: PropsType): JSX.Element | null {
|
||||
|
@ -228,6 +230,7 @@ export function StoryCreator({
|
|||
mostRecentActiveStoryTimestampByGroupOrDistributionList={
|
||||
mostRecentActiveStoryTimestampByGroupOrDistributionList
|
||||
}
|
||||
theme={theme}
|
||||
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2020 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import React, { useRef } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { noop } from 'lodash';
|
||||
import { Manager, Reference, Popper } from 'react-popper';
|
||||
|
@ -89,8 +89,12 @@ export type PropsType = {
|
|||
sticky?: boolean;
|
||||
theme?: Theme;
|
||||
wrapperClassName?: string;
|
||||
delay?: number;
|
||||
};
|
||||
|
||||
let GLOBAL_EXIT_TIMER: NodeJS.Timeout | undefined;
|
||||
let GLOBAL_TOOLTIP_DISABLE_DELAY = false;
|
||||
|
||||
export function Tooltip({
|
||||
children,
|
||||
className,
|
||||
|
@ -100,15 +104,56 @@ export function Tooltip({
|
|||
theme,
|
||||
popperModifiers = [],
|
||||
wrapperClassName,
|
||||
delay,
|
||||
}: PropsType): JSX.Element {
|
||||
const [isHovering, setIsHovering] = React.useState(false);
|
||||
const timeoutRef = useRef<NodeJS.Timeout | undefined>();
|
||||
const [active, setActive] = React.useState(false);
|
||||
|
||||
const showTooltip = isHovering || Boolean(sticky);
|
||||
const showTooltip = active || Boolean(sticky);
|
||||
|
||||
const tooltipThemeClassName = theme
|
||||
? `module-tooltip--${themeClassName(theme)}`
|
||||
: undefined;
|
||||
|
||||
function handleHoverChanged(hovering: boolean) {
|
||||
// Don't accept updates that aren't valid anymore
|
||||
clearTimeout(GLOBAL_EXIT_TIMER);
|
||||
clearTimeout(timeoutRef.current);
|
||||
|
||||
// We can skip past all of this if there's no delay
|
||||
if (delay != null) {
|
||||
// If we're now hovering, and delays haven't been disabled globally
|
||||
// we should start the timer to show the tooltip
|
||||
if (hovering && !GLOBAL_TOOLTIP_DISABLE_DELAY) {
|
||||
timeoutRef.current = setTimeout(() => {
|
||||
setActive(true);
|
||||
// Since we have shown a tooltip we can now disable these delays
|
||||
// globally.
|
||||
GLOBAL_TOOLTIP_DISABLE_DELAY = true;
|
||||
}, delay);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hovering) {
|
||||
// If we're not hovering, we should hide the tooltip immediately
|
||||
setActive(false);
|
||||
|
||||
// If we've disabled delays globally, we need to start a timer to undo
|
||||
// that after some time has passed.
|
||||
if (GLOBAL_TOOLTIP_DISABLE_DELAY) {
|
||||
GLOBAL_EXIT_TIMER = setTimeout(() => {
|
||||
GLOBAL_TOOLTIP_DISABLE_DELAY = false;
|
||||
|
||||
// We're always going to use 300 here so that a tooltip with a really
|
||||
// long delay doesn't affect all of the others
|
||||
}, 300);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
setActive(hovering);
|
||||
}
|
||||
|
||||
return (
|
||||
<Manager>
|
||||
<Reference>
|
||||
|
@ -116,7 +161,7 @@ export function Tooltip({
|
|||
<TooltipEventWrapper
|
||||
className={wrapperClassName}
|
||||
ref={ref}
|
||||
onHoverChanged={setIsHovering}
|
||||
onHoverChanged={handleHoverChanged}
|
||||
>
|
||||
{children}
|
||||
</TooltipEventWrapper>
|
||||
|
|
|
@ -462,10 +462,7 @@ export function ConversationDetails({
|
|||
</div>
|
||||
|
||||
{callHistoryGroup && (
|
||||
<PanelSection>
|
||||
<h2 className="ConversationDetails__CallHistoryGroup__header">
|
||||
{formatDate(i18n, callHistoryGroup.timestamp)}
|
||||
</h2>
|
||||
<PanelSection title={formatDate(i18n, callHistoryGroup.timestamp)}>
|
||||
<ol className="ConversationDetails__CallHistoryGroup__List">
|
||||
{callHistoryGroup.children.map(child => {
|
||||
return (
|
||||
|
|
|
@ -157,6 +157,7 @@ import {
|
|||
callHistoryGroupSchema,
|
||||
CallHistoryFilterStatus,
|
||||
callHistoryDetailsSchema,
|
||||
CallDirection,
|
||||
} from '../types/CallDisposition';
|
||||
|
||||
type ConversationRow = Readonly<{
|
||||
|
@ -3347,6 +3348,7 @@ async function getCallHistory(
|
|||
|
||||
const MISSED = sqlConstant(DirectCallStatus.Missed);
|
||||
const DELETED = sqlConstant(DirectCallStatus.Deleted);
|
||||
const INCOMING = sqlConstant(CallDirection.Incoming);
|
||||
const FOUR_HOURS_IN_MS = sqlConstant(4 * 60 * 60 * 1000);
|
||||
|
||||
function getCallHistoryGroupDataSync(
|
||||
|
@ -3405,7 +3407,10 @@ function getCallHistoryGroupDataSync(
|
|||
const filterClause =
|
||||
status === CallHistoryFilterStatus.All
|
||||
? sqlFragment`status IS NOT ${DELETED}`
|
||||
: sqlFragment`status IS ${MISSED} AND status IS NOT ${DELETED}`;
|
||||
: sqlFragment`
|
||||
direction IS ${INCOMING} AND
|
||||
status IS ${MISSED} AND status IS NOT ${DELETED}
|
||||
`;
|
||||
|
||||
const offsetLimit =
|
||||
limit > 0 ? sqlFragment`LIMIT ${limit} OFFSET ${offset}` : sqlFragment``;
|
||||
|
|
|
@ -11,6 +11,8 @@ import type { ToastActionType } from './toast';
|
|||
import { showToast } from './toast';
|
||||
import { ToastType } from '../../types/Toast';
|
||||
import type { CallHistoryDetails } from '../../types/CallDisposition';
|
||||
import * as log from '../../logging/log';
|
||||
import * as Errors from '../../types/errors';
|
||||
|
||||
export type CallHistoryState = ReadonlyDeep<{
|
||||
// This informs the app that underlying call history data has changed.
|
||||
|
@ -19,19 +21,19 @@ export type CallHistoryState = ReadonlyDeep<{
|
|||
}>;
|
||||
|
||||
const CALL_HISTORY_CACHE = 'callHistory/CACHE';
|
||||
const CALL_HISTORY_CLEAR = 'callHistory/CLEAR';
|
||||
const CALL_HISTORY_RESET = 'callHistory/RESET';
|
||||
|
||||
export type CallHistoryCache = ReadonlyDeep<{
|
||||
type: typeof CALL_HISTORY_CACHE;
|
||||
payload: CallHistoryDetails;
|
||||
}>;
|
||||
|
||||
export type CallHistoryClear = ReadonlyDeep<{
|
||||
type: typeof CALL_HISTORY_CLEAR;
|
||||
export type CallHistoryReset = ReadonlyDeep<{
|
||||
type: typeof CALL_HISTORY_RESET;
|
||||
}>;
|
||||
|
||||
export type CallHistoryAction = ReadonlyDeep<
|
||||
CallHistoryCache | CallHistoryClear
|
||||
CallHistoryCache | CallHistoryReset
|
||||
>;
|
||||
|
||||
export function getEmptyState(): CallHistoryState {
|
||||
|
@ -52,12 +54,18 @@ function clearAllCallHistory(): ThunkAction<
|
|||
void,
|
||||
RootStateType,
|
||||
unknown,
|
||||
CallHistoryClear | ToastActionType
|
||||
CallHistoryReset | ToastActionType
|
||||
> {
|
||||
return async dispatch => {
|
||||
await clearCallHistoryDataAndSync();
|
||||
dispatch({ type: CALL_HISTORY_CLEAR });
|
||||
dispatch(showToast({ toastType: ToastType.CallHistoryCleared }));
|
||||
try {
|
||||
await clearCallHistoryDataAndSync();
|
||||
dispatch(showToast({ toastType: ToastType.CallHistoryCleared }));
|
||||
} catch (error) {
|
||||
log.error('Error clearing call history', Errors.toLogFormat(error));
|
||||
} finally {
|
||||
// Just force a reset, even if the clear failed.
|
||||
dispatch({ type: CALL_HISTORY_RESET });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -75,7 +83,7 @@ export function reducer(
|
|||
action: CallHistoryAction
|
||||
): CallHistoryState {
|
||||
switch (action.type) {
|
||||
case CALL_HISTORY_CLEAR:
|
||||
case CALL_HISTORY_RESET:
|
||||
return { ...state, edition: state.edition + 1, callHistoryByCallId: {} };
|
||||
case CALL_HISTORY_CACHE:
|
||||
return {
|
||||
|
|
|
@ -1328,6 +1328,11 @@ export function getPropsForCallHistory(
|
|||
}: GetPropsForCallHistoryOptions
|
||||
): CallingNotificationType {
|
||||
const { callId } = message;
|
||||
if (callId == null && 'callHistoryDetails' in message) {
|
||||
log.error(
|
||||
'getPropsForCallHistory: Found callHistoryDetails, but no callId'
|
||||
);
|
||||
}
|
||||
strictAssert(callId != null, 'getPropsForCallHistory: Missing callId');
|
||||
const callHistory = callHistorySelector(callId);
|
||||
strictAssert(
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
getMe,
|
||||
} from '../selectors/conversations';
|
||||
import { getDistributionListsWithMembers } from '../selectors/storyDistributionLists';
|
||||
import { getIntl } from '../selectors/user';
|
||||
import { getIntl, getTheme } from '../selectors/user';
|
||||
import { getPreferredBadgeSelector } from '../selectors/badges';
|
||||
import { getHasStoryViewReceiptSetting } from '../selectors/items';
|
||||
import { useGlobalModalActions } from '../ducks/globalModals';
|
||||
|
@ -49,6 +49,7 @@ export function SmartStoriesSettingsModal(): JSX.Element | null {
|
|||
const groupStories = useSelector(getGroupStories);
|
||||
|
||||
const getConversationByUuid = useSelector(getConversationByUuidSelector);
|
||||
const theme = useSelector(getTheme);
|
||||
|
||||
return (
|
||||
<StoriesSettingsModal
|
||||
|
@ -70,6 +71,7 @@ export function SmartStoriesSettingsModal(): JSX.Element | null {
|
|||
onViewersUpdated={updateStoryViewers}
|
||||
setMyStoriesToAllSignalConnections={setMyStoriesToAllSignalConnections}
|
||||
storyViewReceiptsEnabled={storyViewReceiptsEnabled}
|
||||
theme={theme}
|
||||
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
||||
setStoriesDisabled={setStoriesDisabled}
|
||||
/>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import type { LocalizerType } from '../../types/Util';
|
||||
import { ThemeType, type LocalizerType } from '../../types/Util';
|
||||
import type { StateType } from '../reducer';
|
||||
import { LinkPreviewSourceType } from '../../types/LinkPreview';
|
||||
import { SmartCompositionTextArea } from './CompositionTextArea';
|
||||
|
@ -140,6 +140,7 @@ export function SmartStoryCreator(): JSX.Element | null {
|
|||
setMyStoriesToAllSignalConnections={setMyStoriesToAllSignalConnections}
|
||||
signalConnections={signalConnections}
|
||||
skinTone={skinTone}
|
||||
theme={ThemeType.dark}
|
||||
toggleGroupsForStorySend={toggleGroupsForStorySend}
|
||||
toggleSignalConnectionsModal={toggleSignalConnectionsModal}
|
||||
/>
|
||||
|
|
|
@ -2334,6 +2334,14 @@
|
|||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2021-07-30T16:57:33.618Z"
|
||||
},
|
||||
{
|
||||
"rule": "React-useRef",
|
||||
"path": "ts/components/LeftPane.tsx",
|
||||
"line": " const measureRef = useRef<HTMLDivElement>(null);",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-08-09T21:48:42.602Z",
|
||||
"reasonDetail": "<optional>"
|
||||
},
|
||||
{
|
||||
"rule": "React-useRef",
|
||||
"path": "ts/components/LeftPaneSearchInput.tsx",
|
||||
|
@ -2395,7 +2403,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/components/Modal.tsx",
|
||||
"line": " const bodyRef = useRef<HTMLDivElement>(null);",
|
||||
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-07-25T21:55:26.191Z",
|
||||
"reasonDetail": "<optional>"
|
||||
},
|
||||
|
@ -2403,7 +2411,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/components/Modal.tsx",
|
||||
"line": " const bodyInnerRef = useRef<HTMLDivElement>(null);",
|
||||
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-07-25T21:55:26.191Z",
|
||||
"reasonDetail": "<optional>"
|
||||
},
|
||||
|
@ -2556,7 +2564,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/components/TextAttachment.tsx",
|
||||
"line": " const ref = useRef<HTMLDivElement>(null);",
|
||||
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-07-25T21:55:26.191Z",
|
||||
"reasonDetail": "<optional>"
|
||||
},
|
||||
|
@ -2588,6 +2596,14 @@
|
|||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2021-07-30T16:57:33.618Z"
|
||||
},
|
||||
{
|
||||
"rule": "React-useRef",
|
||||
"path": "ts/components/Tooltip.tsx",
|
||||
"line": " const timeoutRef = useRef<NodeJS.Timeout | undefined>();",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-08-10T00:23:35.320Z",
|
||||
"reasonDetail": "<optional>"
|
||||
},
|
||||
{
|
||||
"rule": "React-createRef",
|
||||
"path": "ts/components/conversation/ConversationHeader.tsx",
|
||||
|
@ -2806,7 +2822,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/hooks/useSizeObserver.tsx",
|
||||
"line": " const sizeRef = useRef<Size | null>(null);",
|
||||
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-07-25T21:55:26.191Z",
|
||||
"reasonDetail": "<optional>"
|
||||
},
|
||||
|
@ -2814,7 +2830,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/hooks/useSizeObserver.tsx",
|
||||
"line": " const onSizeChangeRef = useRef<SizeChangeHandler | void>(onSizeChange);",
|
||||
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-07-25T21:55:26.191Z",
|
||||
"reasonDetail": "<optional>"
|
||||
},
|
||||
|
@ -2822,7 +2838,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/hooks/useSizeObserver.tsx",
|
||||
"line": " const ref = useRef<any>();",
|
||||
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-07-25T21:55:26.191Z",
|
||||
"reasonDetail": "<optional>"
|
||||
},
|
||||
|
@ -2830,7 +2846,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/hooks/useSizeObserver.tsx",
|
||||
"line": " * const scrollerRef = useRef()",
|
||||
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-07-25T21:55:26.191Z",
|
||||
"reasonDetail": "<optional>"
|
||||
},
|
||||
|
@ -2838,7 +2854,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/hooks/useSizeObserver.tsx",
|
||||
"line": " * const scrollerInnerRef = useRef()",
|
||||
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-07-25T21:55:26.191Z",
|
||||
"reasonDetail": "<optional>"
|
||||
},
|
||||
|
@ -2846,7 +2862,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/hooks/useSizeObserver.tsx",
|
||||
"line": " const scrollRef = useRef<Scroll | null>(null);",
|
||||
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-07-25T21:55:26.191Z",
|
||||
"reasonDetail": "<optional>"
|
||||
},
|
||||
|
@ -2854,7 +2870,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/hooks/useSizeObserver.tsx",
|
||||
"line": " const onScrollChangeRef = useRef<ScrollChangeHandler>(onScrollChange);",
|
||||
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-07-25T21:55:26.191Z",
|
||||
"reasonDetail": "<optional>"
|
||||
},
|
||||
|
@ -2862,7 +2878,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/quill/formatting/menu.tsx",
|
||||
"line": " const fadeOutTimerRef = React.useRef<NodeJS.Timeout | undefined>();",
|
||||
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-08-02T19:01:24.771Z",
|
||||
"reasonDetail": "We need a persistent timer to know when to remove after fade-out"
|
||||
},
|
||||
|
@ -2870,7 +2886,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/quill/formatting/menu.tsx",
|
||||
"line": " const hoverTimerRef = React.useRef<NodeJS.Timeout | undefined>();",
|
||||
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-08-02T19:01:24.771Z",
|
||||
"reasonDetail": "We need a persistent timer to track long-hovers"
|
||||
},
|
||||
|
@ -2930,7 +2946,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/components/CallsList.tsx",
|
||||
"line": " const infiniteLoaderRef = useRef<InfiniteLoader>(null);",
|
||||
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-08-02T00:21:37.858Z",
|
||||
"reasonDetail": "<optional>"
|
||||
},
|
||||
|
@ -2938,7 +2954,7 @@
|
|||
"rule": "React-useRef",
|
||||
"path": "ts/components/CallsList.tsx",
|
||||
"line": " const listRef = useRef<List>(null);",
|
||||
"reasonCategory": "falseMatch|testCode|exampleCode|otherUtilityCode|regexMatchedSafeCode|notExercisedByOurApp|ruleNeeded|usageTrusted",
|
||||
"reasonCategory": "usageTrusted",
|
||||
"updated": "2023-08-02T00:21:37.858Z",
|
||||
"reasonDetail": "<optional>"
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue