New: Improve Plugin Installation and Removal Process
Fixes restart loops reduces github bans improves UX with messaging for restart improves version notes
This commit is contained in:
@@ -7,11 +7,13 @@ import FormInputGroup from 'Components/Form/FormInputGroup';
|
||||
import FormLabel from 'Components/Form/FormLabel';
|
||||
import SpinnerButton from 'Components/Link/SpinnerButton';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import ConfirmModal from 'Components/Modal/ConfirmModal';
|
||||
import PageContent from 'Components/Page/PageContent';
|
||||
import PageContentBody from 'Components/Page/PageContentBody';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import { inputTypes, kinds } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import PluginRow from './PluginRow';
|
||||
|
||||
const columns = [
|
||||
@@ -78,7 +80,15 @@ class Plugins extends Component {
|
||||
isInstallingPlugin,
|
||||
onInstallPluginPress,
|
||||
isUninstallingPlugin,
|
||||
onUninstallPluginPress
|
||||
onUninstallPluginPress,
|
||||
isRestartRequiredModalOpen,
|
||||
onCloseRestartRequiredModal,
|
||||
pluginOwner,
|
||||
pluginName,
|
||||
pluginVersion,
|
||||
pluginAction,
|
||||
pluginDetailsUrl,
|
||||
pluginBranch
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
@@ -87,6 +97,23 @@ class Plugins extends Component {
|
||||
|
||||
const noPlugins = isPopulated && !error && !items.length;
|
||||
|
||||
// Build modal title and message
|
||||
let modalTitle = translate('RestartRequired');
|
||||
let modalMessage = translate('LidarrRequiresRestartToApplyPluginChanges');
|
||||
|
||||
if (pluginOwner && pluginName && pluginAction) {
|
||||
const versionText = pluginVersion ? ` v${pluginVersion}` : '';
|
||||
const branchText = pluginBranch ? ` (${pluginBranch})` : '';
|
||||
const actionText = pluginAction === 'install' ? 'installed' : 'uninstalled';
|
||||
modalTitle = `Plugin ${actionText} - ${translate('RestartRequired')}`;
|
||||
|
||||
if (pluginDetailsUrl) {
|
||||
modalMessage = `Plugin: [${pluginOwner}/${pluginName}]${versionText}${branchText}\n\nInstalled from:\n${pluginDetailsUrl}\n\nPlease restart Lidarr to apply changes.`;
|
||||
} else {
|
||||
modalMessage = `Plugin: [${pluginOwner}/${pluginName}]${versionText}${branchText}\n\nPlugin ${actionText} successfully.\n\nPlease restart Lidarr to apply changes.`;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<PageContent title="Plugins">
|
||||
<PageContentBody>
|
||||
@@ -148,6 +175,17 @@ class Plugins extends Component {
|
||||
</Table>
|
||||
}
|
||||
</FieldSet>
|
||||
|
||||
<ConfirmModal
|
||||
isOpen={isRestartRequiredModalOpen}
|
||||
kind={kinds.INFO}
|
||||
title={modalTitle}
|
||||
message={modalMessage}
|
||||
confirmLabel={translate('Ok')}
|
||||
hideCancelButton={true}
|
||||
onConfirm={onCloseRestartRequiredModal}
|
||||
onCancel={onCloseRestartRequiredModal}
|
||||
/>
|
||||
</PageContentBody>
|
||||
</PageContent>
|
||||
);
|
||||
@@ -162,7 +200,15 @@ Plugins.propTypes = {
|
||||
isInstallingPlugin: PropTypes.bool.isRequired,
|
||||
onInstallPluginPress: PropTypes.func.isRequired,
|
||||
isUninstallingPlugin: PropTypes.bool.isRequired,
|
||||
onUninstallPluginPress: PropTypes.func.isRequired
|
||||
onUninstallPluginPress: PropTypes.func.isRequired,
|
||||
isRestartRequiredModalOpen: PropTypes.bool,
|
||||
onCloseRestartRequiredModal: PropTypes.func,
|
||||
pluginOwner: PropTypes.string,
|
||||
pluginName: PropTypes.string,
|
||||
pluginVersion: PropTypes.string,
|
||||
pluginAction: PropTypes.string,
|
||||
pluginDetailsUrl: PropTypes.string,
|
||||
pluginBranch: PropTypes.string
|
||||
};
|
||||
|
||||
export default Plugins;
|
||||
|
||||
@@ -38,6 +38,20 @@ class PluginsConnector extends Component {
|
||||
//
|
||||
// Lifecycle
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
this.state = {
|
||||
isRestartRequiredModalOpen: false,
|
||||
pluginOwner: '',
|
||||
pluginName: '',
|
||||
pluginVersion: '',
|
||||
pluginAction: '',
|
||||
pluginDetailsUrl: '',
|
||||
pluginBranch: ''
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
registerPagePopulator(this.repopulate);
|
||||
|
||||
@@ -59,27 +73,108 @@ class PluginsConnector extends Component {
|
||||
// Listeners
|
||||
|
||||
onInstallPluginPress = (url) => {
|
||||
this.currentPluginOperation = { action: 'install', url };
|
||||
this.props.dispatchExecuteCommand({
|
||||
name: commandNames.INSTALL_PLUGIN,
|
||||
githubUrl: url
|
||||
githubUrl: url,
|
||||
commandFinished: this.onPluginCommandFinished
|
||||
});
|
||||
};
|
||||
|
||||
onUninstallPluginPress = (url) => {
|
||||
this.currentPluginOperation = { action: 'uninstall', url };
|
||||
this.props.dispatchExecuteCommand({
|
||||
name: commandNames.UNINSTALL_PLUGIN,
|
||||
githubUrl: url
|
||||
githubUrl: url,
|
||||
commandFinished: this.onPluginCommandFinished
|
||||
});
|
||||
};
|
||||
|
||||
onPluginCommandFinished = (command) => {
|
||||
let pluginOwner = '';
|
||||
let pluginName = '';
|
||||
let pluginVersion = '';
|
||||
let pluginAction = '';
|
||||
let pluginDetailsUrl = '';
|
||||
let pluginBranch = '';
|
||||
|
||||
if (this.currentPluginOperation && command) {
|
||||
const url = this.currentPluginOperation.url;
|
||||
|
||||
const match = url.match(/github\.com\/([^/]+)\/([^/]+)/);
|
||||
if (match) {
|
||||
[, pluginOwner, pluginName] = match;
|
||||
pluginAction = this.currentPluginOperation.action;
|
||||
|
||||
// Extract branch from GitHub URL
|
||||
if (url.includes('/tree/')) {
|
||||
const branchMatch = url.match(/\/tree\/([^/]+)/);
|
||||
if (branchMatch) {
|
||||
pluginBranch = branchMatch[1];
|
||||
}
|
||||
}
|
||||
|
||||
if (this.currentPluginOperation.action === 'install') {
|
||||
if (command && command.message) {
|
||||
const pluginMatch = command.message.match(/Plugin \[([^/]+)\/([^\]]+)\] v([0-9.]+) installed/);
|
||||
if (pluginMatch) {
|
||||
pluginVersion = pluginMatch[3];
|
||||
}
|
||||
console.log('Plugin match result:', pluginMatch);
|
||||
}
|
||||
pluginDetailsUrl = url;
|
||||
} else {
|
||||
if (command && command.message) {
|
||||
const pluginMatch = command.message.match(/Plugin \[([^/]+)\/([^\]]+)\] v([0-9.]+) uninstalled/);
|
||||
if (pluginMatch) {
|
||||
pluginVersion = pluginMatch[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isRestartRequiredModalOpen: true,
|
||||
pluginOwner,
|
||||
pluginName,
|
||||
pluginVersion,
|
||||
pluginAction,
|
||||
pluginDetailsUrl,
|
||||
pluginBranch
|
||||
});
|
||||
this.repopulate();
|
||||
};
|
||||
|
||||
onCloseRestartRequiredModal = () => {
|
||||
this.setState({
|
||||
isRestartRequiredModalOpen: false,
|
||||
pluginOwner: '',
|
||||
pluginName: '',
|
||||
pluginVersion: '',
|
||||
pluginAction: '',
|
||||
pluginDetailsUrl: '',
|
||||
pluginBranch: ''
|
||||
});
|
||||
this.currentPluginOperation = null;
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Plugins
|
||||
isRestartRequiredModalOpen={this.state.isRestartRequiredModalOpen}
|
||||
pluginOwner={this.state.pluginOwner}
|
||||
pluginName={this.state.pluginName}
|
||||
pluginVersion={this.state.pluginVersion}
|
||||
pluginAction={this.state.pluginAction}
|
||||
pluginDetailsUrl={this.state.pluginDetailsUrl}
|
||||
pluginBranch={this.state.pluginBranch}
|
||||
onInstallPluginPress={this.onInstallPluginPress}
|
||||
onUninstallPluginPress={this.onUninstallPluginPress}
|
||||
onCloseRestartRequiredModal={this.onCloseRestartRequiredModal}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
@@ -89,7 +184,8 @@ class PluginsConnector extends Component {
|
||||
|
||||
PluginsConnector.propTypes = {
|
||||
dispatchFetchInstalledPlugins: PropTypes.func.isRequired,
|
||||
dispatchExecuteCommand: PropTypes.func.isRequired
|
||||
dispatchExecuteCommand: PropTypes.func.isRequired,
|
||||
items: PropTypes.array
|
||||
};
|
||||
|
||||
export default connect(createMapStateToProps, mapDispatchToProps)(PluginsConnector);
|
||||
|
||||
Reference in New Issue
Block a user