Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/webapp/src/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,13 @@
"conversationFileUploadOverlayDescription": "Drag & drop to add files",
"conversationFileUploadOverlayTitle": "Upload files",
"conversationFileVideoPreviewLabel": "Video file preview for: {src}",
"conversationFilterDrafts": "Drafts",
"conversationFilterMentions": "Mentions",
"conversationFilterNone": "No filter",
"conversationFilterPings": "Pings",
"conversationFilterReplies": "Replies",
"conversationFilterTooltip": "Filter conversations",
"conversationFilterUnread": "Unread",
"conversationFoldersEmptyText": "Add your conversations to folders to stay organized.",
"conversationFoldersEmptyTextLearnMore": "Learn more",
"conversationFooterArchive": "Archive",
Expand Down Expand Up @@ -1843,6 +1850,8 @@
"selfNotSupportMLSMsgPart1": "You can't communicate with {selfUserName}, as your device doesn't support the suitable protocol.",
"selfNotSupportMLSMsgPart2": "to call, and send messages and files.",
"selfProfileImageAlt": "Your profile picture",
"servicesNotEnabledNoteTitle": "Your team doesn't use apps yet",
"servicesNotEnabledBody": "To improve your workflow with apps, your team needs configuration. Please contact your team admin.",
"servicesOptionsTitle": "Apps",
"servicesRoomToggleInfo": "Open this conversation to apps.",
"servicesRoomToggleInfoExtended": "Open this conversation to apps. You can always change it later.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@
import {CONVERSATION_PROTOCOL} from '@wireapp/api-client/lib/team';
import {container} from 'tsyringe';

import {ConversationType} from 'Components/Modals/CreateConversation/types';
import {AppsDisabledNote} from 'Components/Note/AppsDisabledNote/AppsDisabledNote';
import {InfoToggle} from 'Components/toggle/InfoToggle';
import {TeamState} from 'Repositories/team/TeamState';
import {Config} from 'src/script/Config';
import {useKoSubscribableChildren} from 'Util/ComponentUtil';
import {t} from 'Util/LocalizerUtil';

import {useCreateConversationModal} from '../hooks/useCreateConversationModal';
import {ConversationType} from '../types';

