Support for announcement-only groups
This commit is contained in:
parent
863ae9ed83
commit
56d5d283bd
43 changed files with 1057 additions and 455 deletions
|
@ -41,6 +41,8 @@ export type PropsDataType = {
|
|||
} & Pick<
|
||||
ConversationType,
|
||||
| 'acceptedMessageRequest'
|
||||
| 'announcementsOnly'
|
||||
| 'areWeAdmin'
|
||||
| 'avatarPath'
|
||||
| 'canChangeTimer'
|
||||
| 'color'
|
||||
|
@ -291,6 +293,8 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
|
||||
private renderOutgoingCallButtons(): ReactNode {
|
||||
const {
|
||||
announcementsOnly,
|
||||
areWeAdmin,
|
||||
i18n,
|
||||
onOutgoingAudioCallInConversation,
|
||||
onOutgoingVideoCallInConversation,
|
||||
|
@ -301,15 +305,18 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
|
||||
const videoButton = (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onOutgoingVideoCallInConversation}
|
||||
aria-label={i18n('makeOutgoingVideoCall')}
|
||||
className={classNames(
|
||||
'module-ConversationHeader__button',
|
||||
'module-ConversationHeader__button--video',
|
||||
showBackButton ? null : 'module-ConversationHeader__button--show'
|
||||
showBackButton ? null : 'module-ConversationHeader__button--show',
|
||||
!showBackButton && announcementsOnly && !areWeAdmin
|
||||
? 'module-ConversationHeader__button--show-disabled'
|
||||
: undefined
|
||||
)}
|
||||
disabled={showBackButton}
|
||||
aria-label={i18n('makeOutgoingVideoCall')}
|
||||
onClick={onOutgoingVideoCallInConversation}
|
||||
type="button"
|
||||
/>
|
||||
);
|
||||
|
||||
|
@ -341,14 +348,14 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
return (
|
||||
<button
|
||||
aria-label={i18n('joinOngoingCall')}
|
||||
type="button"
|
||||
onClick={onOutgoingVideoCallInConversation}
|
||||
className={classNames(
|
||||
'module-ConversationHeader__button',
|
||||
'module-ConversationHeader__button--join-call',
|
||||
showBackButton ? null : 'module-ConversationHeader__button--show'
|
||||
)}
|
||||
disabled={showBackButton}
|
||||
onClick={onOutgoingVideoCallInConversation}
|
||||
type="button"
|
||||
>
|
||||
{isNarrow ? null : i18n('joinOngoingCall')}
|
||||
</button>
|
||||
|
|
|
@ -1383,4 +1383,62 @@ storiesOf('Components/Conversation/GroupV2Change', module)
|
|||
)}
|
||||
</>
|
||||
);
|
||||
})
|
||||
.add('Announcement Group (Change)', () => {
|
||||
return (
|
||||
<>
|
||||
{renderChange({
|
||||
from: OUR_ID,
|
||||
details: [
|
||||
{
|
||||
type: 'announcements-only',
|
||||
announcementsOnly: true,
|
||||
},
|
||||
],
|
||||
})}
|
||||
{renderChange({
|
||||
from: ADMIN_A,
|
||||
details: [
|
||||
{
|
||||
type: 'announcements-only',
|
||||
announcementsOnly: true,
|
||||
},
|
||||
],
|
||||
})}
|
||||
{renderChange({
|
||||
details: [
|
||||
{
|
||||
type: 'announcements-only',
|
||||
announcementsOnly: true,
|
||||
},
|
||||
],
|
||||
})}
|
||||
{renderChange({
|
||||
from: OUR_ID,
|
||||
details: [
|
||||
{
|
||||
type: 'announcements-only',
|
||||
announcementsOnly: false,
|
||||
},
|
||||
],
|
||||
})}
|
||||
{renderChange({
|
||||
from: ADMIN_A,
|
||||
details: [
|
||||
{
|
||||
type: 'announcements-only',
|
||||
announcementsOnly: false,
|
||||
},
|
||||
],
|
||||
})}
|
||||
{renderChange({
|
||||
details: [
|
||||
{
|
||||
type: 'announcements-only',
|
||||
announcementsOnly: false,
|
||||
},
|
||||
],
|
||||
})}
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -139,3 +139,15 @@ story.add('Group Links On', () => {
|
|||
|
||||
return <ConversationDetails {...props} isAdmin />;
|
||||
});
|
||||
|
||||
story.add('Group add with missing capabilities', () => (
|
||||
<ConversationDetails
|
||||
{...createProps()}
|
||||
canEditGroupInfo
|
||||
addMembers={async () => {
|
||||
const error = new Error();
|
||||
error.code = 'E_NO_CAPABILITY';
|
||||
throw error;
|
||||
}}
|
||||
/>
|
||||
));
|
||||
|
|
|
@ -30,6 +30,7 @@ import {
|
|||
import { EditConversationAttributesModal } from './EditConversationAttributesModal';
|
||||
import { RequestState } from './util';
|
||||
import { getCustomColorStyle } from '../../../util/getCustomColorStyle';
|
||||
import { ConfirmationDialog } from '../../ConfirmationDialog';
|
||||
|
||||
enum ModalState {
|
||||
NothingOpen,
|
||||
|
@ -109,6 +110,9 @@ export const ConversationDetails: React.ComponentType<Props> = ({
|
|||
addGroupMembersRequestState,
|
||||
setAddGroupMembersRequestState,
|
||||
] = useState<RequestState>(RequestState.Inactive);
|
||||
const [membersMissingCapability, setMembersMissingCapability] = useState(
|
||||
false
|
||||
);
|
||||
|
||||
if (conversation === undefined) {
|
||||
throw new Error('ConversationDetails rendered without a conversation');
|
||||
|
@ -194,7 +198,12 @@ export const ConversationDetails: React.ComponentType<Props> = ({
|
|||
setModalState(ModalState.NothingOpen);
|
||||
setAddGroupMembersRequestState(RequestState.Inactive);
|
||||
} catch (err) {
|
||||
setAddGroupMembersRequestState(RequestState.InactiveWithError);
|
||||
if (err.code === 'E_NO_CAPABILITY') {
|
||||
setMembersMissingCapability(true);
|
||||
setAddGroupMembersRequestState(RequestState.InactiveWithError);
|
||||
} else {
|
||||
setAddGroupMembersRequestState(RequestState.InactiveWithError);
|
||||
}
|
||||
}
|
||||
}}
|
||||
onClose={() => {
|
||||
|
@ -211,6 +220,16 @@ export const ConversationDetails: React.ComponentType<Props> = ({
|
|||
|
||||
return (
|
||||
<div className="conversation-details-panel">
|
||||
{membersMissingCapability && (
|
||||
<ConfirmationDialog
|
||||
cancelText={i18n('Confirmation--confirm')}
|
||||
i18n={i18n}
|
||||
onClose={() => setMembersMissingCapability(false)}
|
||||
>
|
||||
{i18n('GroupV2--add--missing-capability')}
|
||||
</ConfirmationDialog>
|
||||
)}
|
||||
|
||||
<ConversationDetailsHeader
|
||||
canEdit={canEditGroupInfo}
|
||||
conversation={conversation}
|
||||
|
|
|
@ -27,6 +27,8 @@ const conversation: ConversationType = getDefaultConversation({
|
|||
title: 'Some Conversation',
|
||||
type: 'group',
|
||||
sharedGroupNames: [],
|
||||
announcementsOnlyReady: true,
|
||||
areWeAdmin: true,
|
||||
});
|
||||
|
||||
const createProps = (): PropsType => ({
|
||||
|
@ -36,6 +38,7 @@ const createProps = (): PropsType => ({
|
|||
'setAccessControlAttributesSetting'
|
||||
),
|
||||
setAccessControlMembersSetting: action('setAccessControlMembersSetting'),
|
||||
setAnnouncementsOnly: action('setAnnouncementsOnly'),
|
||||
});
|
||||
|
||||
story.add('Basic', () => {
|
||||
|
|
|
@ -6,6 +6,7 @@ import React from 'react';
|
|||
import { ConversationType } from '../../../state/ducks/conversations';
|
||||
import { LocalizerType } from '../../../types/Util';
|
||||
import { getAccessControlOptions } from '../../../util/getAccessControlOptions';
|
||||
import { SignalService as Proto } from '../../../protobuf';
|
||||
|
||||
import { PanelRow } from './PanelRow';
|
||||
import { PanelSection } from './PanelSection';
|
||||
|
@ -16,14 +17,16 @@ export type PropsType = {
|
|||
i18n: LocalizerType;
|
||||
setAccessControlAttributesSetting: (value: number) => void;
|
||||
setAccessControlMembersSetting: (value: number) => void;
|
||||
setAnnouncementsOnly: (value: boolean) => void;
|
||||
};
|
||||
|
||||
export const GroupV2Permissions: React.ComponentType<PropsType> = ({
|
||||
export const GroupV2Permissions = ({
|
||||
conversation,
|
||||
i18n,
|
||||
setAccessControlAttributesSetting,
|
||||
setAccessControlMembersSetting,
|
||||
}) => {
|
||||
setAnnouncementsOnly,
|
||||
}: PropsType): JSX.Element => {
|
||||
if (conversation === undefined) {
|
||||
throw new Error('GroupV2Permissions rendered without a conversation');
|
||||
}
|
||||
|
@ -34,7 +37,16 @@ export const GroupV2Permissions: React.ComponentType<PropsType> = ({
|
|||
const updateAccessControlMembers = (value: string) => {
|
||||
setAccessControlMembersSetting(Number(value));
|
||||
};
|
||||
const AccessControlEnum = Proto.AccessControl.AccessRequired;
|
||||
const updateAnnouncementsOnly = (value: string) => {
|
||||
setAnnouncementsOnly(Number(value) === AccessControlEnum.ADMINISTRATOR);
|
||||
};
|
||||
const accessControlOptions = getAccessControlOptions(i18n);
|
||||
const announcementsOnlyValue = String(
|
||||
conversation.announcementsOnly
|
||||
? AccessControlEnum.ADMINISTRATOR
|
||||
: AccessControlEnum.MEMBER
|
||||
);
|
||||
|
||||
return (
|
||||
<PanelSection>
|
||||
|
@ -60,6 +72,19 @@ export const GroupV2Permissions: React.ComponentType<PropsType> = ({
|
|||
/>
|
||||
}
|
||||
/>
|
||||
{conversation.areWeAdmin && conversation.announcementsOnlyReady && (
|
||||
<PanelRow
|
||||
label={i18n('ConversationDetails--announcement-label')}
|
||||
info={i18n('ConversationDetails--announcement-info')}
|
||||
right={
|
||||
<Select
|
||||
onChange={updateAnnouncementsOnly}
|
||||
options={accessControlOptions}
|
||||
value={announcementsOnlyValue}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</PanelSection>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue