diff --git a/_locales/en/messages.json b/_locales/en/messages.json index cafa1c320..9c6bca17d 100644 --- a/_locales/en/messages.json +++ b/_locales/en/messages.json @@ -590,6 +590,8 @@ "noMessagesInEverythingElse": "You have no messages from $name$. 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", diff --git a/ts/components/NoticeBanner.tsx b/ts/components/NoticeBanner.tsx index 255926d82..592f113db 100644 --- a/ts/components/NoticeBanner.tsx +++ b/ts/components/NoticeBanner.tsx @@ -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 ( { flexDirection={'row'} justifyContent={'center'} alignItems={'center'} + data-testid={dataTestId} > {text} { event?.preventDefault(); - dismissCallback(); + onButtonClick(); }} /> ); }; + +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 ( + + {window.i18n('versionRequiredForNewGroupDescription')} + + ); +}; diff --git a/ts/components/SessionInboxView.tsx b/ts/components/SessionInboxView.tsx index bc69090b2..df3b0b78b 100644 --- a/ts/components/SessionInboxView.tsx +++ b/ts/components/SessionInboxView.tsx @@ -120,7 +120,9 @@ const SomeDeviceOutdatedSyncingNotice = () => { return ( ); }; diff --git a/ts/components/SessionWrapperModal.tsx b/ts/components/SessionWrapperModal.tsx index c75a64c0d..cf5a2cfe6 100644 --- a/ts/components/SessionWrapperModal.tsx +++ b/ts/components/SessionWrapperModal.tsx @@ -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 ( -
{
- + ); }; diff --git a/ts/components/conversation/SessionConversation.tsx b/ts/components/conversation/SessionConversation.tsx index df5128e6a..df9f157a2 100644 --- a/ts/components/conversation/SessionConversation.tsx +++ b/ts/components/conversation/SessionConversation.tsx @@ -251,26 +251,15 @@ export class SessionConversation extends React.Component { // 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 (
- {selectedConversation?.hasOutdatedClient?.length ? ( - { - const conversation = ConvoHub.use().get(selectedConversation.id); - conversation.set({ hasOutdatedClient: undefined }); - void conversation.commit(); - }} - /> - ) : null} + +
{isSelectedConvoInitialLoadingInProgress ? ( @@ -652,3 +641,50 @@ const renderImagePreview = async (contentType: string, file: File, fileName: str thumbnail: null, }; }; + +function OutdatedClientBanner(props: { + selectedConversation: Pick; + 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 ? ( + { + 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; +}) { + const { selectedConversation } = props; + + const isLegacyGroup = + !selectedConversation.isPrivate && + !selectedConversation.isPublic && + selectedConversation.id.startsWith('05'); + + return isLegacyGroup ? ( + { + throw new Error('TODO'); // fixme audric + }} + icon="externalLink" + dataTestId="legacy-group-banner" + /> + ) : null; +} diff --git a/ts/components/dialog/InviteContactsDialog.tsx b/ts/components/dialog/InviteContactsDialog.tsx index 86bfd0ad3..6f80fac8e 100644 --- a/ts/components/dialog/InviteContactsDialog.tsx +++ b/ts/components/dialog/InviteContactsDialog.tsx @@ -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 ( + {hasContacts && isGroupV2 && } + {isGroupV2 && ( - - Share History?{' '} - setShareHistory(!shareHistory)} /> - + <> + + Share History?{' '} + setShareHistory(!shareHistory)} /> + + )}
{hasContacts ? ( diff --git a/ts/components/dialog/StyledRootDialog.tsx b/ts/components/dialog/StyledRootDialog.tsx new file mode 100644 index 000000000..d98a95fae --- /dev/null +++ b/ts/components/dialog/StyledRootDialog.tsx @@ -0,0 +1,3 @@ +import styled from 'styled-components'; + +export const StyledRootDialog = styled.div``; diff --git a/ts/components/leftpane/overlay/OverlayClosedGroup.tsx b/ts/components/leftpane/overlay/OverlayClosedGroup.tsx index a5609094d..fe8db6d1b 100644 --- a/ts/components/leftpane/overlay/OverlayClosedGroup.tsx +++ b/ts/components/leftpane/overlay/OverlayClosedGroup.tsx @@ -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 = () => { + {!noContactsForClosedGroup && window.sessionFeatureFlags.useClosedGroupV2 && ( + + )} + {noContactsForClosedGroup ? ( diff --git a/ts/react.d.ts b/ts/react.d.ts index ba47a8b34..92b76d5ea 100644 --- a/ts/react.d.ts +++ b/ts/react.d.ts @@ -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' diff --git a/ts/types/LocalizerKeys.ts b/ts/types/LocalizerKeys.ts index 458be881c..8bbc5a6b3 100644 --- a/ts/types/LocalizerKeys.ts +++ b/ts/types/LocalizerKeys.ts @@ -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'