= {}): Props => ({
contactLabel: overrideProps.contactLabel,
// disableMenu: overrideProps.disableMenu,
deletedForEveryone: overrideProps.deletedForEveryone,
+ deletedForEveryoneByAdmin: overrideProps.deletedForEveryoneByAdmin,
disableScroll: overrideProps.disableScroll,
direction: overrideProps.direction || 'incoming',
showLightboxForViewOnceMedia: action('showLightboxForViewOnceMedia'),
@@ -943,6 +944,23 @@ export function Deleted(): React.JSX.Element {
);
}
+export function DeletedByAdmin(): React.JSX.Element {
+ const props = createProps({
+ conversationType: 'group',
+ deletedForEveryone: true,
+ deletedForEveryoneByAdmin: {
+ conversationId: 'admin-conversation-id',
+ title: 'Alice Smith',
+ contactNameColor: '200',
+ isMe: false,
+ },
+ canForward: false,
+ status: 'sent',
+ });
+
+ return renderBothDirections(props);
+}
+
export const DeletedWithExpireTimer = Template.bind({});
DeletedWithExpireTimer.args = {
timestamp: Date.now() - 60 * 1000,
@@ -955,10 +973,60 @@ DeletedWithExpireTimer.args = {
status: 'sent',
};
+export function DeletedPending(): React.JSX.Element {
+ const props = createProps({
+ timestamp: Date.now() - 60 * 1000,
+ conversationType: 'group',
+ contactNameColor: '100',
+ deletedForEveryone: true,
+ status: 'sending',
+ direction: 'outgoing',
+ });
+
+ return <>{renderThree(props)}>;
+}
+
+export function AdminDeletedPending(): React.JSX.Element {
+ const props = createProps({
+ timestamp: Date.now() - 60 * 1000,
+ conversationType: 'group',
+ contactNameColor: '100',
+ deletedForEveryone: true,
+ deletedForEveryoneByAdmin: {
+ conversationId: 'admin-conversation-id',
+ title: 'Alice Smith',
+ contactNameColor: '200',
+ isMe: false,
+ },
+ status: 'sending',
+ direction: 'outgoing',
+ });
+ const propsIncoming = createProps({
+ timestamp: Date.now() - 60 * 1000,
+ conversationType: 'group',
+ contactNameColor: '100',
+ deletedForEveryone: true,
+ deletedForEveryoneByAdmin: {
+ conversationId: 'admin-conversation-id',
+ title: 'Alice Smith',
+ contactNameColor: '200',
+ isMe: false,
+ },
+ status: 'sending',
+ direction: 'incoming',
+ });
+
+ return (
+ <>
+ {renderThree(props)}
+ {renderThree(propsIncoming)}
+ >
+ );
+}
+
export function DeletedWithError(): React.JSX.Element {
const propsPartialError = createProps({
timestamp: Date.now() - 60 * 1000,
- // canDeleteForEveryone: true,
conversationType: 'group',
contactNameColor: '100',
deletedForEveryone: true,
@@ -967,7 +1035,6 @@ export function DeletedWithError(): React.JSX.Element {
});
const propsError = createProps({
timestamp: Date.now() - 60 * 1000,
- // canDeleteForEveryone: true,
conversationType: 'group',
contactNameColor: '100',
deletedForEveryone: true,
@@ -983,6 +1050,150 @@ export function DeletedWithError(): React.JSX.Element {
);
}
+export function DeletedWithErrorCanRetry(): React.JSX.Element {
+ const propsPartialError = createProps({
+ timestamp: Date.now() - 60 * 1000,
+ conversationType: 'group',
+ contactNameColor: '100',
+ deletedForEveryone: true,
+ canRetryDeleteForEveryone: true,
+ status: 'partial-sent',
+ direction: 'outgoing',
+ });
+ const propsError = createProps({
+ timestamp: Date.now() - 60 * 1000,
+ conversationType: 'group',
+ contactNameColor: '100',
+ deletedForEveryone: true,
+ canRetryDeleteForEveryone: true,
+ status: 'error',
+ direction: 'outgoing',
+ });
+
+ return (
+ <>
+ {renderThree(propsPartialError)}
+ {renderThree(propsError)}
+ >
+ );
+}
+
+export function AdminDeletedWithError(): React.JSX.Element {
+ const adminProps = {
+ deletedForEveryoneByAdmin: {
+ conversationId: 'admin-conversation-id',
+ title: 'Alice Smith',
+ contactNameColor: '200' as const,
+ isMe: false,
+ },
+ };
+ const propsOutgoingPartialError = createProps({
+ timestamp: Date.now() - 60 * 1000,
+ conversationType: 'group',
+ contactNameColor: '100',
+ deletedForEveryone: true,
+ ...adminProps,
+ status: 'partial-sent',
+ direction: 'outgoing',
+ });
+ const propsOutgoingError = createProps({
+ timestamp: Date.now() - 60 * 1000,
+ conversationType: 'group',
+ contactNameColor: '100',
+ deletedForEveryone: true,
+ ...adminProps,
+ status: 'error',
+ direction: 'outgoing',
+ });
+ const propsIncomingPartialError = createProps({
+ timestamp: Date.now() - 60 * 1000,
+ conversationType: 'group',
+ contactNameColor: '100',
+ deletedForEveryone: true,
+ ...adminProps,
+ status: 'partial-sent',
+ direction: 'incoming',
+ });
+ const propsIncomingError = createProps({
+ timestamp: Date.now() - 60 * 1000,
+ conversationType: 'group',
+ contactNameColor: '100',
+ deletedForEveryone: true,
+ ...adminProps,
+ status: 'error',
+ direction: 'incoming',
+ });
+
+ return (
+ <>
+ {renderThree(propsOutgoingPartialError)}
+ {renderThree(propsOutgoingError)}
+ {renderThree(propsIncomingPartialError)}
+ {renderThree(propsIncomingError)}
+ >
+ );
+}
+
+export function AdminDeletedWithErrorCanRetry(): React.JSX.Element {
+ const adminProps = {
+ deletedForEveryoneByAdmin: {
+ conversationId: 'admin-conversation-id',
+ title: 'Alice Smith',
+ contactNameColor: '200' as const,
+ isMe: false,
+ },
+ };
+ const propsOutgoingPartialError = createProps({
+ timestamp: Date.now() - 60 * 1000,
+ conversationType: 'group',
+ contactNameColor: '100',
+ deletedForEveryone: true,
+ canRetryDeleteForEveryone: true,
+ ...adminProps,
+ status: 'partial-sent',
+ direction: 'outgoing',
+ });
+ const propsOutgoingError = createProps({
+ timestamp: Date.now() - 60 * 1000,
+ conversationType: 'group',
+ contactNameColor: '100',
+ deletedForEveryone: true,
+ canRetryDeleteForEveryone: true,
+ ...adminProps,
+ status: 'error',
+ direction: 'outgoing',
+ });
+ const propsIncomingPartialError = createProps({
+ timestamp: Date.now() - 60 * 1000,
+ conversationType: 'group',
+ contactNameColor: '100',
+ deletedForEveryone: true,
+ canRetryDeleteForEveryone: true,
+ ...adminProps,
+ status: 'partial-sent',
+ direction: 'incoming',
+ });
+ const propsIncomingError = createProps({
+ timestamp: Date.now() - 60 * 1000,
+ conversationType: 'group',
+ contactNameColor: '100',
+ deletedForEveryone: true,
+ canRetryDeleteForEveryone: true,
+ ...adminProps,
+ status: 'error',
+ direction: 'incoming',
+ });
+
+ return (
+ <>
+ {renderThree(propsOutgoingPartialError)}
+ {renderThree(propsOutgoingError)}
+ {renderThree(propsIncomingPartialError)}
+ {renderThree(propsIncomingError)}
+ >
+ );
+}
+
export const CanDeleteForEveryone = Template.bind({});
CanDeleteForEveryone.args = {
status: 'read',
diff --git a/ts/components/conversation/TypingBubble.dom.tsx b/ts/components/conversation/TypingBubble.dom.tsx
index b97d374034..cb752e6e24 100644
--- a/ts/components/conversation/TypingBubble.dom.tsx
+++ b/ts/components/conversation/TypingBubble.dom.tsx
@@ -14,6 +14,7 @@ import type { ConversationType } from '../../state/ducks/conversations.preload.j
import type { PreferredBadgeSelectorType } from '../../state/selectors/badges.preload.js';
import { drop } from '../../util/drop.std.js';
import { useReducedMotion } from '../../hooks/useReducedMotion.dom.js';
+import type { ContactModalStateType } from '../../types/globalModals.std.js';
const MAX_AVATARS_COUNT = 3;
@@ -38,7 +39,7 @@ export type TypingBubblePropsType = {
lastItemTimestamp: number | undefined;
getConversation: (id: string) => ConversationType;
getPreferredBadge: PreferredBadgeSelectorType;
- showContactModal: (contactId: string, conversationId?: string) => void;
+ showContactModal: (payload: ContactModalStateType) => void;
i18n: LocalizerType;
theme: ThemeType;
};
@@ -83,7 +84,7 @@ function TypingBubbleAvatar({
shouldAnimate: boolean;
getPreferredBadge: PreferredBadgeSelectorType;
onContactExit: (id: string | undefined) => void;
- showContactModal: (contactId: string, conversationId?: string) => void;
+ showContactModal: (payload: ContactModalStateType) => void;
i18n: LocalizerType;
theme: ThemeType;
}): ReactElement | null {
@@ -130,7 +131,7 @@ function TypingBubbleAvatar({
onClick={event => {
event.stopPropagation();
event.preventDefault();
- showContactModal(contact.id, conversationId);
+ showContactModal({ contactId: contact.id, conversationId });
}}
phoneNumber={contact.phoneNumber}
profileName={contact.profileName}
diff --git a/ts/components/conversation/conversation-details/ConversationDetails.dom.tsx b/ts/components/conversation/conversation-details/ConversationDetails.dom.tsx
index b4c71768ee..cc8fbe3260 100644
--- a/ts/components/conversation/conversation-details/ConversationDetails.dom.tsx
+++ b/ts/components/conversation/conversation-details/ConversationDetails.dom.tsx
@@ -65,7 +65,7 @@ import {
getTooltipContent,
} from '../InAnotherCallTooltip.dom.js';
import { BadgeSustainerInstructionsDialog } from '../../BadgeSustainerInstructionsDialog.dom.js';
-import type { ContactModalStateType } from '../../../state/ducks/globalModals.preload.js';
+import type { ContactModalStateType } from '../../../types/globalModals.std.js';
import type { ShowToastAction } from '../../../state/ducks/toast.preload.js';
import { ToastType } from '../../../types/Toast.dom.js';
@@ -142,7 +142,7 @@ type ActionProps = {
searchInConversation: (id: string) => unknown;
setDisappearingMessages: (id: string, seconds: DurationInSeconds) => void;
setMuteExpiration: (id: string, muteExpiresAt: undefined | number) => unknown;
- showContactModal: (contactId: string, conversationId?: string) => void;
+ showContactModal: (payload: ContactModalStateType) => void;
showConversation: ShowConversationType;
toggleAboutContactModal: (options: ContactModalStateType) => void;
toggleAddUserToAnotherGroupModal: (contactId?: string) => void;
diff --git a/ts/components/conversation/conversation-details/ConversationDetailsHeader.dom.tsx b/ts/components/conversation/conversation-details/ConversationDetailsHeader.dom.tsx
index 2d7be87067..f9bab8b3e5 100644
--- a/ts/components/conversation/conversation-details/ConversationDetailsHeader.dom.tsx
+++ b/ts/components/conversation/conversation-details/ConversationDetailsHeader.dom.tsx
@@ -16,7 +16,7 @@ import type { BadgeType } from '../../../badges/types.std.js';
import { UserText } from '../../UserText.dom.js';
import { isInSystemContacts } from '../../../util/isInSystemContacts.std.js';
import { InContactsIcon } from '../../InContactsIcon.dom.js';
-import type { ContactModalStateType } from '../../../state/ducks/globalModals.preload.js';
+import type { ContactModalStateType } from '../../../types/globalModals.std.js';
export type Props = {
areWeASubscriber: boolean;
diff --git a/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.dom.tsx b/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.dom.tsx
index c008a35da4..6b1789a6dd 100644
--- a/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.dom.tsx
+++ b/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.dom.tsx
@@ -18,6 +18,7 @@ import { PanelRow } from './PanelRow.dom.js';
import { PanelSection } from './PanelSection.dom.js';
import { GroupMemberLabel } from '../ContactName.dom.js';
import { AriaClickable } from '../../../axo/AriaClickable.dom.js';
+import type { ContactModalStateType } from '../../../types/globalModals.std.js';
export type GroupV2Membership = {
isAdmin: boolean;
@@ -36,7 +37,7 @@ export type Props = {
maxShownMemberCount?: number;
memberships: ReadonlyArray
;
memberColors: Map;
- showContactModal: (contactId: string, conversationId?: string) => void;
+ showContactModal: (payload: ContactModalStateType) => void;
showLabelEditor: () => void;
startAddingNewMembers?: () => void;
theme: ThemeType;
@@ -129,7 +130,9 @@ export function ConversationDetailsMembershipList({
return (
showContactModal(member.id, conversationId)}
+ onClick={() =>
+ showContactModal({ contactId: member.id, conversationId })
+ }
icon={
(null);
@@ -134,17 +131,6 @@ export function GroupMemberLabelEditor({
? { labelEmoji, labelString: labelStringForSave }
: undefined;
- useEffect(() => {
- if (
- !group.areWeAdmin &&
- group.accessControlAttributes ===
- Proto.AccessControl.AccessRequired.ADMINISTRATOR &&
- !isShowingPermissionsError
- ) {
- setIsShowingPermissionsError(true);
- }
- }, [group, isShowingPermissionsError, setIsShowingPermissionsError]);
-
const tryClose = React.useRef<() => void | undefined>();
const [confirmDiscardModal, confirmDiscardIf] = useConfirmDiscard({
i18n,
@@ -245,6 +231,8 @@ export function GroupMemberLabelEditor({
previews={[]}
isPinned={false}
canDeleteForEveryone={false}
+ canRetryDeleteForEveryone={false}
+ retryDeleteForEveryone={noop}
isBlocked={false}
isMessageRequestAccepted={false}
containerElementRef={messageContainer}
@@ -438,38 +426,6 @@ export function GroupMemberLabelEditor({
- {
- if (!value) {
- setIsShowingPermissionsError(false);
- popPanelForConversation();
- }
- }}
- >
-
-
-
- {i18n('icu:ConversationDetails--member-label--error-title')}
-
-
- {i18n('icu:ConversationDetails--member-label--error-permissions')}
-
-
-
- {
- popPanelForConversation();
- setIsShowingPermissionsError(false);
- }}
- >
- {i18n('icu:ok')}
-
-
-
-
);
}
diff --git a/ts/components/conversation/conversation-details/GroupV2Permissions.dom.tsx b/ts/components/conversation/conversation-details/GroupV2Permissions.dom.tsx
index 0990756a40..b4507ef150 100644
--- a/ts/components/conversation/conversation-details/GroupV2Permissions.dom.tsx
+++ b/ts/components/conversation/conversation-details/GroupV2Permissions.dom.tsx
@@ -8,7 +8,6 @@ import { SignalService as Proto } from '../../../protobuf/index.std.js';
import { PanelRow } from './PanelRow.dom.js';
import { PanelSection } from './PanelSection.dom.js';
import { Select } from '../../Select.dom.js';
-import { AxoAlertDialog } from '../../../axo/AxoAlertDialog.dom.js';
export type PropsDataType = {
conversation?: ConversationType;
@@ -32,8 +31,6 @@ export function GroupV2Permissions({
}: PropsType): React.JSX.Element {
const AccessControlEnum = Proto.AccessControl.AccessRequired;
- const [isWarningAboutClearingLabels, setIsWarningAboutClearingLabels] =
- React.useState(false);
const addMembersSelectId = useId();
const groupInfoSelectId = useId();
const announcementSelectId = useId();
@@ -41,17 +38,8 @@ export function GroupV2Permissions({
if (conversation === undefined) {
throw new Error('GroupV2Permissions rendered without a conversation');
}
- const nonAdminsHaveLabels = conversation.memberships?.some(
- membership => !membership.isAdmin && membership.labelString
- );
const updateAccessControlAttributes = (value: string) => {
- const newValue = Number(value);
- if (newValue === AccessControlEnum.ADMINISTRATOR && nonAdminsHaveLabels) {
- setIsWarningAboutClearingLabels(true);
- return;
- }
-
setAccessControlAttributesSetting(conversation.id, Number(value));
};
const updateAccessControlMembers = (value: string) => {
@@ -126,51 +114,6 @@ export function GroupV2Permissions({
}
/>
)}
-