From f07963f93083d74eac153b6c4bc7284fcc2d80b3 Mon Sep 17 00:00:00 2001 From: Donald Green Date: Tue, 8 Oct 2019 08:13:35 -0700 Subject: [PATCH] Bug fixes for bell configuration (#3850) * 1. Restored the ability to turn the "bell" sound on and off using the "bell" config parameter. 2. Restored the ability to change the bell sound by providing a URL. These changes allow for a web url or local absolute file path to an audio file. The goal with these changes was to fix the issue causing the bell to never sound due to a difference in the underlying terminal emulators configurations from the previous one. While in the area, also decided to make sure that the sound can be changed by supplying a web url to an audio file or an absolute path to an audio file within the local machine * Code style changes * Code style changes * 1. More code style changes * 1. Spacing changes to try and abide by the linter * 1. Applied all suggested changes by eslint * 1. Removed functionality to specify a remote url to set a sound file for the bell sound. The amount of effort for handling when there is no internet connection, queuing and so forth wasn't worth keeping the feature. It is likely that the url could be used to download the file in which the user would be able to specify the file path tho this download file. 2. Created a new property that gets passed down from the terms container all the way to the individual term. We want to be able to evaluate if the bellSoundURL has changed to determine if we really need to read the sound file. 3. Moved logic to read the audio file into the main process. Setup a new action in the 'actions/ui' that will update the bell sound when it is finished and ready. This should prevent blocking the terminal from loading and thus increasing loading times. * 1. Modified the file reading method to be more generic to increase reusability. 2. Updated the "arrBuf2Base64" method to utilize the node Buffer class which helped to reduce some complexity and seems to run more efficiently. 3. Removed the CONFIG_ASYNC action and reducer in favor of reusing CONFIG_RELOAD when the process is finished reading the file for the bell sound. In order to achieve this, we had to merge the config from "config.getConfig()" method with the "bellSound" property before dispatching to "reloadConfig". * 1. Removed reference to now removed method * 1. Removed the arrBuf2Base64 as it seemed unnecessary now that the function would be reduced to a single line. Moved the one-liner into file.js. Removed references to arrBuf2Base64 method. 2. Refactored the logic that handles reloading the config when it has been updated to fix an issue that would set the bell sound back to the default sound when the config is saved without changing the value of "bellSoundURL". Setup now to either read file and reload the config, or reuse the "bellSound" value saved in state and reload the config. This removes an inefficiency with the reloadConfig being dispatched twice when "bellSoundURL" has changed as well. * 1. Removed a file that contained a single function, referenced in only one place that is performing a fairly simple task. 2. Updated the "getBase64FileData" method to use "Buffer.from()" instead of "Buffer()" due to messages stating that "Buffer()" is deprecated due to security and usability issues. * Adjustments and regression issues fixed 1. Updated the default config file to better explain the supported options for the "bell" config property. 2. Rearranged the bellSoundURL default property to make it easier to find should one decide to change the bell sound. 3. Typos fixed in comments. 4. Update fetchFileData to utilize the configData provided as function argument. There appeared to be no reason to reference different sources of config data within the same method. * 1. Removed the "BELL_STYLE" constant since it was only being used in one place. 2. Updated comment block to accurately reflect the current logic and made the comment much more concise. * 1. Add null safety check on `configInfo.bell` if the config file does not contain this property. 2. Default bellSound to null if the file specified by the filepath in `configInfo.bellSoundUrl` failed to be read. This helps prevent a repeated error being thrown by the xterm instance. 3. Updated `term.js` to use componentDidUpdate in place of the UNSAFE lifcycle method `componentWillReceiveProps`. I found that this unsafe lifecycle was never being fired, which lead to the terminal never responding to configuration changes. Without this change, the custom bell sound was never being loaded into the xterm instance. --- lib/components/term.js | 59 ++++++++++++++++++++++++++++++++++++++++++ lib/index.js | 4 +-- lib/utils/file.ts | 2 +- 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/lib/components/term.js b/lib/components/term.js index 3441b375..7780ab55 100644 --- a/lib/components/term.js +++ b/lib/components/term.js @@ -298,6 +298,65 @@ export default class Term extends React.PureComponent { return !e.catched; } + componentDidUpdate(prevProps) { + if (!prevProps.cleared && this.props.cleared) { + this.clear(); + } + + const nextTermOptions = getTermOptions(this.props); + + // Use bellSound in nextProps if it exists + // otherwise use the default sound found in xterm. + nextTermOptions.bellSound = this.props.bellSound || this.termDefaultBellSound; + + if (!prevProps.search && this.props.search) { + this.search(); + } + + // Update only options that have changed. + Object.keys(nextTermOptions) + .filter(option => option !== 'theme' && nextTermOptions[option] !== this.termOptions[option]) + .forEach(option => { + try { + this.term.setOption(option, nextTermOptions[option]); + } catch (e) { + if (/The webgl renderer only works with the webgl char atlas/i.test(e.message)) { + // Ignore this because the char atlas will also be changed + } else { + throw e; + } + } + }); + + // Do we need to update theme? + const shouldUpdateTheme = + !this.termOptions.theme || + nextTermOptions.rendererType !== this.termOptions.rendererType || + Object.keys(nextTermOptions.theme).some( + option => nextTermOptions.theme[option] !== this.termOptions.theme[option] + ); + if (shouldUpdateTheme) { + this.term.setOption('theme', nextTermOptions.theme); + } + + this.termOptions = nextTermOptions; + + if ( + this.props.fontSize !== prevProps.fontSize || + this.props.fontFamily !== prevProps.fontFamily || + this.props.lineHeight !== prevProps.lineHeight || + this.props.letterSpacing !== prevProps.letterSpacing + ) { + // resize to fit the container + this.fitResize(); + } + + if (prevProps.rows !== this.props.rows || prevProps.cols !== this.props.cols) { + this.resize(this.props.cols, this.props.rows); + } + } + + //TODO: Remove usage of legacy and soon deprecated lifecycle methods UNSAFE_componentWillReceiveProps(nextProps) { if (!this.props.cleared && nextProps.cleared) { this.clear(); diff --git a/lib/index.js b/lib/index.js index 7fa161a8..de53c0f9 100644 --- a/lib/index.js +++ b/lib/index.js @@ -33,14 +33,14 @@ window.__defineGetter__('plugins', () => plugins); const fetchFileData = configData => { const configInfo = Object.assign({}, configData, {bellSound: null}); - if (configInfo.bell.toUpperCase() !== 'SOUND' || !configInfo.bellSoundURL) { + if (!configInfo.bell || configInfo.bell.toUpperCase() !== 'SOUND' || !configInfo.bellSoundURL) { store_.dispatch(reloadConfig(configInfo)); return; } getBase64FileData(configInfo.bellSoundURL).then(base64FileData => { // prepend "base64," to the result of this method in order for this to work properly within xterm.js - const bellSound = 'base64,' + base64FileData; + const bellSound = !base64FileData ? null : 'base64,' + base64FileData; configInfo.bellSound = bellSound; store_.dispatch(reloadConfig(configInfo)); }); diff --git a/lib/utils/file.ts b/lib/utils/file.ts index 710aa730..247c7e7f 100644 --- a/lib/utils/file.ts +++ b/lib/utils/file.ts @@ -21,7 +21,7 @@ export function isExecutable(fileStat: Stats): boolean { } export function getBase64FileData(filePath: string): Promise { - return new Promise(resolve => { + return new Promise((resolve): void => { return fs.readFile(filePath, (err, data) => { if (err) { // Gracefully fail with a warning