This repository has been archived by the owner on Jul 19, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 194
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
re-enabled plugin & rename 'gen code' to 'show curl, etc'
- Loading branch information
1 parent
979cf04
commit 989edf6
Showing
5 changed files
with
281 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
271 changes: 271 additions & 0 deletions
271
packages/insomnia/src/ui/components/settings/plugins.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,271 @@ | ||
import * as path from 'path'; | ||
import React, { FC, useEffect, useState } from 'react'; | ||
import { useRouteLoaderData } from 'react-router-dom'; | ||
|
||
import { | ||
NPM_PACKAGE_BASE, | ||
PLUGIN_HUB_BASE, | ||
} from '../../../common/constants'; | ||
import { docsPlugins } from '../../../common/documentation'; | ||
import { createPlugin } from '../../../plugins/create'; | ||
import type { Plugin } from '../../../plugins/index'; | ||
import { getPlugins } from '../../../plugins/index'; | ||
import { reload } from '../../../templating/index'; | ||
import { useSettingsPatcher } from '../../hooks/use-request'; | ||
import { RootLoaderData } from '../../routes/root'; | ||
import { CopyButton } from '../base/copy-button'; | ||
import { Link } from '../base/link'; | ||
import { HelpTooltip } from '../help-tooltip'; | ||
import { showAlert, showPrompt } from '../modals'; | ||
import { Button } from '../themed-button'; | ||
interface State { | ||
plugins: Plugin[]; | ||
npmPluginValue: string; | ||
error: Error | null; | ||
installPluginErrMsg: string; | ||
isInstallingFromNpm: boolean; | ||
isRefreshingPlugins: boolean; | ||
} | ||
export const Plugins: FC = () => { | ||
const [state, setState] = useState<State>({ | ||
plugins: [], | ||
npmPluginValue: '', | ||
error: null, | ||
installPluginErrMsg: '', | ||
isInstallingFromNpm: false, | ||
isRefreshingPlugins: false, | ||
}); | ||
const { | ||
plugins, | ||
error, | ||
installPluginErrMsg, | ||
isInstallingFromNpm, | ||
isRefreshingPlugins, | ||
npmPluginValue, | ||
} = state; | ||
const { | ||
settings, | ||
} = useRouteLoaderData('root') as RootLoaderData; | ||
|
||
useEffect(() => { | ||
refreshPlugins(); | ||
}, []); | ||
|
||
async function refreshPlugins() { | ||
setState(state => ({ ...state, isRefreshingPlugins: true })); | ||
// Get and reload plugins | ||
const plugins = await getPlugins(true); | ||
reload(); | ||
|
||
setState(state => ({ ...state, plugins, isRefreshingPlugins: false })); | ||
} | ||
const patchSettings = useSettingsPatcher(); | ||
|
||
return ( | ||
<div> | ||
<p className="notice info no-margin-top"> | ||
Plugins is still an experimental feature. See{' '} | ||
<Link href={docsPlugins}>Documentation</Link> for more info. | ||
</p> | ||
{plugins.length === 0 ? ( | ||
<div className="text-center faint italic pad">No Plugins Added</div> | ||
) : ( | ||
<table className="table--fancy table--striped table--valign-middle margin-top margin-bottom"> | ||
<thead> | ||
<tr> | ||
<th>Enable?</th> | ||
<th>Name</th> | ||
<th>Version</th> | ||
<th>Folder</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{plugins.map(plugin => { | ||
const link = path.join(/^insomnia-plugin-/.test(plugin.name) ? PLUGIN_HUB_BASE : NPM_PACKAGE_BASE, plugin.name); | ||
return !plugin.directory ? null : ( | ||
<tr key={plugin.name}> | ||
<td style={{ width: '4rem' }}> | ||
<input | ||
type="checkbox" | ||
checked={!plugin.config.disabled} | ||
disabled={isRefreshingPlugins} | ||
onChange={async event => { | ||
const newConfig = { ...plugin.config, disabled: !event.target.checked }; | ||
setState(state => ({ ...state, isRefreshingPlugins: true })); | ||
patchSettings({ pluginConfig: { ...settings.pluginConfig, [plugin.name]: newConfig } }); | ||
refreshPlugins(); | ||
}} | ||
/> | ||
</td> | ||
<td> | ||
{plugin.name} | ||
{plugin.description && ( | ||
<HelpTooltip info className="space-left"> | ||
{plugin.description} | ||
</HelpTooltip> | ||
)} | ||
</td> | ||
<td> | ||
{plugin.version} | ||
<a className="space-left" href={link} title={link}> | ||
<i className="fa fa-external-link-square" /> | ||
</a> | ||
</td> | ||
<td | ||
className="no-wrap" | ||
style={{ | ||
width: '10rem', | ||
}} | ||
> | ||
<CopyButton | ||
size="small" | ||
variant="contained" | ||
title={plugin.directory} | ||
content={plugin.directory} | ||
> | ||
Copy Path | ||
</CopyButton>{' '} | ||
<Button | ||
size="small" | ||
variant="contained" | ||
onClick={() => window.shell.showItemInFolder(plugin.directory)} | ||
> | ||
Reveal Folder | ||
</Button> | ||
</td> | ||
</tr> | ||
); | ||
} | ||
)} | ||
</tbody> | ||
</table> | ||
)} | ||
|
||
{error && ( | ||
<div className="notice error text-left margin-bottom"> | ||
<button className="pull-right icon" onClick={() => setState(state => ({ ...state, error: null }))}> | ||
<i className="fa fa-times" /> | ||
</button> | ||
<div className="selectable force-pre-wrap"> | ||
<b>{installPluginErrMsg}</b> | ||
{'\n\nThere may be an issue with the plugin itself, as a note you can discover and install plugins from the '} | ||
<a href={PLUGIN_HUB_BASE}>Plugin Hub.</a> | ||
<details> | ||
<summary>Additional Information</summary> | ||
<pre className="pad-top-sm force-wrap selectable"> | ||
<code>{error.stack || error.message}</code> | ||
</pre> | ||
</details> | ||
</div> | ||
</div> | ||
)} | ||
|
||
<form | ||
onSubmit={async event => { | ||
event.preventDefault(); | ||
setState(state => ({ ...state, isInstallingFromNpm: true })); | ||
const newState: Partial<State> = { | ||
isInstallingFromNpm: false, | ||
error: null, | ||
installPluginErrMsg: '', | ||
}; | ||
try { | ||
await window.main.installPlugin(npmPluginValue.trim()); | ||
await refreshPlugins(); | ||
newState.npmPluginValue = ''; // Clear input if successful install | ||
} catch (err) { | ||
newState.installPluginErrMsg = `Failed to install ${npmPluginValue}`; | ||
newState.error = err; | ||
} | ||
setState(state => ({ ...state, ...newState })); | ||
}} | ||
> | ||
<div className="form-row"> | ||
<div className="form-control form-control--outlined"> | ||
<input | ||
onChange={event => { | ||
if (event.target instanceof HTMLInputElement) { | ||
setState(state => ({ ...state, npmPluginValue: event.target.value })); | ||
} | ||
}} | ||
disabled={isInstallingFromNpm} | ||
type="text" | ||
placeholder="npm-package-name" | ||
value={npmPluginValue} | ||
/> | ||
</div> | ||
<div className="form-control width-auto"> | ||
<Button variant="contained" bg="surprise" disabled={isInstallingFromNpm}> | ||
{isInstallingFromNpm && <i className="fa fa-refresh fa-spin space-right" />} | ||
Install Plugin | ||
</Button> | ||
</div> | ||
</div> | ||
</form> | ||
<hr /> | ||
<div className="text-right"> | ||
<Button | ||
onClick={() => window.main.openInBrowser(PLUGIN_HUB_BASE)} | ||
> | ||
Browse Plugin Hub | ||
</Button> | ||
<Button | ||
style={{ | ||
marginLeft: '0.3em', | ||
}} | ||
onClick={() => showPrompt({ | ||
title: 'New Plugin', | ||
defaultValue: 'demo-example', | ||
placeholder: 'example-name', | ||
submitName: 'Generate', | ||
label: 'Plugin Name', | ||
selectText: true, | ||
validate: name => | ||
name.match(/^[a-z][a-z-]*[a-z]$/) ? '' : 'Plugin name must be of format my-plugin-name', | ||
onComplete: async name => { | ||
// Remove insomnia-plugin- prefix if they accidentally typed it | ||
name = name.replace(/^insomnia-plugin-/, ''); | ||
try { | ||
await createPlugin( | ||
`insomnia-plugin-${name}`, | ||
'0.0.1', | ||
[ | ||
'// For help writing plugins, visit the documentation to get started:', | ||
`// ${docsPlugins}`, | ||
'', | ||
'// TODO: Add plugin code here...', | ||
].join('\n'), | ||
); | ||
} catch (err) { | ||
console.error(err); | ||
showAlert({ | ||
title: 'Failed to Create Plugin', | ||
message: err.message, | ||
}); | ||
} | ||
refreshPlugins(); | ||
}, | ||
})} | ||
>Generate New Plugin</Button> | ||
<Button | ||
style={{ | ||
marginLeft: '0.3em', | ||
}} | ||
onClick={() => window.shell.showItemInFolder(path.join(process.env['INSOMNIA_DATA_PATH'] || window.app.getPath('userData'), 'plugins'))} | ||
> | ||
Reveal Plugins Folder | ||
</Button> | ||
<Button | ||
disabled={isRefreshingPlugins} | ||
style={{ | ||
marginLeft: '0.3em', | ||
}} | ||
onClick={() => refreshPlugins()} | ||
> | ||
Reload Plugins | ||
{isRefreshingPlugins && <i className="fa fa-refresh fa-spin space-left" />} | ||
</Button> | ||
</div> | ||
</div> | ||
); | ||
}; |