UserStatus: Add button to change privacy or buy premium to see user status (#4346)
This commit is contained in:
parent
c96d05bd9f
commit
70dfa2e160
BIN
src/assets/tgs/LastSeen.tgs
Normal file
BIN
src/assets/tgs/LastSeen.tgs
Normal file
Binary file not shown.
@ -30,7 +30,7 @@ export { default as PinMessageModal } from '../components/common/PinMessageModal
|
||||
export { default as UnpinAllMessagesModal } from '../components/common/UnpinAllMessagesModal';
|
||||
export { default as MessageSelectToolbar } from '../components/middle/MessageSelectToolbar';
|
||||
export { default as SeenByModal } from '../components/common/SeenByModal';
|
||||
export { default as ReadTimeModal } from '../components/common/ReadDateModal';
|
||||
export { default as PrivacySettingsNoticeModal } from '../components/common/PrivacySettingsNoticeModal';
|
||||
export { default as ReactorListModal } from '../components/middle/ReactorListModal';
|
||||
export { default as EmojiInteractionAnimation } from '../components/middle/EmojiInteractionAnimation';
|
||||
export { default as ChatLanguageModal } from '../components/middle/ChatLanguageModal';
|
||||
|
||||
18
src/components/common/PrivacySettingsNoticeModal.async.tsx
Normal file
18
src/components/common/PrivacySettingsNoticeModal.async.tsx
Normal file
@ -0,0 +1,18 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React from '../../lib/teact/teact';
|
||||
|
||||
import type { OwnProps } from './PrivacySettingsNoticeModal';
|
||||
|
||||
import { Bundles } from '../../util/moduleLoader';
|
||||
|
||||
import useModuleLoader from '../../hooks/useModuleLoader';
|
||||
|
||||
const PrivacySettingsNoticeModalAsync: FC<OwnProps> = (props) => {
|
||||
const { isOpen } = props;
|
||||
const PrivacySettingsNoticeModal = useModuleLoader(Bundles.Extra, 'PrivacySettingsNoticeModal', !isOpen);
|
||||
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
return PrivacySettingsNoticeModal ? <PrivacySettingsNoticeModal {...props} /> : undefined;
|
||||
};
|
||||
|
||||
export default PrivacySettingsNoticeModalAsync;
|
||||
@ -18,7 +18,7 @@ import Separator from '../ui/Separator';
|
||||
import AnimatedIconWithPreview from './AnimatedIconWithPreview';
|
||||
import Icon from './Icon';
|
||||
|
||||
import styles from './ReadDateModal.module.scss';
|
||||
import styles from './PrivacySettingsNoticeModal.module.scss';
|
||||
|
||||
export type OwnProps = {
|
||||
isOpen: boolean;
|
||||
@ -26,28 +26,47 @@ export type OwnProps = {
|
||||
|
||||
type StateProps = {
|
||||
user?: ApiUser;
|
||||
isReadDate?: boolean;
|
||||
};
|
||||
|
||||
const CLOSE_ANIMATION_DURATION = ANIMATION_DURATION + ANIMATION_END_DELAY;
|
||||
|
||||
const ReadDateModal = ({ isOpen, user }: OwnProps & StateProps) => {
|
||||
const PrivacySettingsNoticeModal = ({ isOpen, isReadDate, user }: OwnProps & StateProps) => {
|
||||
const lang = useLang();
|
||||
const {
|
||||
updateGlobalPrivacySettings, openPremiumModal, closeGetReadDateModal, showNotification,
|
||||
updateGlobalPrivacySettings,
|
||||
openPremiumModal,
|
||||
closePrivacySettingsNoticeModal,
|
||||
showNotification,
|
||||
setPrivacyVisibility,
|
||||
loadUser,
|
||||
} = getActions();
|
||||
const userName = getUserFirstOrLastName(user);
|
||||
|
||||
const handleShowReadTime = useLastCallback(() => {
|
||||
updateGlobalPrivacySettings({ shouldHideReadMarks: false });
|
||||
closeGetReadDateModal();
|
||||
closePrivacySettingsNoticeModal();
|
||||
|
||||
setTimeout(() => {
|
||||
showNotification({ message: lang('PremiumReadSet') });
|
||||
}, CLOSE_ANIMATION_DURATION);
|
||||
});
|
||||
|
||||
const handleShowLastSeen = useLastCallback(() => {
|
||||
setPrivacyVisibility({
|
||||
privacyKey: 'lastSeen',
|
||||
visibility: 'everybody',
|
||||
onSuccess: () => loadUser({ userId: user!.id }),
|
||||
});
|
||||
closePrivacySettingsNoticeModal();
|
||||
|
||||
setTimeout(() => {
|
||||
showNotification({ message: lang('PremiumLastSeenSet') });
|
||||
}, CLOSE_ANIMATION_DURATION);
|
||||
});
|
||||
|
||||
const handleOpenPremium = useLastCallback(() => {
|
||||
closeGetReadDateModal();
|
||||
closePrivacySettingsNoticeModal();
|
||||
|
||||
setTimeout(() => {
|
||||
openPremiumModal();
|
||||
@ -55,7 +74,7 @@ const ReadDateModal = ({ isOpen, user }: OwnProps & StateProps) => {
|
||||
});
|
||||
|
||||
const handleClose = useLastCallback(() => {
|
||||
closeGetReadDateModal();
|
||||
closePrivacySettingsNoticeModal();
|
||||
});
|
||||
|
||||
return (
|
||||
@ -72,25 +91,45 @@ const ReadDateModal = ({ isOpen, user }: OwnProps & StateProps) => {
|
||||
<Icon name="close" />
|
||||
</Button>
|
||||
<AnimatedIconWithPreview
|
||||
tgsUrl={LOCAL_TGS_URLS.ReadTime}
|
||||
tgsUrl={isReadDate ? LOCAL_TGS_URLS.ReadTime : LOCAL_TGS_URLS.LastSeen}
|
||||
size={84}
|
||||
className={styles.icon}
|
||||
nonInteractive
|
||||
noLoop
|
||||
/>
|
||||
<h2 className={styles.header}>{lang('PremiumReadHeader1')}</h2>
|
||||
<p className={styles.desc}>{renderText(lang('PremiumReadText1', userName), ['simple_markdown'])}</p>
|
||||
<h2 className={styles.header}>
|
||||
{lang(isReadDate ? 'PremiumReadHeader1' : 'PremiumLastSeenHeader1')}
|
||||
</h2>
|
||||
<p className={styles.desc}>
|
||||
{renderText(
|
||||
lang(
|
||||
isReadDate ? 'PremiumReadText1' : 'PremiumLastSeenText1Locked',
|
||||
userName,
|
||||
),
|
||||
['simple_markdown'],
|
||||
)}
|
||||
</p>
|
||||
<Button
|
||||
size="smaller"
|
||||
onClick={handleShowReadTime}
|
||||
onClick={isReadDate ? handleShowReadTime : handleShowLastSeen}
|
||||
className={styles.button}
|
||||
>
|
||||
{lang('PremiumReadButton1')}
|
||||
{lang(isReadDate ? 'PremiumReadButton1' : 'PremiumLastSeenButton1')}
|
||||
</Button>
|
||||
<Separator className={styles.separator}>{lang('PremiumOr')}</Separator>
|
||||
<h2 className={styles.header}>{lang('PremiumReadHeader2')}</h2>
|
||||
<p className={styles.desc}>{renderText(lang('PremiumReadText2', userName), ['simple_markdown'])}</p>
|
||||
<Button withPremiumGradient size="smaller" onClick={handleOpenPremium} className={styles.button}>
|
||||
<p className={styles.desc}>
|
||||
{renderText(
|
||||
lang(isReadDate ? 'PremiumReadText2' : 'PremiumLastSeenText2', userName),
|
||||
['simple_markdown'],
|
||||
)}
|
||||
</p>
|
||||
<Button
|
||||
withPremiumGradient
|
||||
size="smaller"
|
||||
onClick={handleOpenPremium}
|
||||
className={styles.button}
|
||||
>
|
||||
{lang('PremiumLastSeenButton2')}
|
||||
</Button>
|
||||
</div>
|
||||
@ -98,11 +137,11 @@ const ReadDateModal = ({ isOpen, user }: OwnProps & StateProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): StateProps => {
|
||||
const { chatId } = selectTabState(global).readDateModal || {};
|
||||
export default memo(
|
||||
withGlobal<OwnProps>((global): StateProps => {
|
||||
const { chatId, isReadDate } = selectTabState(global).privacySettingsNoticeModal || {};
|
||||
const user = chatId ? selectUser(global, chatId) : undefined;
|
||||
|
||||
return { user };
|
||||
},
|
||||
)(ReadDateModal));
|
||||
return { user, isReadDate };
|
||||
})(PrivacySettingsNoticeModal),
|
||||
);
|
||||
@ -48,4 +48,14 @@
|
||||
pointer-events: auto;
|
||||
cursor: var(--custom-cursor, pointer);
|
||||
}
|
||||
|
||||
.get-status {
|
||||
cursor: var(--custom-cursor, pointer);
|
||||
margin-left: 0.375rem;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.125rem 0.375rem;
|
||||
background: var(--color-background-menu-separator);
|
||||
pointer-events: all;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,6 +90,7 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
|
||||
openMediaViewer,
|
||||
openPremiumModal,
|
||||
openStickerSet,
|
||||
openPrivacySettingsNoticeModal,
|
||||
} = getActions();
|
||||
|
||||
const lang = useLang();
|
||||
@ -173,6 +174,10 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
|
||||
setCurrentPhotoIndex(currentPhotoIndex + 1);
|
||||
});
|
||||
|
||||
const handleOpenGetReadDateModal = useLastCallback(() => {
|
||||
openPrivacySettingsNoticeModal({ chatId: chat!.id, isReadDate: false });
|
||||
});
|
||||
|
||||
function handleSelectFallbackPhoto() {
|
||||
if (!isFirst) return;
|
||||
setHasSlideAnimation(true);
|
||||
@ -264,8 +269,21 @@ const ProfileInfo: FC<OwnProps & StateProps> = ({
|
||||
|
||||
if (user) {
|
||||
return (
|
||||
<div className={buildClassName(styles.status, 'status', isUserOnline(user, userStatus) && 'online')}>
|
||||
<span className="user-status" dir="auto">{getUserStatus(lang, user, userStatus)}</span>
|
||||
<div
|
||||
className={buildClassName(
|
||||
styles.status,
|
||||
'status',
|
||||
isUserOnline(user, userStatus) && 'online',
|
||||
)}
|
||||
>
|
||||
<span className="user-status" dir="auto">
|
||||
{getUserStatus(lang, user, userStatus)}
|
||||
</span>
|
||||
{userStatus?.isReadDateRestrictedByMe && (
|
||||
<span className="get-status" onClick={handleOpenGetReadDateModal}>
|
||||
{lang('StatusHiddenShow')}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React from '../../lib/teact/teact';
|
||||
|
||||
import type { OwnProps } from './ReadDateModal';
|
||||
|
||||
import { Bundles } from '../../util/moduleLoader';
|
||||
|
||||
import useModuleLoader from '../../hooks/useModuleLoader';
|
||||
|
||||
const ReadTimeModalAsync: FC<OwnProps> = (props) => {
|
||||
const { isOpen } = props;
|
||||
const ReadTimeModal = useModuleLoader(Bundles.Extra, 'ReadTimeModal', !isOpen);
|
||||
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
return ReadTimeModal ? <ReadTimeModal {...props} /> : undefined;
|
||||
};
|
||||
|
||||
export default ReadTimeModalAsync;
|
||||
@ -11,6 +11,7 @@ import Flame from '../../../assets/tgs/general/Flame.tgs';
|
||||
import PartyPopper from '../../../assets/tgs/general/PartyPopper.tgs';
|
||||
import Invite from '../../../assets/tgs/invites/Invite.tgs';
|
||||
import JoinRequest from '../../../assets/tgs/invites/Requests.tgs';
|
||||
import LastSeen from '../../../assets/tgs/LastSeen.tgs';
|
||||
import MonkeyClose from '../../../assets/tgs/monkeys/TwoFactorSetupMonkeyClose.tgs';
|
||||
import MonkeyIdle from '../../../assets/tgs/monkeys/TwoFactorSetupMonkeyIdle.tgs';
|
||||
import MonkeyPeek from '../../../assets/tgs/monkeys/TwoFactorSetupMonkeyPeek.tgs';
|
||||
@ -52,4 +53,5 @@ export const LOCAL_TGS_URLS = {
|
||||
Flame,
|
||||
ReadTime,
|
||||
Unlock,
|
||||
LastSeen,
|
||||
};
|
||||
|
||||
@ -79,7 +79,7 @@ import useWindowSize from '../../hooks/window/useWindowSize';
|
||||
import usePinnedMessage from './hooks/usePinnedMessage';
|
||||
|
||||
import Composer from '../common/Composer';
|
||||
import ReadTimeModal from '../common/ReadTimeModal.async';
|
||||
import PrivacySettingsNoticeModal from '../common/PrivacySettingsNoticeModal.async';
|
||||
import SeenByModal from '../common/SeenByModal.async';
|
||||
import UnpinAllMessagesModal from '../common/UnpinAllMessagesModal.async';
|
||||
import GiftPremiumModal from '../main/premium/GiftPremiumModal.async';
|
||||
@ -131,7 +131,7 @@ type StateProps = {
|
||||
hasCurrentTextSearch?: boolean;
|
||||
isSelectModeActive?: boolean;
|
||||
isSeenByModalOpen: boolean;
|
||||
isReadDateModalOpen: boolean;
|
||||
isPrivacySettingsNoticeModalOpen: boolean;
|
||||
isReactorListModalOpen: boolean;
|
||||
isGiftPremiumModalOpen?: boolean;
|
||||
isChatLanguageModalOpen?: boolean;
|
||||
@ -191,7 +191,7 @@ function MiddleColumn({
|
||||
hasCurrentTextSearch,
|
||||
isSelectModeActive,
|
||||
isSeenByModalOpen,
|
||||
isReadDateModalOpen,
|
||||
isPrivacySettingsNoticeModalOpen,
|
||||
isReactorListModalOpen,
|
||||
isGiftPremiumModalOpen,
|
||||
isChatLanguageModalOpen,
|
||||
@ -686,7 +686,7 @@ function MiddleColumn({
|
||||
canPost={renderingCanPost}
|
||||
/>
|
||||
<SeenByModal isOpen={isSeenByModalOpen} />
|
||||
<ReadTimeModal isOpen={isReadDateModalOpen} />
|
||||
<PrivacySettingsNoticeModal isOpen={isPrivacySettingsNoticeModalOpen} />
|
||||
<ReactorListModal isOpen={isReactorListModalOpen} />
|
||||
{IS_TRANSLATION_SUPPORTED && <ChatLanguageModal isOpen={isChatLanguageModalOpen} />}
|
||||
</div>
|
||||
@ -734,7 +734,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const {
|
||||
messageLists, isLeftColumnShown, activeEmojiInteractions,
|
||||
seenByModal, giftPremiumModal, reactorModal, audioPlayer, shouldSkipHistoryAnimations,
|
||||
chatLanguageModal, readDateModal,
|
||||
chatLanguageModal, privacySettingsNoticeModal,
|
||||
} = selectTabState(global);
|
||||
const currentMessageList = selectCurrentMessageList(global);
|
||||
const { leftColumnWidth } = global;
|
||||
@ -750,7 +750,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
hasCurrentTextSearch: Boolean(selectCurrentTextSearch(global)),
|
||||
isSelectModeActive: selectIsInSelectMode(global),
|
||||
isSeenByModalOpen: Boolean(seenByModal),
|
||||
isReadDateModalOpen: Boolean(readDateModal),
|
||||
isPrivacySettingsNoticeModalOpen: Boolean(privacySettingsNoticeModal),
|
||||
isReactorListModalOpen: Boolean(reactorModal),
|
||||
isGiftPremiumModalOpen: giftPremiumModal?.isOpen,
|
||||
isChatLanguageModalOpen: Boolean(chatLanguageModal),
|
||||
|
||||
@ -25,14 +25,14 @@ type OwnProps = {
|
||||
function ReadTimeMenuItem({
|
||||
message, shouldRenderShowWhen, canLoadReadDate, closeContextMenu, menuSeparatorSize,
|
||||
}: OwnProps) {
|
||||
const { openGetReadDateModal } = getActions();
|
||||
const { openPrivacySettingsNoticeModal } = getActions();
|
||||
const lang = useLang();
|
||||
const { readDate } = message;
|
||||
const shouldRenderSkeleton = canLoadReadDate && !readDate && !shouldRenderShowWhen;
|
||||
|
||||
const handleOpenModal = () => {
|
||||
closeContextMenu();
|
||||
openGetReadDateModal({ chatId: message.chatId, messageId: message.id });
|
||||
openPrivacySettingsNoticeModal({ chatId: message.chatId, isReadDate: true });
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@ -470,7 +470,28 @@ addActionHandler('loadPrivacySettings', async (global): Promise<void> => {
|
||||
});
|
||||
|
||||
addActionHandler('setPrivacyVisibility', async (global, actions, payload): Promise<void> => {
|
||||
const { privacyKey, visibility } = payload!;
|
||||
const { privacyKey, visibility, onSuccess } = payload!;
|
||||
|
||||
if (!global.settings.privacy[privacyKey]) {
|
||||
const result = await callApi('fetchPrivacySettings', privacyKey);
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
global = getGlobal();
|
||||
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
|
||||
global = {
|
||||
...global,
|
||||
settings: {
|
||||
...global.settings,
|
||||
privacy: {
|
||||
...global.settings.privacy,
|
||||
[privacyKey]: result.rules,
|
||||
},
|
||||
},
|
||||
};
|
||||
setGlobal(global);
|
||||
}
|
||||
|
||||
const {
|
||||
privacy: { [privacyKey]: settings },
|
||||
@ -491,6 +512,8 @@ addActionHandler('setPrivacyVisibility', async (global, actions, payload): Promi
|
||||
return;
|
||||
}
|
||||
|
||||
onSuccess?.();
|
||||
|
||||
global = getGlobal();
|
||||
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
|
||||
global = {
|
||||
|
||||
@ -781,19 +781,19 @@ addActionHandler('closeSeenByModal', (global, actions, payload): ActionReturnTyp
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
addActionHandler('openGetReadDateModal', (global, actions, payload): ActionReturnType => {
|
||||
const { chatId, messageId, tabId = getCurrentTabId() } = payload;
|
||||
addActionHandler('openPrivacySettingsNoticeModal', (global, actions, payload): ActionReturnType => {
|
||||
const { chatId, isReadDate, tabId = getCurrentTabId() } = payload;
|
||||
|
||||
return updateTabState(global, {
|
||||
readDateModal: { chatId, messageId },
|
||||
privacySettingsNoticeModal: { chatId, isReadDate },
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
addActionHandler('closeGetReadDateModal', (global, actions, payload): ActionReturnType => {
|
||||
addActionHandler('closePrivacySettingsNoticeModal', (global, actions, payload): ActionReturnType => {
|
||||
const { tabId = getCurrentTabId() } = payload || {};
|
||||
|
||||
return updateTabState(global, {
|
||||
readDateModal: undefined,
|
||||
privacySettingsNoticeModal: undefined,
|
||||
}, tabId);
|
||||
});
|
||||
|
||||
|
||||
@ -292,9 +292,9 @@ export type TabState = {
|
||||
messageId: number;
|
||||
};
|
||||
|
||||
readDateModal?: {
|
||||
privacySettingsNoticeModal?: {
|
||||
chatId: string;
|
||||
messageId: number;
|
||||
isReadDate: boolean;
|
||||
};
|
||||
|
||||
reactorModal?: {
|
||||
@ -1170,6 +1170,7 @@ export interface ActionPayloads {
|
||||
setPrivacyVisibility: {
|
||||
privacyKey: ApiPrivacyKey;
|
||||
visibility: PrivacyVisibility;
|
||||
onSuccess?: VoidFunction;
|
||||
};
|
||||
|
||||
setPrivacySettings: {
|
||||
@ -1632,11 +1633,11 @@ export interface ActionPayloads {
|
||||
messageId: number;
|
||||
} & WithTabId;
|
||||
closeSeenByModal: WithTabId | undefined;
|
||||
openGetReadDateModal: {
|
||||
openPrivacySettingsNoticeModal: {
|
||||
chatId: string;
|
||||
messageId: number;
|
||||
isReadDate: boolean;
|
||||
} & WithTabId;
|
||||
closeGetReadDateModal: WithTabId | undefined;
|
||||
closePrivacySettingsNoticeModal: WithTabId | undefined;
|
||||
closeReactorListModal: WithTabId | undefined;
|
||||
openReactorListModal: {
|
||||
chatId: string;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user