1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00

Desktop: Resolves #6143: Show installed plugins in Help - About Joplin (#7711)

This commit is contained in:
Julien 2023-02-08 22:16:09 +08:00 committed by GitHub
parent 7841c99c02
commit 631c41a1ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 186 additions and 9 deletions

View File

@ -756,6 +756,7 @@ packages/lib/utils/credentialFiles.js
packages/lib/utils/joplinCloud.js packages/lib/utils/joplinCloud.js
packages/lib/uuid.js packages/lib/uuid.js
packages/lib/versionInfo.js packages/lib/versionInfo.js
packages/lib/versionInfo.test.js
packages/pdf-viewer/FullViewer.js packages/pdf-viewer/FullViewer.js
packages/pdf-viewer/Page.js packages/pdf-viewer/Page.js
packages/pdf-viewer/PdfDocument.js packages/pdf-viewer/PdfDocument.js

1
.gitignore vendored
View File

@ -744,6 +744,7 @@ packages/lib/utils/credentialFiles.js
packages/lib/utils/joplinCloud.js packages/lib/utils/joplinCloud.js
packages/lib/uuid.js packages/lib/uuid.js
packages/lib/versionInfo.js packages/lib/versionInfo.js
packages/lib/versionInfo.test.js
packages/pdf-viewer/FullViewer.js packages/pdf-viewer/FullViewer.js
packages/pdf-viewer/Page.js packages/pdf-viewer/Page.js
packages/pdf-viewer/PdfDocument.js packages/pdf-viewer/PdfDocument.js

View File

@ -12,7 +12,7 @@ class Command extends BaseCommand {
} }
async action() { async action() {
this.stdout(versionInfo(require('./package.json')).message); this.stdout(versionInfo(require('./package.json'), {}).message);
} }
} }

View File

