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
7 changes: 7 additions & 0 deletions apps/webapp/src/script/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ const Config = {

return window.desktopAppConfig;
},
getDesktopSettings: () => {
if (!Runtime.isDesktopApp) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
if (!Runtime.isDesktopApp) {
if (!Runtime.isDesktopApp()) {

return undefined;
}

return window.desktopAppSettings;
},
};

export {Config};
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {ChangeEvent, useCallback, useEffect, useRef, useState} from 'react';
import type {WebappProperties} from '@wireapp/api-client/lib/user/data/';
import {amplify} from 'amplify';

import {Runtime} from '@wireapp/commons';
import {Checkbox, CheckboxLabel} from '@wireapp/react-ui-kit';
import {WebAppEvents} from '@wireapp/webapp-events';

Expand All @@ -30,6 +31,8 @@ import type {PropertiesRepository} from 'Repositories/properties/PropertiesRepos
import {PROPERTIES_TYPE} from 'Repositories/properties/PropertiesType';
import {t} from 'Util/LocalizerUtil';

import {HardwareAccelerationRestartModal} from './HardwareAccelerationRestartModal';

import {Config} from '../../../../../Config';
import {PreferencesSection} from '../components/PreferencesSection';

Expand All @@ -54,6 +57,21 @@ const CallOptions = ({constraintsHandler, propertiesRepository}: CallOptionsProp
!!propertiesRepository.properties.settings.call.enable_press_space_to_unmute,
);

const desktopSettings = Config.getDesktopSettings();

const isHardwareAccelerationChangeable = Runtime.isDesktopApp() && !!desktopSettings;

const [showHwRestartModal, setShowHwRestartModal] = useState(false);
const [pendingHwValue, setPendingHwValue] = useState<boolean | null>(null);

const [hardwareAccelerationEnabled, setHardwareAccelerationEnabled] = useState<boolean>(() => {
if (!isHardwareAccelerationChangeable) {
return true; // default in browser (but not changeable)
}

return desktopSettings.isHardwareAccelerationEnabled();
});

