diff --git a/apps/webapp/src/i18n/en-US.json b/apps/webapp/src/i18n/en-US.json index f9319f97ba3..3b020706739 100644 --- a/apps/webapp/src/i18n/en-US.json +++ b/apps/webapp/src/i18n/en-US.json @@ -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", @@ -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.", diff --git a/apps/webapp/src/script/components/Modals/CreateConversation/CreateConversationSteps/Preference.tsx b/apps/webapp/src/script/components/Modals/CreateConversation/CreateConversationSteps/Preference.tsx index b480d986535..1f4292411ce 100644 --- a/apps/webapp/src/script/components/Modals/CreateConversation/CreateConversationSteps/Preference.tsx +++ b/apps/webapp/src/script/components/Modals/CreateConversation/CreateConversationSteps/Preference.tsx @@ -20,6 +20,8 @@ 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'; @@ -27,7 +29,6 @@ import {useKoSubscribableChildren} from 'Util/ComponentUtil'; import {t} from 'Util/LocalizerUtil'; import {useCreateConversationModal} from '../hooks/useCreateConversationModal'; -import {ConversationType} from '../types'; export const Preference = () => { const { @@ -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; @@ -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 ( @@ -70,17 +84,17 @@ export const Preference = () => { dataUieName="read-receipts" /> - {conversationType === ConversationType.Group && ( - - )} + } + /> + {areReadReceiptsEnabled && ( - {selectedProtocol.value !== CONVERSATION_PROTOCOL.MLS && ( - - )} + + + {!isAppsFeatureAvailable && } + {areReadReceiptsEnabled && ( { + return ( + + {t('servicesNotEnabledBody')} + + ); +}; + +export {AppsDisabledNote}; diff --git a/apps/webapp/src/script/components/Note/Note.styles.ts b/apps/webapp/src/script/components/Note/Note.styles.ts new file mode 100644 index 00000000000..7ded0083327 --- /dev/null +++ b/apps/webapp/src/script/components/Note/Note.styles.ts @@ -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', +}; diff --git a/apps/webapp/src/script/components/Note/Note.tsx b/apps/webapp/src/script/components/Note/Note.tsx new file mode 100644 index 00000000000..de11257df14 --- /dev/null +++ b/apps/webapp/src/script/components/Note/Note.tsx @@ -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 {ReactNode} from 'react'; + +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 ( +
+
+ + {title} +
+ +
+
{children}
+
+
+ ); +}; + +export {Note}; diff --git a/apps/webapp/src/script/components/UserSearchableList/UserSearchableList.tsx b/apps/webapp/src/script/components/UserSearchableList/UserSearchableList.tsx index 9c365987473..db24a0a1f90 100644 --- a/apps/webapp/src/script/components/UserSearchableList/UserSearchableList.tsx +++ b/apps/webapp/src/script/components/UserSearchableList/UserSearchableList.tsx @@ -19,7 +19,7 @@ import React, {useEffect, useState} from 'react'; -import {QualifiedId} from '@wireapp/api-client/lib/user'; +import {QualifiedId, UserType} from '@wireapp/api-client/lib/user'; import {container} from 'tsyringe'; import {useDebouncedCallback} from 'use-debounce'; @@ -53,6 +53,7 @@ export type UserListProps = React.ComponentProps & { /** will do an extra request to the server when user types in (otherwise will only lookup given local users) */ allowRemoteSearch?: boolean; filterRemoteTeamUsers?: boolean; + userType: UserType; }; export const UserSearchableList = ({ @@ -66,6 +67,7 @@ export const UserSearchableList = ({ selfUser, users, teamState = container.resolve(TeamState), + userType, ...props }: UserListProps) => { const {searchRepository, teamRepository, selfFirst, ...userListProps} = props; @@ -74,7 +76,9 @@ export const UserSearchableList = ({ const [filteredUsers, setFilteredUsers] = useState([]); const [remoteTeamMembers, setRemoteTeamMembers] = useState([]); - const filteredSelectedUsers = selectedUsers ? searchRepository.searchUserInSet(filter, selectedUsers) : undefined; + const filteredSelectedUsers = selectedUsers + ? searchRepository.searchUserInSet(filter, selectedUsers).filter(u => u.type === UserType.REGULAR) + : undefined; const selfInTeam = teamState.isInTeam(selfUser); @@ -85,7 +89,7 @@ export const UserSearchableList = ({ const fetchMembersFromBackend = useDebouncedCallback(async (query: string, ignoreMembers: User[]) => { const resultUsers = await searchRepository.searchByName(query, selfUser.teamId); const selfTeamId = selfUser.teamId; - const foundMembers = resultUsers.filter(user => user.teamId === selfTeamId); + const foundMembers = resultUsers.filter(user => user.teamId === selfTeamId && user.type === userType); const ignoreIds = ignoreMembers.map(member => member.id); const uniqueMembers = foundMembers.filter(member => !ignoreIds.includes(member.id)); @@ -108,10 +112,11 @@ export const UserSearchableList = ({ .searchUserInSet(filter, users) .filter( user => - user.isMe || - conversationState.hasConversationWith(user) || - teamRepository.isSelfConnectedTo(user.id) || - user.username() === normalizedQuery, + (user.isMe || + conversationState.hasConversationWith(user) || + teamRepository.isSelfConnectedTo(user.id) || + user.username() === normalizedQuery) && + user.type === userType, ); if (normalizedQuery !== '' && selfInTeam && allowRemoteSearch) { diff --git a/apps/webapp/src/script/components/toggle/InfoToggle.tsx b/apps/webapp/src/script/components/toggle/InfoToggle.tsx index 9f831d4ef04..41265ec002a 100644 --- a/apps/webapp/src/script/components/toggle/InfoToggle.tsx +++ b/apps/webapp/src/script/components/toggle/InfoToggle.tsx @@ -17,7 +17,7 @@ * */ -import {useId} from 'react'; +import React, {useId} from 'react'; import cx from 'classnames'; @@ -29,6 +29,7 @@ interface InfoToggleProps { name: string; className?: string; setIsChecked: (checked: boolean) => void; + label?: React.ReactNode; } const InfoToggle = ({ @@ -39,6 +40,7 @@ const InfoToggle = ({ isDisabled, name, setIsChecked, + label, }: InfoToggleProps) => { const dataUieNameInfoText = `status-info-toggle-${dataUieName}`; const dataUieNameLabelText = `do-toggle-${dataUieName}`; @@ -77,6 +79,7 @@ const InfoToggle = ({ + {label} ); }; diff --git a/apps/webapp/src/script/page/RightSidebar/AddParticipants/AddParticipants.tsx b/apps/webapp/src/script/page/RightSidebar/AddParticipants/AddParticipants.tsx index e89b1e97e3c..8d9b7758bba 100644 --- a/apps/webapp/src/script/page/RightSidebar/AddParticipants/AddParticipants.tsx +++ b/apps/webapp/src/script/page/RightSidebar/AddParticipants/AddParticipants.tsx @@ -19,15 +19,13 @@ import {FC, useMemo, useState} from 'react'; -import {CONVERSATION_PROTOCOL} from '@wireapp/api-client/lib/team'; +import {UserType} from '@wireapp/api-client/lib/user'; import cx from 'classnames'; -import {TabIndex, Button, ButtonVariant} from '@wireapp/react-ui-kit'; +import {Button, TabIndex} from '@wireapp/react-ui-kit'; import {FadingScrollbar} from 'Components/FadingScrollbar'; -import * as Icon from 'Components/Icon'; import {SearchInput} from 'Components/SearchInput'; -import {ServiceList} from 'Components/ServiceList/ServiceList'; import {UserSearchableList} from 'Components/UserSearchableList'; import {ConversationRepository} from 'Repositories/conversation/ConversationRepository'; import {Conversation} from 'Repositories/entity/Conversation'; @@ -118,10 +116,15 @@ const AddParticipants: FC = ({ const contacts = useMemo(() => { if (isTeam) { const isTeamOrServices = isTeamOnly || isServicesRoom; - return isTeamOrServices ? teamMembers.sort(sortUsersByPriority) : teamUsers; + // isTeamOnly is true if guests are not allowed in this conversation + // If guests are allowed teamUsers are loaded as they contain team members AND all users the logged in user is connected with + if (currentState === PARTICIPANTS_STATE.ADD_PEOPLE) { + return isTeamOrServices ? teamMembers.sort(sortUsersByPriority) : teamUsers; + } + return teamMembers.sort(sortUsersByPriority).filter(u => u.type === UserType.APP); } return connectedUsers; - }, [connectedUsers, isServicesRoom, isTeam, isTeamOnly, teamMembers, teamUsers]); + }, [connectedUsers, isServicesRoom, isTeam, isTeamOnly, teamMembers, teamUsers, currentState]); const enabledAddAction = selectedContacts.length > ENABLE_ADD_ACTIONS_LENGTH; @@ -134,14 +137,7 @@ const AddParticipants: FC = ({ const isService = !!firstUserEntity?.isService; const allowIntegrations = isGroupOrChannel || isService; - return ( - isTeam && - allowIntegrations && - inTeam && - !isTeamOnly && - isServicesEnabled && - activeConversation.protocol !== CONVERSATION_PROTOCOL.MLS - ); + return isTeam && allowIntegrations && inTeam && !isTeamOnly && isServicesEnabled; }, [ firstUserEntity?.isService, inTeam, @@ -155,19 +151,24 @@ const AddParticipants: FC = ({ const manageServicesUrl = getManageServicesUrl('client_landing'); const isSearching = searchInput.length > ENABLE_IS_SEARCHING_LENGTH; - const onAddPeople = () => setCurrentState(PARTICIPANTS_STATE.ADD_PEOPLE); - - const searchServices = async (value: string) => { - await integrationRepository.searchForServices(value); - setIsInitialServiceSearch(false); + const onAddPeople = () => { + setSelectedContacts([]); + setCurrentState(PARTICIPANTS_STATE.ADD_PEOPLE); }; const onAddServices = async () => { + setSelectedContacts([]); setCurrentState(PARTICIPANTS_STATE.ADD_SERVICE); await searchServices(searchInput); }; + const searchServices = async (value: string) => { + await integrationRepository.searchForServices(value); + + setIsInitialServiceSearch(false); + }; + const openManageServices = () => { if (manageServicesUrl) { safeWindowOpen(manageServicesUrl); @@ -179,6 +180,8 @@ const AddParticipants: FC = ({ const addUsers = async () => { const userEntities = selectedContacts.slice(); + userEntities.forEach(ue => console.log(`userEntity ${JSON.stringify(ue)}`)); + await conversationRepository.addUsers(activeConversation, userEntities); }; @@ -254,104 +257,101 @@ const AddParticipants: FC = ({ )} - {isAddPeopleState && ( - - )} - - {isAddServiceState && ( - <> - {!!services.length && ( - <> - {canManageServices() && !!manageServicesUrl && ( -
    -
  • - handleKeyDown({ - event, - callback: openManageServices, - keys: [KEY.ENTER, KEY.SPACE], - }) - } - data-uie-name="go-manage-services" - > -
    - -
    - -
    {t('addParticipantsManageServices')}
    -
  • -
- )} - - - - )} - - {!services.length && !isInitialServiceSearch && ( -
- - - {canManageServices() && !!manageServicesUrl && ( - <> -
- {t('addParticipantsNoServicesManager')} -
- - - - )} - - {!canManageServices() && ( -
- {t('addParticipantsNoServicesMember')} -
- )} -
- )} - - )} + + + {/*{isAddServiceState && (*/} + {/* <>*/} + {/* {!!services.length && (*/} + {/* <>*/} + {/* {canManageServices() && !!manageServicesUrl && (*/} + {/*
    */} + {/* */} + {/* handleKeyDown({*/} + {/* event,*/} + {/* callback: openManageServices,*/} + {/* keys: [KEY.ENTER, KEY.SPACE],*/} + {/* })*/} + {/* }*/} + {/* data-uie-name="go-manage-services"*/} + {/* >*/} + {/*
    */} + {/* */} + {/*
    */} + + {/*
    {t('addParticipantsManageServices')}
    */} + {/* */} + {/*
*/} + {/* )}*/} + + {/* */} + {/* */} + {/* )}*/} + + {/* {!services.length && !isInitialServiceSearch && (*/} + {/*
*/} + {/* */} + + {/* {canManageServices() && !!manageServicesUrl && (*/} + {/* <>*/} + {/*
*/} + {/* {t('addParticipantsNoServicesManager')}*/} + {/*
*/} + + {/* */} + {/* handleKeyDown({*/} + {/* event,*/} + {/* callback: openManageServices,*/} + {/* keys: [KEY.ENTER, KEY.SPACE],*/} + {/* })*/} + {/* }*/} + {/* data-uie-name="go-enable-services"*/} + {/* style={{marginTop: '1em'}}*/} + {/* >*/} + {/* {t('addParticipantsManageServicesNoResults')}*/} + {/* */} + {/* */} + {/* )}*/} + + {/* {!canManageServices() && (*/} + {/*
*/} + {/* {t('addParticipantsNoServicesMember')}*/} + {/*
*/} + {/* )}*/} + {/*
*/} + {/* )}*/} + {/* */} + {/*)}*/}
- {isAddPeopleState && ( -
- -
- )} +
+ +
); diff --git a/apps/webapp/src/script/page/RightSidebar/ConversationDetails/components/ConversationDetailsOptions/ConversationDetailsOptions.tsx b/apps/webapp/src/script/page/RightSidebar/ConversationDetails/components/ConversationDetailsOptions/ConversationDetailsOptions.tsx index d9882526793..3ce6ed47339 100644 --- a/apps/webapp/src/script/page/RightSidebar/ConversationDetails/components/ConversationDetailsOptions/ConversationDetailsOptions.tsx +++ b/apps/webapp/src/script/page/RightSidebar/ConversationDetails/components/ConversationDetailsOptions/ConversationDetailsOptions.tsx @@ -29,7 +29,7 @@ import {PanelActions} from 'Components/panel/PanelActions'; import {ReceiptModeToggle} from 'Components/toggle/ReceiptModeToggle'; import {ConversationRepository} from 'Repositories/conversation/ConversationRepository'; import {ConversationRoleRepository} from 'Repositories/conversation/ConversationRoleRepository'; -import {isGroupMLSConversation, isMLSConversation} from 'Repositories/conversation/ConversationSelectors'; +import {isGroupMLSConversation} from 'Repositories/conversation/ConversationSelectors'; import {Conversation} from 'Repositories/entity/Conversation'; import {User} from 'Repositories/entity/User'; import {TeamState} from 'Repositories/team/TeamState'; @@ -126,7 +126,8 @@ const ConversationDetailsOptions = ({ const showOptionGuests = isActiveGroupParticipant && isTeamConversation; const showOptionNotificationsGroup = isMutable && isGroupOrChannel; const showOptionTimedMessages = isActiveGroupParticipant && isSelfDeletingMessagesEnabled; - const showOptionServices = isActiveGroupParticipant && isTeamConversation && !isMLSConversation(activeConversation); + //TODO: Actually check for apps and services availability + const showOptionServices = isActiveGroupParticipant && isTeamConversation; // && !isMLSConversation(activeConversation); const showOptionNotifications1To1 = isMutable && !isGroupOrChannel; const showOptionReadReceipts = isTeamConversation && !isGroupMLSConversation(activeConversation); const showChannelOptions = isChannel && isChannelsEnabled; diff --git a/apps/webapp/src/script/repositories/entity/User/User.ts b/apps/webapp/src/script/repositories/entity/User/User.ts index cded53951ce..c69ba7d3412 100644 --- a/apps/webapp/src/script/repositories/entity/User/User.ts +++ b/apps/webapp/src/script/repositories/entity/User/User.ts @@ -19,7 +19,7 @@ import {ConnectionStatus} from '@wireapp/api-client/lib/connection/'; import {CONVERSATION_PROTOCOL} from '@wireapp/api-client/lib/team'; -import {QualifiedId} from '@wireapp/api-client/lib/user'; +import {QualifiedId, UserType} from '@wireapp/api-client/lib/user'; import {amplify} from 'amplify'; import ko from 'knockout'; @@ -104,6 +104,7 @@ export class User { public domain: string; public readonly isBlockedLegalHold: ko.PureComputed; public readonly supportedProtocols: ko.Observable; + public type: UserType | undefined; public static get ACCENT_COLOR() { return { diff --git a/apps/webapp/src/script/repositories/user/UserMapper.ts b/apps/webapp/src/script/repositories/user/UserMapper.ts index 399ebba7fb6..f1e78b704f7 100644 --- a/apps/webapp/src/script/repositories/user/UserMapper.ts +++ b/apps/webapp/src/script/repositories/user/UserMapper.ts @@ -108,6 +108,7 @@ export class UserMapper { service, team: teamId, supported_protocols: supportedProtocols, + type, } = userData; if (accentId) { @@ -181,6 +182,8 @@ export class UserMapper { userEntity.isDeleted = true; } + userEntity.type = type; + return userEntity; } } diff --git a/apps/webapp/src/types/i18n.d.ts b/apps/webapp/src/types/i18n.d.ts index 25a858c12dd..b6b94385073 100644 --- a/apps/webapp/src/types/i18n.d.ts +++ b/apps/webapp/src/types/i18n.d.ts @@ -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`; @@ -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.`; @@ -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`; @@ -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.`; @@ -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.`; @@ -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`; diff --git a/libraries/api-client/src/user/User.ts b/libraries/api-client/src/user/User.ts index 1516d193ae5..68cff186166 100644 --- a/libraries/api-client/src/user/User.ts +++ b/libraries/api-client/src/user/User.ts @@ -26,6 +26,12 @@ import {Picture} from '../self/'; import {CONVERSATION_PROTOCOL} from '../team'; import {UserAsset} from '../user/'; +export enum UserType { + REGULAR = 'regular', + APP = 'app', + BOT = 'bot', +} + export interface User { accent_id?: AccentColor.AccentColorID; assets?: UserAsset[]; @@ -42,4 +48,5 @@ export interface User { team?: string; searchable?: boolean; supported_protocols?: CONVERSATION_PROTOCOL[]; + type: UserType; } diff --git a/libraries/core/src/conversation/ConversationService/ConversationService.ts b/libraries/core/src/conversation/ConversationService/ConversationService.ts index 546f835a896..032e886b08e 100644 --- a/libraries/core/src/conversation/ConversationService/ConversationService.ts +++ b/libraries/core/src/conversation/ConversationService/ConversationService.ts @@ -444,6 +444,9 @@ export class ConversationService extends TypedEventEmitter { groupId, conversationId, }: Required & {shouldRetry?: boolean}): Promise { + console.log( + `addUsersToMLSConversation(qualifiedUsers=${JSON.stringify(qualifiedUsers)}, groupId=${groupId}, conversationId=${conversationId})`, + ); return this.MLSRecoveryOrchestrator.execute({ context: {operationName: OperationName.addUsers, qualifiedConversationId: conversationId, groupId}, callBack: () => this.performAddUsersToMLSConversationAPI({qualifiedUsers, groupId, conversationId}),