PluginManager
Summary: Adding the plugin installer to the plugin sheet as a second tab Reviewed By: passy Differential Revision: D17450842 fbshipit-source-id: 211c9f15ed2614a1dd46d974b86f50c825f81fb0
This commit is contained in:
committed by
Facebook Github Bot
parent
039d1cca99
commit
8c623867bd
12
src/App.tsx
12
src/App.tsx
@@ -19,24 +19,22 @@ import ShareSheetExportFile from './chrome/ShareSheetExportFile';
|
||||
import PluginContainer from './PluginContainer';
|
||||
import Sheet from './chrome/Sheet';
|
||||
import {ipcRenderer, remote} from 'electron';
|
||||
import PluginDebugger from './chrome/PluginDebugger';
|
||||
import {
|
||||
ActiveSheet,
|
||||
ShareType,
|
||||
ACTIVE_SHEET_BUG_REPORTER,
|
||||
ACTIVE_SHEET_PLUGIN_DEBUGGER,
|
||||
ACTIVE_SHEET_PLUGINS,
|
||||
ACTIVE_SHEET_SHARE_DATA,
|
||||
ACTIVE_SHEET_SIGN_IN,
|
||||
ACTIVE_SHEET_SHARE_DATA_IN_FILE,
|
||||
ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT,
|
||||
ACTIVE_SHEET_PLUGIN_SHEET,
|
||||
ACTIVE_SHEET_PLUGIN_INSTALLER,
|
||||
} from './reducers/application';
|
||||
import {Logger} from './fb-interfaces/Logger';
|
||||
import BugReporter from './fb-stubs/BugReporter';
|
||||
import {State as Store} from './reducers/index';
|
||||
import {StaticView} from './reducers/connections';
|
||||
import PluginInstaller from './chrome/PluginInstaller';
|
||||
import PluginManager from './chrome/PluginManager';
|
||||
const version = remote.app.getVersion();
|
||||
|
||||
type OwnProps = {
|
||||
@@ -80,16 +78,14 @@ export class App extends React.Component<Props> {
|
||||
onHide={onHide}
|
||||
/>
|
||||
);
|
||||
case ACTIVE_SHEET_PLUGIN_DEBUGGER:
|
||||
return <PluginDebugger onHide={onHide} />;
|
||||
case ACTIVE_SHEET_PLUGINS:
|
||||
return <PluginManager onHide={onHide} />;
|
||||
case ACTIVE_SHEET_SHARE_DATA:
|
||||
return <ShareSheet onHide={onHide} logger={this.props.logger} />;
|
||||
case ACTIVE_SHEET_SIGN_IN:
|
||||
return <SignInSheet onHide={onHide} />;
|
||||
case ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT:
|
||||
return <ExportDataPluginSheet onHide={onHide} />;
|
||||
case ACTIVE_SHEET_PLUGIN_INSTALLER:
|
||||
return <PluginInstaller onHide={onHide} />;
|
||||
case ACTIVE_SHEET_SHARE_DATA_IN_FILE:
|
||||
return (
|
||||
<ShareSheetExportFile
|
||||
|
||||
@@ -11,7 +11,7 @@ import Client from '../Client';
|
||||
import {UninitializedClient} from '../UninitializedClient';
|
||||
import {FlipperBasePlugin} from '../plugin';
|
||||
import {PluginNotification} from '../reducers/notifications';
|
||||
import {ActiveSheet} from '../reducers/application';
|
||||
import {ActiveSheet, ACTIVE_SHEET_PLUGINS} from '../reducers/application';
|
||||
import {State as Store} from '../reducers';
|
||||
|
||||
import {
|
||||
@@ -392,7 +392,7 @@ class MainSidebar extends PureComponent<Props> {
|
||||
))}
|
||||
</Plugins>
|
||||
<PluginDebugger
|
||||
onClick={() => this.props.setActiveSheet('PLUGIN_DEBUGGER')}>
|
||||
onClick={() => this.props.setActiveSheet(ACTIVE_SHEET_PLUGINS)}>
|
||||
<Glyph
|
||||
name="question-circle"
|
||||
size={16}
|
||||
|
||||
@@ -11,8 +11,6 @@ import {TableBodyRow} from '../ui/components/table/types';
|
||||
import React, {Component, Fragment} from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {
|
||||
FlexColumn,
|
||||
Button,
|
||||
Text,
|
||||
ManagedTable,
|
||||
styled,
|
||||
@@ -24,38 +22,22 @@ import {
|
||||
import StatusIndicator from '../ui/components/StatusIndicator';
|
||||
import {State as Store} from '../reducers';
|
||||
|
||||
const Container = styled(FlexColumn)({
|
||||
padding: 10,
|
||||
width: 700,
|
||||
});
|
||||
|
||||
const InfoText = styled(Text)({
|
||||
lineHeight: '130%',
|
||||
marginBottom: 8,
|
||||
});
|
||||
|
||||
const Title = styled('div')({
|
||||
fontWeight: 500,
|
||||
marginBottom: 10,
|
||||
marginTop: 8,
|
||||
});
|
||||
|
||||
const Ellipsis = styled(Text)({
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
});
|
||||
|
||||
const Row = styled(FlexColumn)({
|
||||
alignItems: 'flex-end',
|
||||
});
|
||||
|
||||
const TableContainer = styled('div')({
|
||||
borderRadius: 4,
|
||||
overflow: 'hidden',
|
||||
border: `1px solid ${colors.macOSTitleBarButtonBorder}`,
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
backgroundColor: colors.white,
|
||||
height: 400,
|
||||
display: 'flex',
|
||||
@@ -77,9 +59,7 @@ type StateFromProps = {
|
||||
|
||||
type DispatchFromProps = {};
|
||||
|
||||
type OwnProps = {
|
||||
onHide: () => any;
|
||||
};
|
||||
type OwnProps = {};
|
||||
|
||||
const COLUMNS = {
|
||||
lamp: {
|
||||
@@ -304,17 +284,7 @@ class PluginDebugger extends Component<Props> {
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Container>
|
||||
<Title>Plugin Status</Title>
|
||||
{content}
|
||||
<Row>
|
||||
<Button compact padded onClick={this.props.onHide}>
|
||||
Close
|
||||
</Button>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
return content;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,12 +41,6 @@ type PluginDefinition = {
|
||||
description: string;
|
||||
};
|
||||
|
||||
const Container = styled(FlexColumn)({
|
||||
width: 600,
|
||||
height: 400,
|
||||
background: colors.white,
|
||||
});
|
||||
|
||||
const EllipsisText = styled(Text)({
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
@@ -75,6 +69,14 @@ const columns = {
|
||||
},
|
||||
};
|
||||
|
||||
const Container = styled(FlexColumn)({
|
||||
height: 300,
|
||||
backgroundColor: colors.white,
|
||||
borderRadius: 4,
|
||||
overflow: 'hidden',
|
||||
border: `1px solid ${colors.macOSTitleBarButtonBorder}`,
|
||||
});
|
||||
|
||||
const RestartBar = styled(FlexColumn)({
|
||||
backgroundColor: colors.red,
|
||||
color: colors.white,
|
||||
@@ -84,7 +86,7 @@ const RestartBar = styled(FlexColumn)({
|
||||
textAlign: 'center',
|
||||
});
|
||||
|
||||
export default function(props: {onHide: () => any}) {
|
||||
export default function() {
|
||||
const [restartRequired, setRestartRequired] = useState(false);
|
||||
const [query, setQuery] = useState('');
|
||||
const rows = useNPMSearch(setRestartRequired, query, setQuery);
|
||||
@@ -120,10 +122,6 @@ export default function(props: {onHide: () => any}) {
|
||||
highlightedRows={new Set()}
|
||||
rows={rows}
|
||||
/>
|
||||
<Toolbar position="bottom">
|
||||
<Spacer />
|
||||
<Button onClick={props.onHide}>Close</Button>
|
||||
</Toolbar>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -210,7 +208,7 @@ function useNPMSearch(
|
||||
(h: PluginDefinition) => ({
|
||||
key: h.name,
|
||||
columns: {
|
||||
name: {value: <EllipsisText bold>{h.name}</EllipsisText>},
|
||||
name: {value: <EllipsisText>{h.name}</EllipsisText>},
|
||||
version: {
|
||||
value: <EllipsisText>{h.version}</EllipsisText>,
|
||||
align: 'flex-end' as 'flex-end',
|
||||
|
||||
@@ -1,404 +0,0 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
import {
|
||||
PureComponent,
|
||||
Button,
|
||||
FlexColumn,
|
||||
FlexBox,
|
||||
Text,
|
||||
LoadingIndicator,
|
||||
ButtonGroup,
|
||||
colors,
|
||||
Glyph,
|
||||
FlexRow,
|
||||
styled,
|
||||
Searchable,
|
||||
} from 'flipper';
|
||||
const {spawn} = require('child_process');
|
||||
const path = require('path');
|
||||
const {app, shell} = require('electron').remote;
|
||||
|
||||
const FLIPPER_PLUGIN_PATH = path.join(app.getPath('home'), '.flipper');
|
||||
const DYNAMIC_PLUGINS = JSON.parse(window.process.env.PLUGINS || '[]');
|
||||
|
||||
type NPMModule = {
|
||||
name: string,
|
||||
version: string,
|
||||
description?: string,
|
||||
error?: Object,
|
||||
};
|
||||
|
||||
type Status =
|
||||
| 'installed'
|
||||
| 'outdated'
|
||||
| 'install'
|
||||
| 'remove'
|
||||
| 'update'
|
||||
| 'uninstalled'
|
||||
| 'uptodate';
|
||||
|
||||
type PluginT = {
|
||||
name: string,
|
||||
version?: string,
|
||||
description?: string,
|
||||
status: Status,
|
||||
managed?: boolean,
|
||||
entry?: string,
|
||||
rootDir?: string,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
searchTerm: string,
|
||||
};
|
||||
type State = {
|
||||
plugins: {
|
||||
[name: string]: PluginT,
|
||||
},
|
||||
restartRequired: boolean,
|
||||
searchCompleted: boolean,
|
||||
};
|
||||
|
||||
const Container = styled(FlexBox)({
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
background: colors.light02,
|
||||
overflowY: 'scroll',
|
||||
});
|
||||
|
||||
const Title = styled(Text)({
|
||||
fontWeight: 500,
|
||||
});
|
||||
|
||||
const Plugin = styled(FlexColumn)({
|
||||
backgroundColor: colors.white,
|
||||
borderRadius: 4,
|
||||
padding: 15,
|
||||
margin: '0 15px 25px',
|
||||
boxShadow: '0 1px 2px rgba(0,0,0,0.05)',
|
||||
});
|
||||
|
||||
const SectionTitle = styled('span')({
|
||||
fontWeight: 'bold',
|
||||
fontSize: 24,
|
||||
margin: 15,
|
||||
marginLeft: 20,
|
||||
});
|
||||
|
||||
const Loading = styled(FlexBox)({
|
||||
padding: 50,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
});
|
||||
|
||||
const RestartRequired = styled(FlexBox)({
|
||||
textAlign: 'center',
|
||||
justifyContent: 'center',
|
||||
fontWeight: 500,
|
||||
color: colors.white,
|
||||
padding: 12,
|
||||
backgroundColor: colors.green,
|
||||
cursor: 'pointer',
|
||||
});
|
||||
|
||||
const TitleRow = styled(FlexRow)({
|
||||
alignItems: 'center',
|
||||
marginBottom: 10,
|
||||
fontSize: '1.1em',
|
||||
});
|
||||
|
||||
const Description = styled(FlexRow)({
|
||||
marginBottom: 15,
|
||||
lineHeight: '130%',
|
||||
});
|
||||
|
||||
const PluginGlyph = styled(Glyph)({
|
||||
marginRight: 5,
|
||||
});
|
||||
|
||||
const PluginLoading = styled(LoadingIndicator)({
|
||||
marginLeft: 5,
|
||||
marginTop: 5,
|
||||
});
|
||||
|
||||
const getLatestVersion = (name: string): Promise<NPMModule> => {
|
||||
return fetch(`http://registry.npmjs.org/${name}/latest`).then(res =>
|
||||
res.json(),
|
||||
);
|
||||
};
|
||||
|
||||
const getPluginList = (): Promise<Array<NPMModule>> => {
|
||||
return fetch(
|
||||
'http://registry.npmjs.org/-/v1/search?text=keywords:flipper&size=250',
|
||||
)
|
||||
.then(res => res.json())
|
||||
.then(res => res.objects.map(o => o.package));
|
||||
};
|
||||
|
||||
const sortByName = (a: PluginT, b: PluginT): 1 | -1 =>
|
||||
a.name > b.name ? 1 : -1;
|
||||
|
||||
const INSTALLED = ['installed', 'outdated', 'uptodate'];
|
||||
|
||||
class PluginItem extends PureComponent<
|
||||
{
|
||||
plugin: PluginT,
|
||||
onChangeState: (action: Status) => void,
|
||||
},
|
||||
{
|
||||
working: boolean,
|
||||
},
|
||||
> {
|
||||
state = {
|
||||
working: false,
|
||||
};
|
||||
|
||||
npmAction = (action: Status) => {
|
||||
const {name, status: initialStatus} = this.props.plugin;
|
||||
this.setState({working: true});
|
||||
const npm = spawn('npm', [action, name], {
|
||||
cwd: FLIPPER_PLUGIN_PATH,
|
||||
});
|
||||
|
||||
npm.stderr.on('data', e => {
|
||||
console.error(e.toString());
|
||||
});
|
||||
|
||||
npm.on('close', code => {
|
||||
this.setState({working: false});
|
||||
const newStatus = action === 'remove' ? 'uninstalled' : 'uptodate';
|
||||
this.props.onChangeState(code !== 0 ? initialStatus : newStatus);
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
entry,
|
||||
status,
|
||||
version,
|
||||
description,
|
||||
managed,
|
||||
name,
|
||||
rootDir,
|
||||
} = this.props.plugin;
|
||||
|
||||
return (
|
||||
<Plugin>
|
||||
<TitleRow>
|
||||
<PluginGlyph
|
||||
name="apps"
|
||||
size={24}
|
||||
variant="outline"
|
||||
color={colors.light30}
|
||||
/>
|
||||
<Title>{name}</Title>
|
||||
|
||||
<Text code={true}>{version}</Text>
|
||||
</TitleRow>
|
||||
{description && <Description>{description}</Description>}
|
||||
<FlexRow>
|
||||
{managed ? (
|
||||
<Text size="0.9em" color={colors.light30}>
|
||||
This plugin is not managed by Flipper, but loaded from{' '}
|
||||
<Text size="1em" code={true}>
|
||||
{rootDir}
|
||||
</Text>
|
||||
</Text>
|
||||
) : (
|
||||
<ButtonGroup>
|
||||
{status === 'outdated' && (
|
||||
<Button
|
||||
disabled={this.state.working}
|
||||
onClick={() => this.npmAction('update')}>
|
||||
Update
|
||||
</Button>
|
||||
)}
|
||||
{INSTALLED.includes(status) ? (
|
||||
<Button
|
||||
disabled={this.state.working}
|
||||
title={
|
||||
managed === true && entry != null
|
||||
? `This plugin is dynamically loaded from ${entry}`
|
||||
: undefined
|
||||
}
|
||||
onClick={() => this.npmAction('remove')}>
|
||||
Remove
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
disabled={this.state.working}
|
||||
onClick={() => this.npmAction('install')}>
|
||||
Install
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
onClick={() =>
|
||||
shell.openExternal(`https://www.npmjs.com/package/${name}`)
|
||||
}>
|
||||
Info
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
)}
|
||||
{this.state.working && <PluginLoading size={18} />}
|
||||
</FlexRow>
|
||||
</Plugin>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PluginManager extends PureComponent<Props, State> {
|
||||
state = {
|
||||
plugins: DYNAMIC_PLUGINS.reduce((acc, plugin) => {
|
||||
acc[plugin.name] = {
|
||||
...plugin,
|
||||
managed: !(plugin.entry, '').startsWith(FLIPPER_PLUGIN_PATH),
|
||||
status: 'installed',
|
||||
};
|
||||
return acc;
|
||||
}, {}),
|
||||
restartRequired: false,
|
||||
searchCompleted: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
Promise.all(
|
||||
Object.keys(this.state.plugins)
|
||||
.filter(name => this.state.plugins[name].managed)
|
||||
.map(getLatestVersion),
|
||||
).then((res: Array<NPMModule>) => {
|
||||
const updates = {};
|
||||
res.forEach(plugin => {
|
||||
if (
|
||||
plugin.error == null &&
|
||||
this.state.plugins[plugin.name].version !== plugin.version
|
||||
) {
|
||||
updates[plugin.name] = {
|
||||
...plugin,
|
||||
...this.state.plugins[plugin.name],
|
||||
status: 'outdated',
|
||||
};
|
||||
}
|
||||
});
|
||||
this.setState({
|
||||
plugins: {
|
||||
...this.state.plugins,
|
||||
...updates,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
getPluginList().then(pluginList => {
|
||||
const plugins = {...this.state.plugins};
|
||||
pluginList.forEach(plugin => {
|
||||
if (plugins[plugin.name] != null) {
|
||||
plugins[plugin.name] = {
|
||||
...plugin,
|
||||
...plugins[plugin.name],
|
||||
status:
|
||||
plugin.version === plugins[plugin.name].version
|
||||
? 'uptodate'
|
||||
: 'outdated',
|
||||
};
|
||||
} else {
|
||||
plugins[plugin.name] = {
|
||||
...plugin,
|
||||
status: 'uninstalled',
|
||||
};
|
||||
}
|
||||
});
|
||||
this.setState({
|
||||
plugins,
|
||||
searchCompleted: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onChangePluginState = (name: string, status: Status) => {
|
||||
this.setState({
|
||||
plugins: {
|
||||
...this.state.plugins,
|
||||
[name]: {
|
||||
...this.state.plugins[name],
|
||||
status,
|
||||
},
|
||||
},
|
||||
restartRequired: true,
|
||||
});
|
||||
};
|
||||
|
||||
relaunch() {
|
||||
app.relaunch();
|
||||
app.exit(0);
|
||||
}
|
||||
|
||||
render() {
|
||||
// $FlowFixMe
|
||||
const plugins: Array<PluginT> = Object.values(this.state.plugins);
|
||||
const availablePlugins = plugins.filter(
|
||||
({status}) => !INSTALLED.includes(status),
|
||||
);
|
||||
return (
|
||||
<Container>
|
||||
<FlexColumn grow={true}>
|
||||
{this.state.restartRequired && (
|
||||
<RestartRequired onClick={this.relaunch}>
|
||||
<Glyph name="arrows-circle" size={12} color={colors.white} />
|
||||
Restart Required: Click to Restart
|
||||
</RestartRequired>
|
||||
)}
|
||||
<SectionTitle>Installed Plugins</SectionTitle>
|
||||
{plugins
|
||||
.filter(
|
||||
({status, name}) =>
|
||||
INSTALLED.includes(status) &&
|
||||
name.indexOf(this.props.searchTerm) > -1,
|
||||
)
|
||||
.sort(sortByName)
|
||||
.map((plugin: PluginT) => (
|
||||
<PluginItem
|
||||
plugin={plugin}
|
||||
key={plugin.name}
|
||||
onChangeState={action =>
|
||||
this.onChangePluginState(plugin.name, action)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
<SectionTitle>Available Plugins</SectionTitle>
|
||||
{availablePlugins
|
||||
.filter(({name}) => name.indexOf(this.props.searchTerm) > -1)
|
||||
.sort(sortByName)
|
||||
.map((plugin: PluginT) => (
|
||||
<PluginItem
|
||||
plugin={plugin}
|
||||
key={plugin.name}
|
||||
onChangeState={action =>
|
||||
this.onChangePluginState(plugin.name, action)
|
||||
}
|
||||
/>
|
||||
))}
|
||||
{!this.state.searchCompleted && (
|
||||
<Loading>
|
||||
<LoadingIndicator size={32} />
|
||||
</Loading>
|
||||
)}
|
||||
</FlexColumn>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const SearchablePluginManager = Searchable(PluginManager);
|
||||
|
||||
export default class extends PureComponent<{}> {
|
||||
render() {
|
||||
return (
|
||||
<FlexColumn grow={true}>
|
||||
<SearchablePluginManager />
|
||||
</FlexColumn>
|
||||
);
|
||||
}
|
||||
}
|
||||
45
src/chrome/PluginManager.tsx
Normal file
45
src/chrome/PluginManager.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright 2018-present Facebook.
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
* @format
|
||||
*/
|
||||
|
||||
import React, {useState} from 'react';
|
||||
import {FlexColumn, Button, styled, Tab, Tabs, TabsContainer} from 'flipper';
|
||||
import PluginDebugger from './PluginDebugger';
|
||||
import PluginInstaller from './PluginInstaller';
|
||||
|
||||
const Container = styled(FlexColumn)({
|
||||
padding: 15,
|
||||
width: 700,
|
||||
});
|
||||
|
||||
const Row = styled(FlexColumn)({
|
||||
alignItems: 'flex-end',
|
||||
});
|
||||
|
||||
type Tabs = 'Plugin Status' | 'Install Plugins';
|
||||
|
||||
export default function(props: {onHide: () => any}) {
|
||||
const [tab, setTab] = useState<Tabs>('Plugin Status');
|
||||
return (
|
||||
<Container>
|
||||
<TabsContainer>
|
||||
<Tabs
|
||||
active={tab}
|
||||
onActive={setTab as (s: string | null | undefined) => void}>
|
||||
<Tab label="Plugin Status" />
|
||||
<Tab label="Install Plugins" />
|
||||
</Tabs>
|
||||
{tab === 'Plugin Status' && <PluginDebugger />}
|
||||
{tab === 'Install Plugins' && <PluginInstaller />}
|
||||
</TabsContainer>
|
||||
<Row>
|
||||
<Button compact padded onClick={props.onHide}>
|
||||
Close
|
||||
</Button>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
@@ -12,8 +12,7 @@ import CancellableExportStatus from '../chrome/CancellableExportStatus';
|
||||
import {Actions} from './';
|
||||
export const ACTIVE_SHEET_PLUGIN_SHEET: 'PLUGIN_SHEET' = 'PLUGIN_SHEET';
|
||||
export const ACTIVE_SHEET_BUG_REPORTER: 'BUG_REPORTER' = 'BUG_REPORTER';
|
||||
export const ACTIVE_SHEET_PLUGIN_DEBUGGER: 'PLUGIN_DEBUGGER' =
|
||||
'PLUGIN_DEBUGGER';
|
||||
export const ACTIVE_SHEET_PLUGINS: 'PLUGINS' = 'PLUGINS';
|
||||
export const ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT: 'SELECT_PLUGINS_TO_EXPORT' =
|
||||
'SELECT_PLUGINS_TO_EXPORT';
|
||||
export const ACTIVE_SHEET_SHARE_DATA: 'SHARE_DATA' = 'SHARE_DATA';
|
||||
@@ -23,18 +22,15 @@ export const ACTIVE_SHEET_SHARE_DATA_IN_FILE: 'SHARE_DATA_IN_FILE' =
|
||||
export const SET_EXPORT_STATUS_MESSAGE: 'SET_EXPORT_STATUS_MESSAGE' =
|
||||
'SET_EXPORT_STATUS_MESSAGE';
|
||||
export const UNSET_SHARE: 'UNSET_SHARE' = 'UNSET_SHARE';
|
||||
export const ACTIVE_SHEET_PLUGIN_INSTALLER: 'ACTIVE_SHEET_PLUGIN_INSTALLER' =
|
||||
'ACTIVE_SHEET_PLUGIN_INSTALLER';
|
||||
|
||||
export type ActiveSheet =
|
||||
| typeof ACTIVE_SHEET_PLUGIN_SHEET
|
||||
| typeof ACTIVE_SHEET_BUG_REPORTER
|
||||
| typeof ACTIVE_SHEET_PLUGIN_DEBUGGER
|
||||
| typeof ACTIVE_SHEET_PLUGINS
|
||||
| typeof ACTIVE_SHEET_SHARE_DATA
|
||||
| typeof ACTIVE_SHEET_SIGN_IN
|
||||
| typeof ACTIVE_SHEET_SHARE_DATA_IN_FILE
|
||||
| typeof ACTIVE_SHEET_SELECT_PLUGINS_TO_EXPORT
|
||||
| typeof ACTIVE_SHEET_PLUGIN_INSTALLER
|
||||
| null;
|
||||
|
||||
export type LauncherMsg = {
|
||||
|
||||
Reference in New Issue
Block a user