useEffect(() => {
const updateProperties = ({settings}: WebappProperties) => {
setVbrEncoding(!isCbrEncodingEnforced && settings.call.enable_vbr_encoding);
Expand Down Expand Up @@ -102,6 +120,32 @@ const CallOptions = ({constraintsHandler, propertiesRepository}: CallOptionsProp
[propertiesRepository],
);

const handleHardwareAccelerationChange = useCallback((event: ChangeEvent<HTMLInputElement>) => {
const isChecked = event.target.checked;

setPendingHwValue(isChecked);
setShowHwRestartModal(true);
}, []);

const confirmHardwareAccelerationChange = () => {
if (!desktopSettings || pendingHwValue === null) {
setShowHwRestartModal(false);
return;
}

desktopSettings.setHardwareAccelerationEnabled(pendingHwValue);
setHardwareAccelerationEnabled(pendingHwValue);

setShowHwRestartModal(false);

amplify.publish(WebAppEvents.LIFECYCLE.RESTART);
Copy link
Contributor

Choose a reason for hiding this comment

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

if restart is delayed then pendingHwValue will be stale

Suggested change
amplify.publish(WebAppEvents.LIFECYCLE.RESTART);
amplify.publish(WebAppEvents.LIFECYCLE.RESTART);
setPendingHwValue(null);

};

const cancelHardwareAccelerationChange = () => {
setShowHwRestartModal(false);
setPendingHwValue(null);
};

return (
<PreferencesSection title={t('preferencesOptionsCall')}>
<div>
Expand Down Expand Up @@ -153,6 +197,29 @@ const CallOptions = ({constraintsHandler, propertiesRepository}: CallOptionsProp
</p>
</div>
)}

{isHardwareAccelerationChangeable && (
<div className="checkbox-margin">
<Checkbox
onChange={handleHardwareAccelerationChange}
checked={hardwareAccelerationEnabled}
data-uie-name="status-preference-hardware-acceleration"
Copy link
Contributor

Choose a reason for hiding this comment

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

the htmlFor attribute in the CheckboxLabel looks for id in the Checkbox

Suggested change
data-uie-name="status-preference-hardware-acceleration"
id="status-preference-hardware-acceleration" data-uie-name="status-preference-hardware-acceleration"

>
<CheckboxLabel htmlFor="status-preference-hardware-acceleration">
{t('preferencesOptionsEnableHardwareAcceleration')}
</CheckboxLabel>
</Checkbox>
<p className="preferences-detail preferences-detail-intended">
{t('preferencesOptionsEnableHardwareAccelerationDetails')}
</p>
</div>
)}

<HardwareAccelerationRestartModal
isShown={showHwRestartModal}
onCancel={cancelHardwareAccelerationChange}
onConfirm={confirmHardwareAccelerationChange}
/>
</PreferencesSection>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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 {ModalComponent} from 'Components/Modals/ModalComponent';
import {t} from 'Util/LocalizerUtil';

interface HardwareAccelerationRestartModalProps {
isShown: boolean;
onCancel: () => void;
onConfirm: () => void;
}

const HardwareAccelerationRestartModal = ({isShown, onCancel, onConfirm}: HardwareAccelerationRestartModalProps) => {
return (
<ModalComponent isShown={isShown} onBgClick={onCancel}>
<div style={{padding: 24, maxWidth: 420}}>
<h3>{t('preferencesOptionsEnableHardwareAccelerationModalTitle')}</h3>

<p>{t('preferencesOptionsEnableHardwareAccelerationModalMessage')}</p>

<div style={{display: 'flex', justifyContent: 'flex-end', gap: 12}}>
<button onClick={onCancel}>{t('preferencesOptionsEnableHardwareAccelerationModalCancel')}</button>

<button onClick={onConfirm}>{t('preferencesOptionsEnableHardwareAccelerationModalOk')}</button>
</div>
</div>
</ModalComponent>
);
};

export {HardwareAccelerationRestartModal};
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ declare global {
interface Window {
openGraphAsync?: (url: string) => Promise<OpenGraphResult>;
desktopAppConfig?: {version: string; supportsCallingPopoutWindow?: boolean};
desktopAppSettings?: {
setHardwareAccelerationEnabled: (enabled: boolean) => void;
isHardwareAccelerationEnabled: () => boolean;
};
}
}
const logger = getLogger('LinkPreviewRepository');
Expand Down
20 changes: 13 additions & 7 deletions apps/webapp/src/types/i18n.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -717,13 +717,6 @@ declare module 'I18n/en-US.json' {
'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 @@ -1692,6 +1685,12 @@ declare module 'I18n/en-US.json' {
'preferencesOptionsEmojiReplaceDetail': `:-) → [icon]`;
'preferencesOptionsEnableAgcCheckbox': `Automatic gain control (AGC)`;
'preferencesOptionsEnableAgcDetails': `Enable to allow your microphone volume to be adjusted automatically to ensure all participants in a call are heard with similar and comfortable loudness.`;
'preferencesOptionsEnableHardwareAcceleration': `Enable Hardware Acceleration (Recommended)`;
Copy link
Contributor

Choose a reason for hiding this comment

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

These keys must also be added in the en-US.json

'preferencesOptionsEnableHardwareAccelerationDetails': `When enabled, hardware acceleration allows the webcam to use your device’s GPU to improve video performance and reduce CPU usage. This can result in smoother video playback, better responsiveness, and improved overall stability.\nThis option is enabled by default and is recommended for most systems. Disable it only if you experience compatibility or display issues.\nA restart of the application is required for changes to take effect.`;
'preferencesOptionsEnableHardwareAccelerationModalTitle': `Restart required`;
'preferencesOptionsEnableHardwareAccelerationModalMessage': `Changing the hardware acceleration setting requires a restart of the application. The app will close and reopen automatically. Do you want to restart now?`;
'preferencesOptionsEnableHardwareAccelerationModalCancel': `Cancel`;
'preferencesOptionsEnableHardwareAccelerationModalOk': `Restart now`;
'preferencesOptionsEnablePressSpaceToUnmute': `Unmute with space bar`;
'preferencesOptionsEnablePressSpaceToUnmuteDetails': `Enable to unmute your microphone by pressing and holding the space bar as long as you want to speak. You can use this option in full view.`;
'preferencesOptionsEnableSoundlessIncomingCalls': `Silence other calls`;
Expand Down Expand Up @@ -1798,6 +1797,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 +1816,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 +1827,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 +1838,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 Down Expand Up @@ -1885,6 +1889,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