export const Preference = () => {
const {
Expand All @@ -44,9 +45,16 @@ export const Preference = () => {

const teamState = container.resolve(TeamState);

const {isCellsEnabled: isCellsEnabledForTeam, isMLSEnabled} = useKoSubscribableChildren(teamState, [
const {
isCellsEnabled: isCellsEnabledForTeam,
isMLSEnabled,
isAppsEnabled,
hasWhitelistedServices,
} = useKoSubscribableChildren(teamState, [
'isCellsEnabled',
'isMLSEnabled',
'isAppsEnabled',
'hasWhitelistedServices',
]);
const isCellsEnabledForEnvironment = Config.getConfig().FEATURE.ENABLE_CELLS;
const isCellsOptionEnabled = isCellsEnabledForEnvironment && isCellsEnabledForTeam;
Expand All @@ -55,7 +63,13 @@ export const Preference = () => {
? teamState.teamFeatures()?.mls?.config.defaultProtocol
: CONVERSATION_PROTOCOL.PROTEUS;

// Read receipts are temorarily disabled for MLS groups and channels until it is supported
const isAppsFeatureAvailable =
(defaultProtocol === CONVERSATION_PROTOCOL.MLS && isAppsEnabled) ||
(defaultProtocol === CONVERSATION_PROTOCOL.PROTEUS &&
hasWhitelistedServices &&
conversationType !== ConversationType.Channel);

// Read receipts are temporarily disabled for MLS groups and channels until it is supported
const areReadReceiptsEnabled = defaultProtocol !== CONVERSATION_PROTOCOL.MLS;

return (
Expand All @@ -70,17 +84,17 @@ export const Preference = () => {
dataUieName="read-receipts"
/>

{conversationType === ConversationType.Group && (
<InfoToggle
className="modal-style"
dataUieName="services"
info={t('servicesRoomToggleInfoExtended')}
setIsChecked={setIsServicesEnabled}
isDisabled={false}
name={t('servicesOptionsTitle')}
isChecked={isServicesEnabled}
/>
)}
<InfoToggle
className="modal-style"
dataUieName="services"
info={t('servicesRoomToggleInfoExtended')}
setIsChecked={setIsServicesEnabled}
isDisabled={!isAppsFeatureAvailable}
name={t('servicesOptionsTitle')}
isChecked={isServicesEnabled && isAppsFeatureAvailable}
label={!isAppsFeatureAvailable && <AppsDisabledNote />}
/>

{areReadReceiptsEnabled && (
<InfoToggle
className="modal-style"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {WebAppEvents} from '@wireapp/webapp-events';
import {FadingScrollbar} from 'Components/FadingScrollbar';
import * as Icon from 'Components/Icon';
import {ModalComponent} from 'Components/Modals/ModalComponent';
import {AppsDisabledNote} from 'Components/Note/AppsDisabledNote/AppsDisabledNote';
import {SearchInput} from 'Components/SearchInput';
import {TextInput} from 'Components/TextInput';
import {InfoToggle} from 'Components/toggle/InfoToggle';
Expand Down Expand Up @@ -79,11 +80,13 @@ const GroupCreationModal = ({
isMLSEnabled: isMLSEnabledForTeam,
isProtocolToggleEnabledForUser,
isCellsEnabled: isCellsEnabledForTeam,
isAppsEnabled: isAppsEnabledForTeam,
} = useKoSubscribableChildren(teamState, [
'isTeam',
'isMLSEnabled',
'isProtocolToggleEnabledForUser',
'isCellsEnabled',
'isAppsEnabled',
]);
const {self: selfUser} = useKoSubscribableChildren(userState, ['self']);

Expand Down Expand Up @@ -154,7 +157,13 @@ const GroupCreationModal = ({
const isGuestAndServicesRoom = accessState === ACCESS_STATE.TEAM.GUESTS_SERVICES;
const isGuestRoom = accessState === ACCESS_STATE.TEAM.GUEST_ROOM;
const isGuestEnabled = isGuestRoom || isGuestAndServicesRoom;
const isServicesEnabled = isServicesRoom || isGuestAndServicesRoom;

const isAppsFeatureAvailable =
isTeam &&
((selectedProtocol.value == CONVERSATION_PROTOCOL.PROTEUS && teamState?.hasWhitelistedServices()) ||
(selectedProtocol.value == CONVERSATION_PROTOCOL.MLS && isAppsEnabledForTeam));
Comment on lines +161 to +164
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have this logic in two places, it is possible to keep it in a single places? A hook or reusable function?


const isServicesEnabled = isAppsFeatureAvailable && (isServicesRoom || isGuestAndServicesRoom);

const {setCurrentTab: setCurrentSidebarTab} = useSidebarStore();

Expand Down Expand Up @@ -504,17 +513,18 @@ const GroupCreationModal = ({
name={t('guestOptionsTitle')}
info={t('guestRoomToggleInfo')}
/>
{selectedProtocol.value !== CONVERSATION_PROTOCOL.MLS && (
<InfoToggle
className="modal-style"
dataUieName="services"
isChecked={isServicesEnabled}
setIsChecked={clickOnToggleServicesMode}
isDisabled={false}
name={t('servicesOptionsTitle')}
info={t('servicesRoomToggleInfo')}
/>
)}
<InfoToggle
className="modal-style"
dataUieName="services"
isChecked={isServicesEnabled}
setIsChecked={clickOnToggleServicesMode}
isDisabled={!isAppsFeatureAvailable}
name={t('servicesOptionsTitle')}
info={t('servicesRoomToggleInfo')}
/>

{!isAppsFeatureAvailable && <AppsDisabledNote />}

{areReadReceiptsEnabled && (
<InfoToggle
className="modal-style"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Wire
* Copyright (C) 2026 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
*/

import React from 'react';

Check failure on line 20 in apps/webapp/src/script/components/Note/AppsDisabledNote/AppsDisabledNote.tsx

View workflow job for this annotation

GitHub Actions / test

'React' is declared but its value is never read.

import {Note} from 'Components/Note/Note';
import {t} from 'Util/LocalizerUtil';

const AppsDisabledNote = () => {
return (
<Note title={t('servicesNotEnabledNoteTitle')}>
<span className={'subline'}>{t('servicesNotEnabledBody')}</span>
</Note>
);
};

export {AppsDisabledNote};
49 changes: 49 additions & 0 deletions apps/webapp/src/script/components/Note/Note.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Wire
* Copyright (C) 2026 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
*/

import {CSSObject} from '@emotion/react';

export const ContainerStyle: CSSObject = {
display: 'flex',
flexDirection: 'column',
gap: '0.3rem',
padding: '0.75rem',
background: 'var(--accent-color-50)',
'.theme-dark &': {
background: 'var(--accent-color-800)',
boxShadow: 'none',
},
border: '1px solid var(--accent-color-500)',
borderRadius: '0.5rem',
lineHeight: '1.5',
marginTop: '0.3rem',
};

export const HeaderStyle: CSSObject = {
display: 'flex',
alignItems: 'center',
fontWeight: 'var(--font-weight-semibold)',
gap: '0.5rem',
};

export const ContentStyle: CSSObject = {
display: 'flex',
flexDirection: 'column',
gap: '4px',
};
45 changes: 45 additions & 0 deletions apps/webapp/src/script/components/Note/Note.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Wire
* Copyright (C) 2026 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
*/

import React, {ReactNode} from 'react';

Check failure on line 20 in apps/webapp/src/script/components/Note/Note.tsx

View workflow job for this annotation

GitHub Actions / test

'React' is declared but its value is never read.

import {InfoIcon} from 'Components/Icon';
import {ContainerStyle, ContentStyle, HeaderStyle} from 'Components/Note/Note.styles';

interface NoteProps {
title: string;
children?: ReactNode;
}

const Note = ({title, children}: NoteProps) => {
return (
<div css={ContainerStyle}>
<div css={HeaderStyle}>
<InfoIcon />
<span className="heading-h4">{title}</span>
</div>

<div css={ContentStyle}>
<div>{children}</div>
</div>
</div>
);
};

export {Note};
5 changes: 4 additions & 1 deletion apps/webapp/src/script/components/toggle/InfoToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
*
*/

import {useId} from 'react';
import React, {useId} from 'react';

import cx from 'classnames';

Expand All @@ -29,6 +29,7 @@ interface InfoToggleProps {
name: string;
className?: string;
setIsChecked: (checked: boolean) => void;
label?: React.ReactNode;
}

const InfoToggle = ({
Expand All @@ -39,6 +40,7 @@ const InfoToggle = ({
isDisabled,
name,
setIsChecked,
label,
}: InfoToggleProps) => {
const dataUieNameInfoText = `status-info-toggle-${dataUieName}`;
const dataUieNameLabelText = `do-toggle-${dataUieName}`;
Expand Down Expand Up @@ -77,6 +79,7 @@ const InfoToggle = ({
</button>
</div>
</div>
{label}
</div>
);
};
Expand Down
9 changes: 9 additions & 0 deletions apps/webapp/src/types/i18n.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1798,6 +1798,7 @@ declare module 'I18n/en-US.json' {
'searchCreateGroup': `Create group`;
'searchCreateGuestRoom': `Create guest room`;
'searchDirectConversations': `Search 1:1 conversations`;
'searchDraftsConversations': `Search in drafts`;
'searchFavoriteConversations': `Search favorites`;
'searchFederatedDomainNotAvailable': `The federated domain is currently not available.`;
'searchFederatedDomainNotAvailableLearnMore': `Learn more`;
Expand All @@ -1816,6 +1817,7 @@ declare module 'I18n/en-US.json' {
'searchManageServices': `Manage Apps`;
'searchManageServicesNoResults': `Manage apps`;
'searchMemberInvite': `Invite people to join the team`;
'searchMentionsConversations': `Search in mentions`;
'searchNoContactsOnWire': `You have no contacts on {brandName}.\nTry finding people by\nname or username.`;
'searchNoMatchesPartner': `No results`;
'searchNoServicesManager': `Apps are helpers that can improve your workflow.`;
Expand All @@ -1826,6 +1828,8 @@ declare module 'I18n/en-US.json' {
'searchPeople': `People`;
'searchPeopleOnlyPlaceholder': `Search people`;
'searchPeoplePlaceholder': `Search for people and conversations`;
'searchPingsConversations': `Search in pings`;
'searchRepliesConversations': `Search in replies`;
'searchServiceConfirmButton': `Open Conversation`;
'searchServicePlaceholder': `Search by name`;
'searchServices': `Apps`;
Expand All @@ -1835,6 +1839,7 @@ declare module 'I18n/en-US.json' {
'searchTrySearch': `Find people by\nname or username`;
'searchTrySearchFederation': `Find people in Wire by name or\n@username\n\nFind people from another domain\nby @username@domainname`;
'searchTrySearchLearnMore': `Learn more`;
'searchUnreadConversations': `Search in unread`;
'selectAccountTypeHeading': `How will you use Wire?`;
'selectPersonalAccountTypeOptionButtonText': `Create Personal Account`;
'selectPersonalAccountTypeOptionDescription': `Chat with friends and family.`;
Expand All @@ -1849,6 +1854,8 @@ declare module 'I18n/en-US.json' {
'selfNotSupportMLSMsgPart1': `You can\'t communicate with {selfUserName}, as your device doesn\'t support the suitable protocol.`;
'selfNotSupportMLSMsgPart2': `to call, and send messages and files.`;
'selfProfileImageAlt': `Your profile picture`;
'servicesNotEnabledNoteTitle': `Your team doesn\'t use apps yet`;
'servicesNotEnabledBody': `To improve your workflow with apps, your team needs configuration. Please contact your team admin.`;
'servicesOptionsTitle': `Apps`;
'servicesRoomToggleInfo': `Open this conversation to apps.`;
'servicesRoomToggleInfoExtended': `Open this conversation to apps. You can always change it later.`;
Expand Down Expand Up @@ -1885,6 +1892,8 @@ declare module 'I18n/en-US.json' {
'success.openWebAppText': `Open Wire for web`;
'success.subheader': `What do you want to do next?`;
'systemMessageLearnMore': `Learn more`;
'tabsFilterHeader': `Show filters`;
'tabsFilterTooltip': `Customize visible tabs`;
'takeoverButtonChoose': `Choose your own`;
'takeoverButtonKeep': `Keep this one`;
'takeoverLink': `Learn more`;
Expand Down
Loading