Add more buttons to ContactModal
This commit is contained in:
parent
307fb7346d
commit
848ed95bda
6 changed files with 150 additions and 30 deletions
|
@ -43,6 +43,10 @@
|
||||||
"messageformat": "Add “{contact}” to the group “{group}”",
|
"messageformat": "Add “{contact}” to the group “{group}”",
|
||||||
"description": "Shown in the confirmation dialog body when adding a contact to a group"
|
"description": "Shown in the confirmation dialog body when adding a contact to a group"
|
||||||
},
|
},
|
||||||
|
"icu:AddUserToAnotherGroupModal__search-placeholder": {
|
||||||
|
"messageformat": "Search",
|
||||||
|
"description": "Placeholder to use when searching for groups in the AddUserToAnotherGroupModal"
|
||||||
|
},
|
||||||
"icu:AddUserToAnotherGroupModal__toast--user-added-to-group": {
|
"icu:AddUserToAnotherGroupModal__toast--user-added-to-group": {
|
||||||
"messageformat": "{contact} was added to {group}",
|
"messageformat": "{contact} was added to {group}",
|
||||||
"description": "Shown in toast after a user is added to an existing group"
|
"description": "Shown in toast after a user is added to an existing group"
|
||||||
|
@ -356,7 +360,7 @@
|
||||||
"description": "Placeholder of the search input in left pane when looking up someone by phone number"
|
"description": "Placeholder of the search input in left pane when looking up someone by phone number"
|
||||||
},
|
},
|
||||||
"icu:LeftPaneFindByHelper__description--findByUsername": {
|
"icu:LeftPaneFindByHelper__description--findByUsername": {
|
||||||
"messageformat": "Enter a full username with its pair of digits.",
|
"messageformat": "Enter a username followed by a dot and its set of numbers.",
|
||||||
"description": "Description displayed under search input in left pane when looking up someone by username"
|
"description": "Description displayed under search input in left pane when looking up someone by username"
|
||||||
},
|
},
|
||||||
"icu:CountryCodeSelect__placeholder": {
|
"icu:CountryCodeSelect__placeholder": {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 24px;
|
||||||
|
|
||||||
&__name {
|
&__name {
|
||||||
@include button-reset();
|
@include button-reset();
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: baseline;
|
align-items: baseline;
|
||||||
|
|
||||||
margin-top: 6px;
|
margin-top: 12px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,8 +63,8 @@
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding-block: 7px;
|
padding-block: 6px;
|
||||||
padding-inline: 16px;
|
padding-inline: 24px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
&:last-child {
|
&:last-child {
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-inline-end: 10px;
|
margin-inline-end: 12px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +130,19 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__block__bubble-icon {
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
|
||||||
|
@include light-theme {
|
||||||
|
@include color-svg('../images/icons/v3/block/block.svg', $color-gray-75);
|
||||||
|
}
|
||||||
|
|
||||||
|
@include dark-theme {
|
||||||
|
@include color-svg('../images/icons/v3/block/block.svg', $color-gray-15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&__make-admin__bubble-icon {
|
&__make-admin__bubble-icon {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
|
@ -239,4 +252,33 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__quick-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
margin-block: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__divider {
|
||||||
|
// Full width minus margin
|
||||||
|
width: calc(100% - 48px);
|
||||||
|
|
||||||
|
border-style: solid;
|
||||||
|
border-bottom: none;
|
||||||
|
border-width: 1px;
|
||||||
|
|
||||||
|
@include light-theme {
|
||||||
|
border-color: $color-gray-15;
|
||||||
|
}
|
||||||
|
|
||||||
|
@include dark-theme {
|
||||||
|
border-color: $color-gray-75;
|
||||||
|
}
|
||||||
|
|
||||||
|
margin-block: 8px 5px;
|
||||||
|
margin-inline: 24px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,7 +175,9 @@ export function AddUserToAnotherGroupModal({
|
||||||
<div className="AddUserToAnotherGroupModal__main-body">
|
<div className="AddUserToAnotherGroupModal__main-body">
|
||||||
<SearchInput
|
<SearchInput
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
placeholder={i18n('icu:contactSearchPlaceholder')}
|
placeholder={i18n(
|
||||||
|
'icu:AddUserToAnotherGroupModal__search-placeholder'
|
||||||
|
)}
|
||||||
onChange={handleSearchInputChange}
|
onChange={handleSearchInputChange}
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
value={searchTerm}
|
value={searchTerm}
|
||||||
|
|
|
@ -31,17 +31,28 @@ const defaultGroup: ConversationType = getDefaultConversation({
|
||||||
export default {
|
export default {
|
||||||
title: 'Components/Conversation/ContactModal',
|
title: 'Components/Conversation/ContactModal',
|
||||||
component: ContactModal,
|
component: ContactModal,
|
||||||
|
argTypes: {
|
||||||
|
hasActiveCall: { control: { type: 'boolean' } },
|
||||||
|
},
|
||||||
args: {
|
args: {
|
||||||
i18n,
|
i18n,
|
||||||
areWeASubscriber: false,
|
areWeASubscriber: false,
|
||||||
areWeAdmin: false,
|
areWeAdmin: false,
|
||||||
badges: [],
|
badges: [],
|
||||||
|
blockConversation: action('blockConversation'),
|
||||||
contact: defaultContact,
|
contact: defaultContact,
|
||||||
conversation: defaultGroup,
|
conversation: defaultGroup,
|
||||||
|
hasActiveCall: false,
|
||||||
hasStories: undefined,
|
hasStories: undefined,
|
||||||
hideContactModal: action('hideContactModal'),
|
hideContactModal: action('hideContactModal'),
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
isMember: true,
|
isMember: true,
|
||||||
|
onOutgoingAudioCallInConversation: action(
|
||||||
|
'onOutgoingAudioCallInConversation'
|
||||||
|
),
|
||||||
|
onOutgoingVideoCallInConversation: action(
|
||||||
|
'onOutgoingVideoCallInConversation'
|
||||||
|
),
|
||||||
removeMemberFromGroup: action('removeMemberFromGroup'),
|
removeMemberFromGroup: action('removeMemberFromGroup'),
|
||||||
showConversation: action('showConversation'),
|
showConversation: action('showConversation'),
|
||||||
theme: ThemeType.light,
|
theme: ThemeType.light,
|
||||||
|
|
|
@ -14,16 +14,15 @@ import type { LocalizerType, ThemeType } from '../../types/Util';
|
||||||
import type { ViewUserStoriesActionCreatorType } from '../../state/ducks/stories';
|
import type { ViewUserStoriesActionCreatorType } from '../../state/ducks/stories';
|
||||||
import { StoryViewModeType } from '../../types/Stories';
|
import { StoryViewModeType } from '../../types/Stories';
|
||||||
import * as log from '../../logging/log';
|
import * as log from '../../logging/log';
|
||||||
import { About } from './About';
|
|
||||||
import { Avatar, AvatarSize } from '../Avatar';
|
import { Avatar, AvatarSize } from '../Avatar';
|
||||||
import { AvatarLightbox } from '../AvatarLightbox';
|
import { AvatarLightbox } from '../AvatarLightbox';
|
||||||
import { BadgeDialog } from '../BadgeDialog';
|
import { BadgeDialog } from '../BadgeDialog';
|
||||||
import { ConfirmationDialog } from '../ConfirmationDialog';
|
import { ConfirmationDialog } from '../ConfirmationDialog';
|
||||||
import { Modal } from '../Modal';
|
import { Modal } from '../Modal';
|
||||||
import { RemoveGroupMemberConfirmationDialog } from './RemoveGroupMemberConfirmationDialog';
|
import { RemoveGroupMemberConfirmationDialog } from './RemoveGroupMemberConfirmationDialog';
|
||||||
import { SharedGroupNames } from '../SharedGroupNames';
|
|
||||||
import { missingCaseError } from '../../util/missingCaseError';
|
import { missingCaseError } from '../../util/missingCaseError';
|
||||||
import { UserText } from '../UserText';
|
import { UserText } from '../UserText';
|
||||||
|
import { Button, ButtonIconType, ButtonVariant } from '../Button';
|
||||||
|
|
||||||
export type PropsDataType = {
|
export type PropsDataType = {
|
||||||
areWeASubscriber: boolean;
|
areWeASubscriber: boolean;
|
||||||
|
@ -36,10 +35,14 @@ export type PropsDataType = {
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
isMember: boolean;
|
isMember: boolean;
|
||||||
theme: ThemeType;
|
theme: ThemeType;
|
||||||
|
hasActiveCall: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type PropsActionType = {
|
type PropsActionType = {
|
||||||
|
blockConversation: (id: string) => void;
|
||||||
hideContactModal: () => void;
|
hideContactModal: () => void;
|
||||||
|
onOutgoingAudioCallInConversation: (conversationId: string) => unknown;
|
||||||
|
onOutgoingVideoCallInConversation: (conversationId: string) => unknown;
|
||||||
removeMemberFromGroup: (conversationId: string, contactId: string) => void;
|
removeMemberFromGroup: (conversationId: string, contactId: string) => void;
|
||||||
showConversation: ShowConversationType;
|
showConversation: ShowConversationType;
|
||||||
toggleAdmin: (conversationId: string, contactId: string) => void;
|
toggleAdmin: (conversationId: string, contactId: string) => void;
|
||||||
|
@ -62,19 +65,24 @@ enum SubModalState {
|
||||||
None = 'None',
|
None = 'None',
|
||||||
ToggleAdmin = 'ToggleAdmin',
|
ToggleAdmin = 'ToggleAdmin',
|
||||||
MemberRemove = 'MemberRemove',
|
MemberRemove = 'MemberRemove',
|
||||||
|
ConfirmingBlock = 'ConfirmingBlock',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ContactModal({
|
export function ContactModal({
|
||||||
areWeAdmin,
|
areWeAdmin,
|
||||||
areWeASubscriber,
|
areWeASubscriber,
|
||||||
badges,
|
badges,
|
||||||
|
blockConversation,
|
||||||
contact,
|
contact,
|
||||||
conversation,
|
conversation,
|
||||||
|
hasActiveCall,
|
||||||
hasStories,
|
hasStories,
|
||||||
hideContactModal,
|
hideContactModal,
|
||||||
i18n,
|
i18n,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
isMember,
|
isMember,
|
||||||
|
onOutgoingAudioCallInConversation,
|
||||||
|
onOutgoingVideoCallInConversation,
|
||||||
removeMemberFromGroup,
|
removeMemberFromGroup,
|
||||||
showConversation,
|
showConversation,
|
||||||
theme,
|
theme,
|
||||||
|
@ -160,6 +168,27 @@ export function ContactModal({
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case SubModalState.ConfirmingBlock:
|
||||||
|
modalNode = (
|
||||||
|
<ConfirmationDialog
|
||||||
|
dialogName="ContactModal.confirmBlock"
|
||||||
|
actions={[
|
||||||
|
{
|
||||||
|
text: i18n('icu:MessageRequests--block'),
|
||||||
|
action: () => blockConversation(contact.id),
|
||||||
|
style: 'affirmative',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
i18n={i18n}
|
||||||
|
onClose={() => setSubModalState(SubModalState.None)}
|
||||||
|
title={i18n('icu:MessageRequests--block-direct-confirm-title', {
|
||||||
|
title: contact.title,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{i18n('icu:MessageRequests--block-direct-confirm-body')}
|
||||||
|
</ConfirmationDialog>
|
||||||
|
);
|
||||||
|
break;
|
||||||
default: {
|
default: {
|
||||||
const state: never = subModalState;
|
const state: never = subModalState;
|
||||||
log.warn(`ContactModal: unexpected ${state}!`);
|
log.warn(`ContactModal: unexpected ${state}!`);
|
||||||
|
@ -221,31 +250,61 @@ export function ContactModal({
|
||||||
<UserText text={contact.title} />
|
<UserText text={contact.title} />
|
||||||
<i className="ContactModal__name__chevron" />
|
<i className="ContactModal__name__chevron" />
|
||||||
</button>
|
</button>
|
||||||
<div className="module-about__container">
|
|
||||||
<About text={contact.about} />
|
|
||||||
</div>
|
|
||||||
{!contact.isMe && (
|
{!contact.isMe && (
|
||||||
<div className="ContactModal__info">
|
<div className="ContactModal__quick-actions">
|
||||||
<SharedGroupNames
|
<Button
|
||||||
i18n={i18n}
|
icon={ButtonIconType.message}
|
||||||
sharedGroupNames={contact.sharedGroupNames || []}
|
variant={ButtonVariant.Details}
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="ContactModal__button-container">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="ContactModal__button ContactModal__send-message"
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
hideContactModal();
|
hideContactModal();
|
||||||
showConversation({ conversationId: contact.id });
|
showConversation({
|
||||||
|
conversationId: contact?.id,
|
||||||
|
switchToAssociatedView: true,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="ContactModal__bubble-icon">
|
{i18n('icu:ConversationDetails__HeaderButton--Message')}
|
||||||
<div className="ContactModal__send-message__bubble-icon" />
|
</Button>
|
||||||
|
<Button
|
||||||
|
icon={ButtonIconType.video}
|
||||||
|
variant={ButtonVariant.Details}
|
||||||
|
disabled={hasActiveCall}
|
||||||
|
onClick={() => {
|
||||||
|
hideContactModal();
|
||||||
|
onOutgoingVideoCallInConversation(contact.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18n('icu:video')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
icon={ButtonIconType.audio}
|
||||||
|
variant={ButtonVariant.Details}
|
||||||
|
disabled={hasActiveCall}
|
||||||
|
onClick={() => {
|
||||||
|
hideContactModal();
|
||||||
|
onOutgoingAudioCallInConversation(contact.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18n('icu:audio')}
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<span>{i18n('icu:ContactModal--message')}</span>
|
)}
|
||||||
|
<div className="ContactModal__divider" />
|
||||||
|
<div className="ContactModal__button-container">
|
||||||
|
{!contact.isMe && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="ContactModal__button ContactModal__block"
|
||||||
|
onClick={() =>
|
||||||
|
setSubModalState(SubModalState.ConfirmingBlock)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="ContactModal__bubble-icon">
|
||||||
|
<div className="ContactModal__block__bubble-icon" />
|
||||||
|
</div>
|
||||||
|
<span>{i18n('icu:MessageRequests--block')}</span>
|
||||||
</button>
|
</button>
|
||||||
|
)}
|
||||||
{!contact.isMe && (
|
{!contact.isMe && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { getIntl, getTheme } from '../selectors/user';
|
||||||
import { getBadgesSelector } from '../selectors/badges';
|
import { getBadgesSelector } from '../selectors/badges';
|
||||||
import { getConversationSelector } from '../selectors/conversations';
|
import { getConversationSelector } from '../selectors/conversations';
|
||||||
import { getHasStoriesSelector } from '../selectors/stories2';
|
import { getHasStoriesSelector } from '../selectors/stories2';
|
||||||
|
import { getActiveCallState } from '../selectors/calling';
|
||||||
|
|
||||||
const mapStateToProps = (state: StateType): PropsDataType => {
|
const mapStateToProps = (state: StateType): PropsDataType => {
|
||||||
const { contactId, conversationId } =
|
const { contactId, conversationId } =
|
||||||
|
@ -42,6 +43,7 @@ const mapStateToProps = (state: StateType): PropsDataType => {
|
||||||
areWeASubscriber: getAreWeASubscriber(state),
|
areWeASubscriber: getAreWeASubscriber(state),
|
||||||
areWeAdmin,
|
areWeAdmin,
|
||||||
badges: getBadgesSelector(state)(contact.badges),
|
badges: getBadgesSelector(state)(contact.badges),
|
||||||
|
hasActiveCall: Boolean(getActiveCallState(state)),
|
||||||
contact,
|
contact,
|
||||||
conversation: currentConversation,
|
conversation: currentConversation,
|
||||||
hasStories,
|
hasStories,
|
||||||
|
|
Loading…
Reference in a new issue