feat: add notice banner for deprecating group and inviting users

pull/3052/head
Audric Ackermann 1 year ago
parent 1796e82bcb
commit 6f7e576604

@ -590,6 +590,8 @@
"noMessagesInEverythingElse": "You have no messages from <b>$name$</b>. Send a message to start the conversation!",
"hideBanner": "Hide",
"someOfYourDeviceUseOutdatedVersion": "Some of your devices are using outdated versions. Syncing may be unreliable until they are updated.",
"versionRequiredForNewGroupDescription": "Users must have version {VERSION} or higher to receive invitations",
"upgradeYourGroupBefore": "Groups have been upgraded. Upgrade your group chats by creating a new Group. Support for old Groups will be discontinued on [Date].",
"openMessageRequestInboxDescription": "View your Message Request inbox",
"clearAllReactions": "Are you sure you want to clear all $emoji$ ?",
"expandedReactionsText": "Show Less",

@ -1,7 +1,8 @@
import React from 'react';
import React, { SessionDataTestId } from 'react';
import styled from 'styled-components';
import { Flex } from './basic/Flex';
import { SessionIconButton } from './icon';
import { SessionIconButton, SessionIconType } from './icon';
import { StyledRootDialog } from './dialog/StyledRootDialog';
const StyledNoticeBanner = styled(Flex)`
position: relative;
@ -23,11 +24,13 @@ const StyledText = styled.span`
type NoticeBannerProps = {
text: string;
dismissCallback: () => void;
icon: SessionIconType;
onButtonClick: () => void;
dataTestId: SessionDataTestId;
};
export const NoticeBanner = (props: NoticeBannerProps) => {
const { text, dismissCallback } = props;
const { text, onButtonClick, icon, dataTestId } = props;
return (
<StyledNoticeBanner
@ -35,17 +38,43 @@ export const NoticeBanner = (props: NoticeBannerProps) => {
flexDirection={'row'}
justifyContent={'center'}
alignItems={'center'}
data-testid={dataTestId}
>
<StyledText>{text}</StyledText>
<SessionIconButton
iconType="exit"
iconType={icon}
iconColor="inherit"
iconSize="small"
onClick={event => {
event?.preventDefault();
dismissCallback();
onButtonClick();
}}
/>
</StyledNoticeBanner>
);
};
const StyledGroupInviteBanner = styled(Flex)`
position: relative;
background-color: var(--orange-color);
color: var(--black-color);
font-size: var(--font-size-sm);
padding: var(--margins-xs) var(--margins-lg);
text-align: center;
flex-shrink: 0;
// when part a a dialog, invert it and make it narrower (as the dialog grows to make it fit)
${StyledRootDialog} & {
background-color: unset;
color: var(--orange-color);
max-width: 300px;
}
`;
export const GroupInviteRequiredVersionBanner = () => {
return (
<StyledGroupInviteBanner data-testid="invite-warning">
{window.i18n('versionRequiredForNewGroupDescription')}
</StyledGroupInviteBanner>
);
};

@ -120,7 +120,9 @@ const SomeDeviceOutdatedSyncingNotice = () => {
return (
<NoticeBanner
text={window.i18n('someOfYourDeviceUseOutdatedVersion')}
dismissCallback={dismiss}
onButtonClick={dismiss}
icon="exit"
dataTestId='some-of-your-devices-outdated-inbox'
/>
);
};

@ -5,6 +5,7 @@ import useKey from 'react-use/lib/useKey';
import { SessionIconButton } from './icon';
import { SessionButton, SessionButtonColor, SessionButtonType } from './basic/SessionButton';
import { StyledRootDialog } from './dialog/StyledRootDialog';
export type SessionWrapperModalType = {
title?: string;
@ -63,7 +64,7 @@ export const SessionWrapperModal = (props: SessionWrapperModalType) => {
};
return (
<div
<StyledRootDialog
className={classNames('loki-dialog modal', additionalClassName || null)}
onClick={handleClick}
role="dialog"
@ -125,6 +126,6 @@ export const SessionWrapperModal = (props: SessionWrapperModalType) => {
</div>
</div>
</div>
</div>
</StyledRootDialog>
);
};

@ -251,26 +251,15 @@ export class SessionConversation extends React.Component<Props, State> {
// TODOLATER break selectionMode into it's own container component so we can use hooks to fetch relevant state from the store
const selectionMode = selectedMessages.length > 0;
const bannerText =
selectedConversation.hasOutdatedClient &&
selectedConversation.hasOutdatedClient !== ourDisplayNameInProfile
? window.i18n('disappearingMessagesModeOutdated', [selectedConversation.hasOutdatedClient])
: window.i18n('someOfYourDeviceUseOutdatedVersion');
return (
<SessionTheme>
<div className="conversation-header">
<ConversationHeaderWithDetails />
{selectedConversation?.hasOutdatedClient?.length ? (
<NoticeBanner
text={bannerText}
dismissCallback={() => {
const conversation = ConvoHub.use().get(selectedConversation.id);
conversation.set({ hasOutdatedClient: undefined });
void conversation.commit();
}}
<OutdatedClientBanner
ourDisplayNameInProfile={ourDisplayNameInProfile}
selectedConversation={selectedConversation}
/>
) : null}
<OutdatedLegacyGroupBanner selectedConversation={selectedConversation} />
</div>
{isSelectedConvoInitialLoadingInProgress ? (
<ConvoLoadingSpinner />
@ -652,3 +641,50 @@ const renderImagePreview = async (contentType: string, file: File, fileName: str
thumbnail: null,
};
};
function OutdatedClientBanner(props: {
selectedConversation: Pick<ReduxConversationType, 'id' | 'hasOutdatedClient'>;
ourDisplayNameInProfile: string;
}) {
const { selectedConversation, ourDisplayNameInProfile } = props;
const bannerText =
selectedConversation.hasOutdatedClient &&
selectedConversation.hasOutdatedClient !== ourDisplayNameInProfile
? window.i18n('disappearingMessagesModeOutdated', [selectedConversation.hasOutdatedClient])
: window.i18n('someOfYourDeviceUseOutdatedVersion');
return selectedConversation.hasOutdatedClient?.length ? (
<NoticeBanner
text={bannerText}
onButtonClick={() => {
const conversation = ConvoHub.use().get(selectedConversation.id);
conversation.set({ hasOutdatedClient: undefined });
void conversation.commit();
}}
icon="exit"
dataTestId="some-of-your-devices-outdated-conversation"
/>
) : null;
}
function OutdatedLegacyGroupBanner(props: {
selectedConversation: Pick<ReduxConversationType, 'id' | 'isPrivate' | 'isPublic'>;
}) {
const { selectedConversation } = props;
const isLegacyGroup =
!selectedConversation.isPrivate &&
!selectedConversation.isPublic &&
selectedConversation.id.startsWith('05');
return isLegacyGroup ? (
<NoticeBanner
text={window.i18n('upgradeYourGroupBefore')}
onButtonClick={() => {
throw new Error('TODO'); // fixme audric
}}
icon="externalLink"
dataTestId="legacy-group-banner"
/>
) : null;
}

@ -31,6 +31,7 @@ import { SessionWrapperModal } from '../SessionWrapperModal';
import { SessionButton, SessionButtonColor, SessionButtonType } from '../basic/SessionButton';
import { SessionSpinner } from '../basic/SessionSpinner';
import { SessionToggle } from '../basic/SessionToggle';
import { GroupInviteRequiredVersionBanner } from '../NoticeBanner';
type Props = {
conversationId: string;
@ -186,12 +187,16 @@ const InviteContactsDialogInner = (props: Props) => {
return (
<SessionWrapperModal title={titleText} onClose={closeDialog}>
{hasContacts && isGroupV2 && <GroupInviteRequiredVersionBanner />}
<SpacerLG />
{isGroupV2 && (
<>
<span style={{ display: 'flex', alignItems: 'center' }}>
Share History?{' '}
<SessionToggle active={shareHistory} onClick={() => setShareHistory(!shareHistory)} />
</span>
</>
)}
<div className="contact-selection-list">
{hasContacts ? (

@ -0,0 +1,3 @@
import styled from 'styled-components';
export const StyledRootDialog = styled.div``;

@ -23,6 +23,7 @@ import { getSearchResultsContactOnly, isSearching } from '../../../state/selecto
import { useOurPkStr } from '../../../state/selectors/user';
import { SessionSearchInput } from '../../SessionSearchInput';
import { SpacerLG } from '../../basic/Text';
import { GroupInviteRequiredVersionBanner } from '../../NoticeBanner';
const StyledMemberListNoContacts = styled.div`
font-family: var(--font-mono), var(--font-default);
@ -178,6 +179,10 @@ export const OverlayClosedGroupV2 = () => {
<SessionSpinner loading={isCreatingGroup} />
<SpacerLG />
<SessionSearchInput />
{!noContactsForClosedGroup && window.sessionFeatureFlags.useClosedGroupV2 && (
<GroupInviteRequiredVersionBanner />
)}
<StyledGroupMemberListContainer>
{noContactsForClosedGroup ? (
<NoContacts />

4
ts/react.d.ts vendored

@ -51,6 +51,10 @@ declare module 'react' {
| 'microphone-button'
| 'call-button'
| 'attachments-button'
| 'invite-warning'
| 'some-of-your-devices-outdated-conversation'
| 'some-of-your-devices-outdated-inbox'
| 'legacy-group-banner'
// generic button types
| 'emoji-button'

@ -290,8 +290,8 @@ export type LocalizerKeys =
| 'leaveGroupConfirmation'
| 'leaveGroupConfirmationAdmin'
| 'leaveGroupConfirmationOnlyAdmin'
| 'leaveGroupConfirmationOnlyAdminWarning'
| 'leaveGroupConfirmationOnlyAdminLegacy'
| 'leaveGroupConfirmationOnlyAdminWarning'
| 'leaveGroupFailed'
| 'leaveGroupFailedPleaseTryAgain'
| 'leaving'
@ -575,6 +575,7 @@ export type LocalizerKeys =
| 'unreadMessages'
| 'updateDisappearingMessagesFallback'
| 'updateGroupDialogTitle'
| 'upgradeYourGroupBefore'
| 'userAddedToModerators'
| 'userBanFailed'
| 'userBanned'
@ -582,6 +583,7 @@ export type LocalizerKeys =
| 'userRemovedFromModerators'
| 'userUnbanFailed'
| 'userUnbanned'
| 'versionRequiredForNewGroupDescription'
| 'video'
| 'videoAttachmentAlt'
| 'viewMenuResetZoom'

Loading…
Cancel
Save