mirror of
https://github.com/lux-tel/session-desktop.git
synced 2026-10-30 20:33:24 +00:00
338 lines
11 KiB
TypeScript
338 lines
11 KiB
TypeScript
import { useEffect, useState, Dispatch } from 'react';
|
|
import { useSelector } from 'react-redux';
|
|
import styled from 'styled-components';
|
|
import { getAppDispatch } from '../../../../../state/dispatch';
|
|
import { setDisappearingMessagesByConvoId } from '../../../../../interactions/conversationInteractions';
|
|
import { TimerOptions } from '../../../../../session/disappearing_messages/timerOptions';
|
|
import { DisappearingMessageConversationModeType } from '../../../../../session/disappearing_messages/types';
|
|
|
|
import {
|
|
getSelectedConversationExpirationModes,
|
|
useSelectedConversationDisappearingMode,
|
|
useSelectedConversationKey,
|
|
useSelectedExpireTimer,
|
|
useSelectedIsGroupOrCommunity,
|
|
} from '../../../../../state/selectors/selectedConversation';
|
|
import { Flex } from '../../../../basic/Flex';
|
|
import { SpacerLG, SpacerMD } from '../../../../basic/Text';
|
|
import { StyledScrollContainer } from '../../../../conversation/right-panel/overlay/components';
|
|
import { DisappearingModes } from './DisappearingModes';
|
|
import { TimeOptions } from './TimeOptions';
|
|
import { useConversationSettingsModalIsStandalone } from '../../../../../state/selectors/modal';
|
|
import {
|
|
updateConversationSettingsModal,
|
|
type ConversationSettingsModalState,
|
|
} from '../../../../../state/ducks/modalDialog';
|
|
import { useShowConversationSettingsFor } from '../../../../menuAndSettingsHooks/useShowConversationSettingsFor';
|
|
import { tr } from '../../../../../localization/localeTools';
|
|
import { SessionSpinner } from '../../../../loading';
|
|
import {
|
|
SessionButton,
|
|
SessionButtonColor,
|
|
SessionButtonType,
|
|
} from '../../../../basic/SessionButton';
|
|
import {
|
|
useBackActionForPage,
|
|
useCloseActionFromPage,
|
|
useTitleFromPage,
|
|
} from '../conversationSettingsHooks';
|
|
import {
|
|
ModalBasicHeader,
|
|
ModalActionsContainer,
|
|
SessionWrapperModal,
|
|
WrapperModalWidth,
|
|
} from '../../../../SessionWrapperModal';
|
|
import { ModalBackButton } from '../../../shared/ModalBackButton';
|
|
|
|
const StyledNonAdminDescription = styled.div`
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
margin: 0 var(--margins-lg);
|
|
color: var(--text-secondary-color);
|
|
font-size: var(--font-size-sm);
|
|
text-align: center;
|
|
line-height: 15px;
|
|
`;
|
|
|
|
function loadDefaultTimeValue(
|
|
modeSelected: DisappearingMessageConversationModeType,
|
|
hasOnlyOneMode: boolean
|
|
) {
|
|
// NOTE if there is only 1 disappearing message mode available the default state is that it is turned off
|
|
if (hasOnlyOneMode) {
|
|
return 0;
|
|
}
|
|
|
|
return modeSelected !== 'off'
|
|
? modeSelected === 'deleteAfterSend'
|
|
? TimerOptions.DEFAULT_OPTIONS.DELETE_AFTER_SEND
|
|
: TimerOptions.DEFAULT_OPTIONS.DELETE_AFTER_READ
|
|
: 0;
|
|
}
|
|
|
|
/** if there is only one disappearing message mode and 'off' enabled then we trigger single mode UI */
|
|
function useSingleMode(disappearingModeOptions: Record<string, boolean> | undefined) {
|
|
const singleMode: DisappearingMessageConversationModeType | undefined =
|
|
disappearingModeOptions &&
|
|
disappearingModeOptions.off !== undefined &&
|
|
Object.keys(disappearingModeOptions).length === 2
|
|
? (Object.keys(disappearingModeOptions)[1] as DisappearingMessageConversationModeType)
|
|
: undefined;
|
|
|
|
return { singleMode };
|
|
}
|
|
|
|
// NOTE: [react-compiler] this has to live here for the hook to be identified as static
|
|
function useDisappearingMessagesForConversationModalInternal(
|
|
props: ConversationSettingsModalState
|
|
) {
|
|
const onClose = useCloseActionFromPage(props);
|
|
const title = useTitleFromPage(props?.settingsModalPage);
|
|
const selectedConversationKey = useSelectedConversationKey();
|
|
const disappearingModeOptions = useSelector(getSelectedConversationExpirationModes);
|
|
const isGroup = useSelectedIsGroupOrCommunity();
|
|
const expirationMode = useSelectedConversationDisappearingMode() || 'off';
|
|
const expireTimer = useSelectedExpireTimer();
|
|
const backAction = useBackActionForPage(props);
|
|
const isStandalone = useConversationSettingsModalIsStandalone();
|
|
|
|
const showConvoSettingsCb = useShowConversationSettingsFor(selectedConversationKey);
|
|
|
|
return {
|
|
onClose,
|
|
title,
|
|
selectedConversationKey,
|
|
disappearingModeOptions,
|
|
isGroup,
|
|
expirationMode,
|
|
expireTimer,
|
|
backAction,
|
|
isStandalone,
|
|
showConvoSettingsCb,
|
|
};
|
|
}
|
|
|
|
// NOTE: [react-compiler] this has to live here for the hook to be identified as static
|
|
function useDisappearingMessagesForConversationStateInternal(
|
|
initialExpirationModeSelected: DisappearingMessageConversationModeType,
|
|
expireTimer?: number
|
|
) {
|
|
const [modeSelected, setModeSelected] = useState<DisappearingMessageConversationModeType>(
|
|
initialExpirationModeSelected
|
|
);
|
|
const [timeSelected, setTimeSelected] = useState(expireTimer || 0);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
return {
|
|
modeSelected,
|
|
setModeSelected,
|
|
timeSelected,
|
|
setTimeSelected,
|
|
loading,
|
|
setLoading,
|
|
};
|
|
}
|
|
|
|
// NOTE: [react-compiler] this has to live here for the hook to be identified as static
|
|
function useHandleExpirationTimeChange(
|
|
setTimeSelected: Dispatch<number>,
|
|
modeSelected: DisappearingMessageConversationModeType,
|
|
hasOnlyOneMode: boolean,
|
|
expireTimer?: number
|
|
) {
|
|
useEffect(() => {
|
|
// NOTE loads a time value from the conversation model or the default
|
|
setTimeSelected(
|
|
expireTimer !== undefined && expireTimer > -1
|
|
? expireTimer
|
|
: loadDefaultTimeValue(modeSelected, hasOnlyOneMode)
|
|
);
|
|
}, [expireTimer, hasOnlyOneMode, modeSelected, setTimeSelected]);
|
|
}
|
|
|
|
/**
|
|
* NOTE: [react-compiler] Helper function to handle the async operation with try/catch.
|
|
* This is extracted outside the component to work around the React Compiler limitation:
|
|
* "Support value blocks (conditional, logical, optional chaining, etc) within a try/catch statement"
|
|
*/
|
|
async function setDisappearingMessagesWithErrorHandling(
|
|
convoKey: string,
|
|
mode: DisappearingMessageConversationModeType,
|
|
time: number
|
|
): Promise<{ success: true } | { success: false; error: unknown }> {
|
|
try {
|
|
await setDisappearingMessagesByConvoId(convoKey, mode, time);
|
|
return { success: true };
|
|
} catch (error) {
|
|
return { success: false, error };
|
|
}
|
|
}
|
|
|
|
export const DisappearingMessagesForConversationModal = (props: ConversationSettingsModalState) => {
|
|
const dispatch = getAppDispatch();
|
|
|
|
const {
|
|
onClose,
|
|
title,
|
|
selectedConversationKey,
|
|
disappearingModeOptions,
|
|
isGroup,
|
|
expirationMode,
|
|
expireTimer,
|
|
backAction,
|
|
isStandalone,
|
|
showConvoSettingsCb,
|
|
} = useDisappearingMessagesForConversationModalInternal(props);
|
|
|
|
const { singleMode } = useSingleMode(disappearingModeOptions);
|
|
const hasOnlyOneMode = !!(singleMode && singleMode.length > 0);
|
|
|
|
const { modeSelected, setModeSelected, timeSelected, setTimeSelected, loading, setLoading } =
|
|
useDisappearingMessagesForConversationStateInternal(
|
|
hasOnlyOneMode ? singleMode : expirationMode,
|
|
expireTimer
|
|
);
|
|
|
|
function closeOrBackInPage() {
|
|
if (isStandalone) {
|
|
dispatch(updateConversationSettingsModal(null));
|
|
} else {
|
|
showConvoSettingsCb?.({
|
|
settingsModalPage: 'default',
|
|
});
|
|
}
|
|
}
|
|
|
|
const handleSetMode = async () => {
|
|
if (!selectedConversationKey) {
|
|
return;
|
|
}
|
|
|
|
if (hasOnlyOneMode) {
|
|
if (singleMode) {
|
|
const modeToSet = timeSelected === 0 ? 'off' : singleMode;
|
|
setLoading(true);
|
|
const result = await setDisappearingMessagesWithErrorHandling(
|
|
selectedConversationKey,
|
|
modeToSet,
|
|
timeSelected
|
|
);
|
|
setLoading(false);
|
|
if (result.success) {
|
|
closeOrBackInPage();
|
|
} else {
|
|
throw result.error;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
const result = await setDisappearingMessagesWithErrorHandling(
|
|
selectedConversationKey,
|
|
modeSelected,
|
|
timeSelected
|
|
);
|
|
setLoading(false);
|
|
if (result.success) {
|
|
closeOrBackInPage();
|
|
} else {
|
|
throw result.error;
|
|
}
|
|
};
|
|
|
|
useHandleExpirationTimeChange(setTimeSelected, modeSelected, hasOnlyOneMode, expireTimer);
|
|
|
|
if (!disappearingModeOptions) {
|
|
return null;
|
|
}
|
|
|
|
if (!selectedConversationKey) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<SessionWrapperModal
|
|
modalId="conversationSettingsModal"
|
|
headerChildren={
|
|
<ModalBasicHeader
|
|
title={title}
|
|
bigHeader={true}
|
|
extraLeftButton={backAction ? <ModalBackButton onClick={backAction} /> : undefined}
|
|
/>
|
|
}
|
|
onClose={onClose}
|
|
shouldOverflow={true}
|
|
allowOutsideClick={false}
|
|
topAnchor="5vh"
|
|
$contentMinWidth={WrapperModalWidth.narrow}
|
|
buttonChildren={
|
|
<ModalActionsContainer buttonType={SessionButtonType.Outline}>
|
|
{loading ? (
|
|
<SessionSpinner $loading={true} />
|
|
) : (
|
|
<SessionButton
|
|
buttonColor={SessionButtonColor.PrimaryDark}
|
|
onClick={handleSetMode}
|
|
buttonType={SessionButtonType.Outline}
|
|
disabled={
|
|
singleMode
|
|
? disappearingModeOptions[singleMode]
|
|
: modeSelected
|
|
? disappearingModeOptions[modeSelected]
|
|
: undefined
|
|
}
|
|
dataTestId={'disappear-set-button'}
|
|
>
|
|
{tr('set')}
|
|
</SessionButton>
|
|
)}
|
|
</ModalActionsContainer>
|
|
}
|
|
>
|
|
<StyledScrollContainer style={{ position: 'relative' }}>
|
|
<Flex $container={true} $flexDirection={'column'} $alignItems={'center'}>
|
|
<DisappearingModes
|
|
options={disappearingModeOptions}
|
|
selected={modeSelected}
|
|
setSelected={setModeSelected}
|
|
hasOnlyOneMode={hasOnlyOneMode}
|
|
singleMode={singleMode}
|
|
/>
|
|
{(hasOnlyOneMode || modeSelected !== 'off') && (
|
|
<>
|
|
<TimeOptions
|
|
modeSelected={modeSelected}
|
|
selected={timeSelected}
|
|
setSelected={setTimeSelected}
|
|
hasOnlyOneMode={hasOnlyOneMode}
|
|
disabled={
|
|
singleMode
|
|
? disappearingModeOptions[singleMode]
|
|
: modeSelected
|
|
? disappearingModeOptions[modeSelected]
|
|
: undefined
|
|
}
|
|
/>
|
|
</>
|
|
)}
|
|
|
|
{isGroup && (
|
|
<>
|
|
<SpacerLG />
|
|
{/* We want those to be shown no matter our admin rights in a group. */}
|
|
<StyledNonAdminDescription>
|
|
{tr('disappearingMessagesDescription')}
|
|
<br />
|
|
{tr('disappearingMessagesOnlyAdmins')}
|
|
</StyledNonAdminDescription>
|
|
</>
|
|
)}
|
|
</Flex>
|
|
</StyledScrollContainer>
|
|
<SpacerMD />
|
|
</SessionWrapperModal>
|
|
);
|
|
};
|