@ -1,6 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import versionInfo from '@joplin/lib/versionInfo'; import versionInfo from '@joplin/lib/versionInfo';
import PluginService from '@joplin/lib/services/plugins/PluginService'; import PluginService, { Plugins } from '@joplin/lib/services/plugins/PluginService';
import Setting from '@joplin/lib/models/Setting'; import Setting from '@joplin/lib/models/Setting';
import restart from '../services/restart'; import restart from '../services/restart';
const packageInfo = require('../packageInfo.js'); const packageInfo = require('../packageInfo.js');
@ -21,6 +21,7 @@ interface State {
error: Error; error: Error;
errorInfo: ErrorInfo; errorInfo: ErrorInfo;
pluginInfos: PluginInfo[]; pluginInfos: PluginInfo[];
plugins: Plugins;
} }
interface Props { interface Props {
@ -29,14 +30,16 @@ interface Props {
export default class ErrorBoundary extends React.Component<Props, State> { export default class ErrorBoundary extends React.Component<Props, State> {
public state: State = { error: null, errorInfo: null, pluginInfos: [] }; public state: State = { error: null, errorInfo: null, pluginInfos: [], plugins: {} };
componentDidCatch(error: any, errorInfo: ErrorInfo) { componentDidCatch(error: any, errorInfo: ErrorInfo) {
if (typeof error === 'string') error = { message: error }; if (typeof error === 'string') error = { message: error };
const pluginInfos: PluginInfo[] = []; const pluginInfos: PluginInfo[] = [];
let plugins: Plugins = {};
try { try {
const service = PluginService.instance(); const service = PluginService.instance();
plugins = service.plugins;
const pluginSettings = service.unserializePluginSettings(Setting.value('plugins.states')); const pluginSettings = service.unserializePluginSettings(Setting.value('plugins.states'));
for (const pluginId in pluginSettings) { for (const pluginId in pluginSettings) {
const plugin = PluginService.instance().pluginById(pluginId); const plugin = PluginService.instance().pluginById(pluginId);
@ -52,7 +55,7 @@ export default class ErrorBoundary extends React.Component<Props, State> {
console.error('Could not get plugin info:', error); console.error('Could not get plugin info:', error);
} }
this.setState({ error, errorInfo, pluginInfos }); this.setState({ error, errorInfo, pluginInfos, plugins });
} }
componentDidMount() { componentDidMount() {
@ -91,7 +94,7 @@ export default class ErrorBoundary extends React.Component<Props, State> {
output.push( output.push(
<section key="versionInfo"> <section key="versionInfo">
<h2>Version info</h2> <h2>Version info</h2>
<pre>{versionInfo(packageInfo).message}</pre> <pre>{versionInfo(packageInfo, this.state.plugins).message}</pre>
</section> </section>
); );

View File

@ -21,6 +21,7 @@ import checkForUpdates from '../checkForUpdates';
const { connect } = require('react-redux'); const { connect } = require('react-redux');
import { reg } from '@joplin/lib/registry'; import { reg } from '@joplin/lib/registry';
import { ProfileConfig } from '@joplin/lib/services/profileConfig/types'; import { ProfileConfig } from '@joplin/lib/services/profileConfig/types';
import PluginService from '@joplin/lib/services/plugins/PluginService';
const packageInfo = require('../packageInfo.js'); const packageInfo = require('../packageInfo.js');
const { clipboard } = require('electron'); const { clipboard } = require('electron');
const Menu = bridge().Menu; const Menu = bridge().Menu;
@ -485,7 +486,8 @@ function useMenu(props: Props) {
} }
function _showAbout() { function _showAbout() {
const v = versionInfo(packageInfo); const v = versionInfo(packageInfo, PluginService.instance().plugins);
const copyToClipboard = bridge().showMessageBox(v.message, { const copyToClipboard = bridge().showMessageBox(v.message, {
icon: `${bridge().electronApp().buildDir()}/icons/128x128.png`, icon: `${bridge().electronApp().buildDir()}/icons/128x128.png`,

View File

@ -0,0 +1,132 @@
import versionInfo from './versionInfo';
import { reg } from './registry';
import { Plugins } from './services/plugins/PluginService';
import Plugin from './services/plugins/Plugin';
jest.mock('./registry');
const info = jest.spyOn(console, 'info').mockImplementation(() => {});
const mockedVersion = jest.fn(() => 'test');
const mockedDb = { version: mockedVersion };
const packageInfo = {
'name': 'Joplin',
'version': '2.10.5',
'description': 'Joplin for Desktop',
'repository': {
'type': 'git',
'url': 'git+https://github.com/laurent22/joplin.git',
},
'author': 'Laurent Cozic',
'license': 'AGPL-3.0-or-later',
'bugs': {
'url': 'https://github.com/laurent22/joplin/issues',
},
'homepage': 'https://github.com/laurent22/joplin#readme',
'build': {
'appId': 'net.cozic.joplin-desktop',
},
'git': {
'branch': 'dev',
'hash': '1b527f2bb',
},
};
describe('getPluginLists', function() {
beforeAll(() => {
(reg.db as jest.Mock).mockReturnValue(mockedDb);
});
beforeEach(() => {
jest.clearAllMocks();
});
it('should not list any plugin when no plugin is installed', () => {
const v = versionInfo(packageInfo, {});
expect(v.body).toMatch(/Revision:\s[a-z0-9]{3,}\s\([a-zA-Z0-9-_/.]{1,}\)$/);
expect(v.message).toMatch(/Revision:\s[a-z0-9]{3,}\s\([a-zA-Z0-9-_/.]{1,}\)$/);
});
it('should list one plugin', () => {
const plugin: Plugin = new Plugin(
'',
{
manifest_version: 1,
id: '1',
name: 'Plugin1',
version: '1',
app_min_version: '1' },
'',
() => {},
''
);
const plugins: Plugins = {};
plugins[plugin.manifest.id] = plugin;
const v = versionInfo(packageInfo, plugins);
expect(v.body).toMatch(/\n\nPlugin1: 1/);
expect(v.message).toMatch(/\n\nPlugin1: 1/);
});
it('should show a list of three plugins', () => {
const plugins: Plugins = {};
for (let i = 1; i <= 3; i++) {
const plugin: Plugin = new Plugin(
'',
{
manifest_version: i,
id: i.toString(),
name: `Plugin${i}`,
version: '1',
app_min_version: '1' },
'',
() => {},
''
);
plugins[plugin.manifest.id] = plugin;
}
const v = versionInfo(packageInfo, plugins);
expect(v.body).toMatch(/\n\nPlugin1: 1\nPlugin2: 1\nPlugin3: 1/);
expect(v.message).toMatch(/\n\nPlugin1: 1\nPlugin2: 1\nPlugin3: 1/);
});
it('should show an abridged list of plugins in message and the full list in body', () => {
const plugins: Plugins = {};
for (let i = 1; i <= 21; i++) {
const plugin: Plugin = new Plugin(
'',
{
manifest_version: i,
id: i.toString(),
name: `Plugin${i}`,
version: '1',
app_min_version: '1' },
'',
() => {},
''
);
plugins[plugin.manifest.id] = plugin;
}
const v = versionInfo(packageInfo, plugins);
const body = '\n';
for (let i = 1; i <= 21; i++) {
body.concat(`\nPlugin${i}: 1`);
}
expect(v.body).toMatch(new RegExp(body));
const message = '\n';
for (let i = 1; i <= 20; i++) {
message.concat(`\nPlugin${i}: 1`);
}
message.concat('\n...');
expect(v.message).toMatch(new RegExp(message));
});
info.mockReset();
});

View File

@ -1,8 +1,44 @@
import { _ } from './locale'; import { _ } from './locale';
import Setting from './models/Setting'; import Setting from './models/Setting';
import { reg } from './registry'; import { reg } from './registry';
import { Plugins } from './services/plugins/PluginService';
export default function versionInfo(packageInfo: any) { interface PluginList {
completeList: string;
summary: string;
}
function getPluginLists(plugins: Plugins): PluginList {
const pluginList = [];
if (Object.keys(plugins).length > 0) {
for (const pluginId in plugins) {
pluginList.push(`${plugins[pluginId].manifest.name}: ${plugins[pluginId].manifest.version}`);
}
}
let completeList = '';
let summary = '';
if (pluginList.length > 0) {
completeList = ['\n', ...pluginList].join('\n');
if (pluginList.length > 20) {
summary = [
'\n',
...[...pluginList].filter((_, index) => index < 20),
'...',
].join('\n');
} else {
summary = completeList;
}
}
return {
completeList,
summary,
};
}
export default function versionInfo(packageInfo: any, plugins: Plugins) {
const p = packageInfo; const p = packageInfo;
let gitInfo = ''; let gitInfo = '';
if ('git' in p) { if ('git' in p) {
@ -32,9 +68,11 @@ export default function versionInfo(packageInfo: any) {
console.info(gitInfo); console.info(gitInfo);
} }
const pluginList = getPluginLists(plugins);
return { return {
header: header.join('\n'), header: header.join('\n'),
body: body.join('\n'), body: body.join('\n').concat(pluginList.completeList),
message: header.concat(body).join('\n'), message: header.concat(body).join('\n').concat(pluginList.summary),
}; };
} }