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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -18,47 +18,11 @@ describe('isConversationSMSOnly', () => {
});
['direct', 'private'].forEach(type => {
it('returns false if passed an undefined discoveredUnregisteredAt', () => {
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' }));
it(`requires an e164 but no serviceId, type ${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;
}
if (e164 && !serviceId) {
return true;
if (serviceId) {
return false;
}
return conversation.discoveredUnregisteredAt !== undefined;
if (!e164) {
return false;
}
return true;
}