Clarify behavior for SMS-only conversations

This commit is contained in:
trevor-signal 2025-01-05 15:19:43 -05:00 committed by GitHub
parent 85c74e7e68
commit 9b84402fb0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 42 additions and 57 deletions

View file

@ -36,6 +36,7 @@ export default {
areWePendingApproval: { control: { type: 'boolean' } }, areWePendingApproval: { control: { type: 'boolean' } },
}, },
args: { args: {
acceptedMessageRequest: true,
addAttachment: action('addAttachment'), addAttachment: action('addAttachment'),
conversationId: '123', conversationId: '123',
convertDraftBodyRangesIntoHydrated: () => undefined, convertDraftBodyRangesIntoHydrated: () => undefined,
@ -116,8 +117,7 @@ export default {
groupAdmins: [], groupAdmins: [],
cancelJoinRequest: action('cancelJoinRequest'), cancelJoinRequest: action('cancelJoinRequest'),
showConversation: action('showConversation'), showConversation: action('showConversation'),
// SMS-only isSmsOnlyOrUnregistered: false,
isSMSOnly: false,
isFetchingUUID: false, isFetchingUUID: false,
renderSmartCompositionRecording: _ => <div>RECORDING</div>, renderSmartCompositionRecording: _ => <div>RECORDING</div>,
renderSmartCompositionRecordingDraft: _ => <div>RECORDING DRAFT</div>, renderSmartCompositionRecordingDraft: _ => <div>RECORDING DRAFT</div>,
@ -158,17 +158,26 @@ export function StickerButton(args: Props): JSX.Element {
export function MessageRequest(args: Props): JSX.Element { export function MessageRequest(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext); const theme = useContext(StorybookThemeContext);
return <CompositionArea {...args} theme={theme} />; return (
<CompositionArea {...args} theme={theme} acceptedMessageRequest={false} />
);
} }
export function SmsOnlyFetchingUuid(args: Props): JSX.Element { export function SmsOnlyFetchingUuid(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext); const theme = useContext(StorybookThemeContext);
return <CompositionArea {...args} theme={theme} isSMSOnly isFetchingUUID />; return (
<CompositionArea
{...args}
theme={theme}
isSmsOnlyOrUnregistered
isFetchingUUID
/>
);
} }
export function SmsOnly(args: Props): JSX.Element { export function SmsOnly(args: Props): JSX.Element {
const theme = useContext(StorybookThemeContext); const theme = useContext(StorybookThemeContext);
return <CompositionArea {...args} theme={theme} isSMSOnly />; return <CompositionArea {...args} theme={theme} isSmsOnlyOrUnregistered />;
} }
export function Attachments(args: Props): JSX.Element { export function Attachments(args: Props): JSX.Element {

View file

@ -118,7 +118,7 @@ export type OwnProps = Readonly<{
recordingState: RecordingState; recordingState: RecordingState;
messageCompositionId: string; messageCompositionId: string;
shouldHidePopovers: boolean | null; shouldHidePopovers: boolean | null;
isSMSOnly: boolean | null; isSmsOnlyOrUnregistered: boolean | null;
left: boolean | null; left: boolean | null;
linkPreviewLoading: boolean; linkPreviewLoading: boolean;
linkPreviewResult: LinkPreviewType | null; linkPreviewResult: LinkPreviewType | null;
@ -331,7 +331,7 @@ export const CompositionArea = memo(function CompositionArea({
cancelJoinRequest, cancelJoinRequest,
showConversation, showConversation,
// SMS-only contacts // SMS-only contacts
isSMSOnly, isSmsOnlyOrUnregistered,
isFetchingUUID, isFetchingUUID,
renderSmartCompositionRecording, renderSmartCompositionRecording,
renderSmartCompositionRecordingDraft, renderSmartCompositionRecordingDraft,
@ -800,7 +800,7 @@ export const CompositionArea = memo(function CompositionArea({
); );
} }
if (conversationType === 'direct' && isSMSOnly) { if (conversationType === 'direct' && isSmsOnlyOrUnregistered) {
return ( return (
<div <div
className={classNames([ className={classNames([

View file

@ -211,7 +211,7 @@ export function PrivateConvo(): JSX.Element {
title: 'SMS-only conversation', title: 'SMS-only conversation',
props: { props: {
...commonProps, ...commonProps,
isSMSOnly: true, isSmsOnlyOrUnregistered: true,
conversation: getDefaultConversation({ conversation: getDefaultConversation({
color: getRandomColor(), color: getRandomColor(),
title: '(202) 555-0006', title: '(202) 555-0006',

View file

@ -100,7 +100,7 @@ export type PropsDataType = {
isMissingMandatoryProfileSharing?: boolean; isMissingMandatoryProfileSharing?: boolean;
isSelectMode: boolean; isSelectMode: boolean;
isSignalConversation?: boolean; isSignalConversation?: boolean;
isSMSOnly?: boolean; isSmsOnlyOrUnregistered?: boolean;
outgoingCallButtonStyle: OutgoingCallButtonStyle; outgoingCallButtonStyle: OutgoingCallButtonStyle;
sharedGroupNames: ReadonlyArray<string>; sharedGroupNames: ReadonlyArray<string>;
theme: ThemeType; theme: ThemeType;
@ -159,7 +159,7 @@ export const ConversationHeader = memo(function ConversationHeader({
isMissingMandatoryProfileSharing, isMissingMandatoryProfileSharing,
isSelectMode, isSelectMode,
isSignalConversation, isSignalConversation,
isSMSOnly, isSmsOnlyOrUnregistered,
localDeleteWarningShown, localDeleteWarningShown,
onConversationAccept, onConversationAccept,
onConversationArchive, onConversationArchive,
@ -295,7 +295,7 @@ export const ConversationHeader = memo(function ConversationHeader({
onViewUserStories={onViewUserStories} onViewUserStories={onViewUserStories}
onViewConversationDetails={onViewConversationDetails} onViewConversationDetails={onViewConversationDetails}
/> />
{!isSMSOnly && !isSignalConversation && ( {!isSmsOnlyOrUnregistered && !isSignalConversation && (
<OutgoingCallButtons <OutgoingCallButtons
conversation={conversation} conversation={conversation}
hasActiveCall={hasActiveCall} hasActiveCall={hasActiveCall}

View file

@ -65,6 +65,7 @@ import { useGlobalModalActions } from '../ducks/globalModals';
import { useStickersActions } from '../ducks/stickers'; import { useStickersActions } from '../ducks/stickers';
import { useToastActions } from '../ducks/toast'; import { useToastActions } from '../ducks/toast';
import { isShowingAnyModal } from '../selectors/globalModals'; import { isShowingAnyModal } from '../selectors/globalModals';
import { isConversationEverUnregistered } from '../../util/isConversationUnregistered';
function renderSmartCompositionRecording( function renderSmartCompositionRecording(
recProps: SmartCompositionRecordingProps recProps: SmartCompositionRecordingProps
@ -318,7 +319,10 @@ export const SmartCompositionArea = memo(function SmartCompositionArea({
isBlocked={conversation.isBlocked ?? false} isBlocked={conversation.isBlocked ?? false}
isReported={conversation.isReported ?? false} isReported={conversation.isReported ?? false}
isHidden={conversation.removalStage != null} isHidden={conversation.removalStage != null}
isSMSOnly={Boolean(isConversationSMSOnly(conversation))} isSmsOnlyOrUnregistered={
isConversationSMSOnly(conversation) ||
isConversationEverUnregistered(conversation)
}
isSignalConversation={isSignalConversation(conversation)} isSignalConversation={isSignalConversation(conversation)}
isFetchingUUID={conversation.isFetchingUUID ?? null} isFetchingUUID={conversation.isFetchingUUID ?? null}
isMissingMandatoryProfileSharing={isMissingRequiredProfileSharing( isMissingMandatoryProfileSharing={isMissingRequiredProfileSharing(

View file

@ -43,6 +43,7 @@ import { getIntl, getTheme, getUserACI } from '../selectors/user';
import { useItemsActions } from '../ducks/items'; import { useItemsActions } from '../ducks/items';
import { getLocalDeleteWarningShown } from '../selectors/items'; import { getLocalDeleteWarningShown } from '../selectors/items';
import { getDeleteSyncSendEnabled } from '../selectors/items-extra'; import { getDeleteSyncSendEnabled } from '../selectors/items-extra';
import { isConversationEverUnregistered } from '../../util/isConversationUnregistered';
export type OwnProps = { export type OwnProps = {
id: string; id: string;
@ -275,7 +276,10 @@ export const SmartConversationHeader = memo(function SmartConversationHeader({
isMissingMandatoryProfileSharing={isMissingMandatoryProfileSharing} isMissingMandatoryProfileSharing={isMissingMandatoryProfileSharing}
isSelectMode={isSelectMode} isSelectMode={isSelectMode}
isSignalConversation={isSignalConversation(conversation)} isSignalConversation={isSignalConversation(conversation)}
isSMSOnly={isConversationSMSOnly(conversation)} isSmsOnlyOrUnregistered={
isConversationSMSOnly(conversation) ||
isConversationEverUnregistered(conversation)
}
onConversationAccept={onConversationAccept} onConversationAccept={onConversationAccept}
onConversationArchive={onConversationArchive} onConversationArchive={onConversationArchive}
onConversationBlock={onConversationBlock} onConversationBlock={onConversationBlock}

View file

@ -18,47 +18,11 @@ describe('isConversationSMSOnly', () => {
}); });
['direct', 'private'].forEach(type => { ['direct', 'private'].forEach(type => {
it('returns false if passed an undefined discoveredUnregisteredAt', () => { it(`requires an e164 but no serviceId, type ${type}`, () => {
assert.isFalse(
isConversationSMSOnly({ type, discoveredUnregisteredAt: undefined })
);
});
it('returns true if passed a very old discoveredUnregisteredAt', () => {
assert.isTrue(
isConversationSMSOnly({
type,
e164: 'e164',
serviceId,
discoveredUnregisteredAt: 1,
})
);
});
it(`returns true if passed a time fewer than 6 hours ago and is ${type}`, () => {
assert.isTrue(
isConversationSMSOnly({
type,
e164: 'e164',
serviceId,
discoveredUnregisteredAt: Date.now(),
})
);
const fiveHours = 1000 * 60 * 60 * 5;
assert.isTrue(
isConversationSMSOnly({
type,
e164: 'e164',
serviceId,
discoveredUnregisteredAt: Date.now() - fiveHours,
})
);
});
it(`returns true conversation is ${type} and has no uuid`, () => {
assert.isTrue(isConversationSMSOnly({ type, e164: 'e164' }));
assert.isFalse(isConversationSMSOnly({ type })); assert.isFalse(isConversationSMSOnly({ type }));
assert.isFalse(isConversationSMSOnly({ type, serviceId }));
assert.isFalse(isConversationSMSOnly({ type, e164: 'e164', serviceId }));
assert.isTrue(isConversationSMSOnly({ type, e164: 'e164' }));
}); });
}); });

View file

@ -19,9 +19,13 @@ export function isConversationSMSOnly(
return false; return false;
} }
if (e164 && !serviceId) { if (serviceId) {
return true; return false;
} }
return conversation.discoveredUnregisteredAt !== undefined; if (!e164) {
return false;
}
return true;
} }