refactor: rewrite for error handling, logging and disposing

This commit is contained in:
iCrawl 2020-07-16 18:25:05 +02:00
parent ad9dad83e1
commit 99766de53d
No known key found for this signature in database
GPG key ID: 1AB888B16355FBB2
8 changed files with 655 additions and 1388 deletions

45
package-lock.json generated
View file

@ -1,6 +1,6 @@
{
"name": "discord-vscode",
"version": "3.16.0",
"version": "4.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -912,9 +912,9 @@
"dev": true
},
"cacache": {
"version": "15.0.4",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.4.tgz",
"integrity": "sha512-YlnKQqTbD/6iyoJvEY3KJftjrdBYroCbxxYXzhOzsFLWlp6KX4BOlEf4mTx0cMUfVaTS3ENL2QtDWeRYoGLkkw==",
"version": "15.0.5",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz",
"integrity": "sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==",
"dev": true,
"requires": {
"@npmcli/move-file": "^1.0.1",
@ -922,7 +922,7 @@
"fs-minipass": "^2.0.0",
"glob": "^7.1.4",
"infer-owner": "^1.0.4",
"lru-cache": "^5.1.1",
"lru-cache": "^6.0.0",
"minipass": "^3.1.1",
"minipass-collect": "^1.0.2",
"minipass-flush": "^1.0.5",
@ -936,6 +936,15 @@
"unique-filename": "^1.1.1"
},
"dependencies": {
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"requires": {
"yallist": "^4.0.0"
}
},
"mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
@ -1461,7 +1470,7 @@
}
},
"discord-rpc": {
"version": "github:discordjs/RPC#df0a9e00b17b86c728e7b43bc2bae2641560a887",
"version": "github:discordjs/RPC#69915999307d337688b92f6011ea4961a7f8313e",
"from": "github:discordjs/RPC",
"requires": {
"node-fetch": "2.6.0",
@ -2864,9 +2873,9 @@
}
},
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"version": "4.17.19",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==",
"dev": true
},
"lru-cache": {
@ -4286,15 +4295,15 @@
}
},
"terser-webpack-plugin": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-3.0.6.tgz",
"integrity": "sha512-z3HLOOPUHkCNGkeEHqqiMAIy1pjpHwS1o+i6Zn0Ws3EAvHJj46737efNNEvJ0Vx9BdDQM83d56qySDJOSORA0A==",
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-3.0.7.tgz",
"integrity": "sha512-5JqibUOctE6Ou4T00IVGYTQJBOhu24jz0PpqYeitQJJ3hlZY2ZKSwzzuqjmBH8MzbdWMgIefpmHwTkvwm6Q4CQ==",
"dev": true,
"requires": {
"cacache": "^15.0.4",
"cacache": "^15.0.5",
"find-cache-dir": "^3.3.1",
"jest-worker": "^26.0.0",
"p-limit": "^3.0.1",
"jest-worker": "^26.1.0",
"p-limit": "^3.0.2",
"schema-utils": "^2.6.6",
"serialize-javascript": "^4.0.0",
"source-map": "^0.6.1",
@ -4303,9 +4312,9 @@
},
"dependencies": {
"p-limit": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.1.tgz",
"integrity": "sha512-mw/p92EyOzl2MhauKodw54Rx5ZK4624rNfgNaBguFZkHzyUG9WsDzFF5/yQVEJinbJDdP4jEfMN+uBquiGnaLg==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
"integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
"dev": true,
"requires": {
"p-try": "^2.0.0"

View file

@ -1,7 +1,7 @@
{
"name": "discord-vscode",
"displayName": "Discord Presence",
"version": "3.16.0",
"version": "4.0.0",
"description": "Update your discord status with the newly added rich presence.",
"private": true,
"author": {
@ -181,7 +181,7 @@
},
"dependencies": {
"bufferutil": "^4.0.1",
"discord-rpc": "discordjs/RPC",
"discord-rpc": "github:discordjs/RPC",
"utf-8-validate": "^5.0.2",
"vsls": "^1.0.2426"
},
@ -199,7 +199,7 @@
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.4",
"prettier": "^2.0.5",
"terser-webpack-plugin": "^3.0.6",
"terser-webpack-plugin": "^3.0.7",
"ts-loader": "^8.0.1",
"typescript": "^3.9.6",
"webpack": "^4.43.0",

View file

@ -8,81 +8,87 @@ import { API } from '../git';
let activityTimer: NodeJS.Timer | undefined;
export default class RPCClient implements Disposable {
public statusBarIcon: StatusBarItem;
public config = workspace.getConfiguration('discord');
public git?: API;
private _rpc: any;
private rpc: any;
private readonly _activity = new Activity(this);
private readonly activity = new Activity(this);
private readonly _clientId: string;
public constructor(clientId: string, statusBarIcon: StatusBarItem) {
this._clientId = clientId;
this.statusBarIcon = statusBarIcon;
}
public constructor(private readonly clientId: string, public statusBarIcon: StatusBarItem) {}
public get client() {
return this._rpc;
return this.rpc;
}
public async setActivity(workspaceElapsedTime = false) {
if (!this._rpc) return;
const activity = await this._activity.generate(workspaceElapsedTime);
if (!this.rpc) return;
const activity = await this.activity.generate(workspaceElapsedTime);
if (!activity) return;
Logger.log('Sending activity to Discord.');
this._rpc.setActivity(activity);
this.rpc.setActivity(activity);
}
public async allowSpectate() {
if (!this._rpc) return;
public allowSpectate() {
if (!this.rpc) return;
Logger.log('Allowed spectating.');
Logger.log('Sending spectate activity to Discord.');
await this._activity.allowSpectate();
void this.activity.allowSpectate();
}
public async disableSpectate() {
if (!this._rpc) return;
public disableSpectate() {
if (!this.rpc) return;
Logger.log('Disabled spectating.');
await this._activity.disableSpectate();
void this.activity.disableSpectate();
}
public async allowJoinRequests() {
if (!this._rpc) return;
public allowJoinRequests() {
if (!this.rpc) return;
Logger.log('Allowed join requests.');
Logger.log('Sending join activity to Discord.');
await this._activity.allowJoinRequests();
void this.activity.allowJoinRequests();
}
public async disableJoinRequests() {
if (!this._rpc) return;
public disableJoinRequests() {
if (!this.rpc) return;
Logger.log('Disabled join requests.');
await this._activity.disableJoinRequests();
void this.activity.disableJoinRequests();
}
public async login() {
if (this._rpc) return;
this._rpc = new Client({ transport: 'ipc' });
Logger.log('Logging into RPC.');
this._rpc.once('ready', async () => {
if (this.rpc) {
this.dispose();
}
this.rpc = new Client({ transport: 'ipc' });
Logger.log('Logging into RPC...');
this.rpc.transport.once('close', () => {
if (!this.config.get<boolean>('enabled')) return;
void this.dispose();
this.statusBarIcon.text = '$(plug) Reconnect to Discord';
this.statusBarIcon.command = 'discord.reconnect';
this.statusBarIcon.tooltip = '';
});
this.rpc.once('ready', async () => {
Logger.log('Successfully connected to Discord.');
this.statusBarIcon.text = '$(globe) Connected to Discord';
this.statusBarIcon.tooltip = 'Connected to Discord';
setTimeout(() => (this.statusBarIcon.text = '$(globe)'), 5000);
if (activityTimer) clearInterval(activityTimer);
await this.setActivity(this.config.get<boolean>('workspaceElapsedTime'));
void this.setActivity(this.config.get<boolean>('workspaceElapsedTime'));
activityTimer = setInterval(() => {
this.config = workspace.getConfiguration('discord');
void this.setActivity(this.config.get<boolean>('workspaceElapsedTime'));
}, 10000);
}, 1000);
this._rpc.subscribe('ACTIVITY_SPECTATE', async ({ secret }: { secret: string }) => {
this.rpc.subscribe('ACTIVITY_SPECTATE', async ({ secret }: { secret: string }) => {
const liveshare = await vsls.getApi();
if (!liveshare) return;
try {
@ -104,7 +110,7 @@ export default class RPCClient implements Disposable {
// Same here, this is a real nasty race condition that happens inside the discord-rpc module currently
// To circumvent this we need to timeout sending the subscribe events to the discord client
setTimeout(() => {
this._rpc.subscribe(
this.rpc.subscribe(
'ACTIVITY_JOIN_REQUEST',
async ({ user }: { user: { username: string; discriminator: string } }) => {
const val = await window.showInformationMessage(
@ -112,13 +118,13 @@ export default class RPCClient implements Disposable {
{ title: 'Accept' },
{ title: 'Decline' },
);
if (val && val.title === 'Accept') await this._rpc.sendJoinInvite(user);
else await this._rpc.closeJoinRequest(user);
if (val && val.title === 'Accept') await this.rpc.sendJoinInvite(user);
else await this.rpc.closeJoinRequest(user);
},
);
}, 1000);
setTimeout(() => {
this._rpc.subscribe('ACTIVITY_JOIN', async ({ secret }: { secret: string }) => {
this.rpc.subscribe('ACTIVITY_JOIN', async ({ secret }: { secret: string }) => {
const liveshare = await vsls.getApi();
if (!liveshare) return;
try {
@ -137,33 +143,28 @@ export default class RPCClient implements Disposable {
const liveshare = await vsls.getApi();
if (!liveshare) return;
liveshare.onDidChangeSession(({ session }) => {
if (session.id) return this._activity.changePartyId(session.id);
return this._activity.changePartyId();
if (session.id) return this.activity.changePartyId(session.id);
return this.activity.changePartyId();
});
liveshare.onDidChangePeers(({ added, removed }) => {
if (added.length) return this._activity.increasePartySize(added.length);
else if (removed.length) return this._activity.decreasePartySize(removed.length);
if (added.length) return this.activity.increasePartySize(added.length);
else if (removed.length) return this.activity.decreasePartySize(removed.length);
});
});
this._rpc.transport.once('close', async () => {
if (!this.config.get<boolean>('enabled')) return;
await this.dispose();
this.statusBarIcon.text = '$(plug) Reconnect to Discord';
this.statusBarIcon.command = 'discord.reconnect';
this.statusBarIcon.tooltip = '';
});
await this._rpc.login({ clientId: this._clientId });
try {
await this.rpc.login({ clientId: this.clientId });
} catch (error) {
throw error;
}
}
public async dispose() {
this._activity.dispose();
try {
if (this._rpc) await this._rpc.destroy();
} catch {}
this._rpc = null;
public dispose() {
this.activity.dispose();
if (this.rpc) this.rpc.destroy();
this.rpc = null;
this.statusBarIcon.tooltip = '';
if (activityTimer) clearInterval(activityTimer);

File diff suppressed because it is too large Load diff

View file

@ -31,27 +31,25 @@ export async function activate(context: ExtensionContext) {
}
}
const enabler = commands.registerCommand('discord.enable', async () => {
await rpc.dispose();
const enabler = commands.registerCommand('discord.enable', () => {
rpc.dispose();
void config.update('enabled', true);
rpc.config = workspace.getConfiguration('discord');
rpc.statusBarIcon.text = '$(pulse) Connecting to Discord...';
rpc.statusBarIcon.show();
await rpc.login();
void rpc.login();
void window.showInformationMessage('Enabled Discord Rich Presence for this workspace.');
});
const disabler = commands.registerCommand('discord.disable', async () => {
const disabler = commands.registerCommand('discord.disable', () => {
void config.update('enabled', false);
rpc.config = workspace.getConfiguration('discord');
await rpc.dispose();
rpc.dispose();
rpc.statusBarIcon.hide();
void window.showInformationMessage('Disabled Discord Rich Presence for this workspace.');
});
const reconnecter = commands.registerCommand('discord.reconnect', async () => {
const reconnecter = commands.registerCommand('discord.reconnect', () => {
if (loginTimeout) clearTimeout(loginTimeout);
await rpc.dispose();
rpc.dispose();
loginTimeout = setTimeout(() => {
void rpc.login();
if (!config.get('silent')) void window.showInformationMessage('Reconnecting to Discord RPC...');
@ -59,28 +57,15 @@ export async function activate(context: ExtensionContext) {
rpc.statusBarIcon.command = 'discord.reconnect';
}, 1000);
});
const disconnect = commands.registerCommand('discord.disconnect', async () => {
await rpc.dispose();
const disconnect = commands.registerCommand('discord.disconnect', () => {
rpc.dispose();
rpc.statusBarIcon.text = '$(pulse) Reconnect to Discord';
rpc.statusBarIcon.command = 'discord.reconnect';
});
const allowSpectate = commands.registerCommand('discord.allowSpectate', async () => {
await rpc.allowSpectate();
});
const disableSpectate = commands.registerCommand('discord.disableSpectate', async () => {
await rpc.disableSpectate();
});
const allowJoinRequests = commands.registerCommand('discord.allowJoinRequests', async () => {
await rpc.allowJoinRequests();
});
const disableJoinRequests = commands.registerCommand('discord.disableJoinRequests', async () => {
await rpc.disableJoinRequests();
});
const allowSpectate = commands.registerCommand('discord.allowSpectate', () => rpc.allowSpectate());
const disableSpectate = commands.registerCommand('discord.disableSpectate', () => rpc.disableSpectate());
const allowJoinRequests = commands.registerCommand('discord.allowJoinRequests', () => rpc.allowJoinRequests());
const disableJoinRequests = commands.registerCommand('discord.disableJoinRequests', () => rpc.disableJoinRequests());
context.subscriptions.push(
enabler,
@ -93,22 +78,13 @@ export async function activate(context: ExtensionContext) {
disableJoinRequests,
);
setTimeout(() => {
const gitExtension = extensions.getExtension<GitExtension>('vscode.git');
if (gitExtension) {
if (gitExtension.isActive) {
rpc.git = gitExtension.exports.getAPI(1);
}
}
}, 5000);
if (!isWorkspaceExcluded && config.get<boolean>('enabled')) {
statusBarIcon.show();
try {
await rpc.login();
} catch (error) {
Logger.log(`Encountered following error after trying to login:\n${error as string}`);
await rpc.dispose();
rpc.dispose();
if (!config.get('silent')) {
if (error?.message?.includes('ENOENT')) void window.showErrorMessage('No Discord Client detected!');
else void window.showErrorMessage(`Couldn't connect to Discord via RPC: ${error as string}`);
@ -117,10 +93,20 @@ export async function activate(context: ExtensionContext) {
rpc.statusBarIcon.command = 'discord.reconnect';
}
}
const gitExtension = extensions.getExtension<GitExtension>('vscode.git');
if (gitExtension) {
if (gitExtension.isActive) {
rpc.git = gitExtension.exports.getAPI(1);
} else {
const extension = await gitExtension.activate();
rpc.git = extension.getAPI(1);
}
}
}
export async function deactivate() {
await rpc.dispose();
export function deactivate() {
rpc.dispose();
}
process.on('unhandledRejection', (err) => Logger.log(err as string));

View file

@ -2,7 +2,7 @@ import { basename, parse, sep } from 'path';
import { debug, Disposable, env, window, workspace } from 'vscode';
import * as vsls from 'vsls';
import RPCClient from '../client/RPCClient';
const lang = require('../data/languages.json'); // eslint-disable-line
import lang from '../data/languages.json';
const knownExtentions: { [key: string]: { image: string } } = lang.knownExtentions;
const knownLanguages: string[] = lang.knownLanguages;
@ -39,10 +39,9 @@ interface FileDetail {
export default class Activity implements Disposable {
private _state: State | null = null;
private _lastKnownFile = '';
private lastKnownFile = '';
// eslint-disable-next-line no-useless-constructor
public constructor(public client: RPCClient) {}
public constructor(private readonly client: RPCClient) {}
public get state() {
return this._state;
@ -52,7 +51,7 @@ export default class Activity implements Disposable {
let largeImageKey: any = 'vscode-big';
if (window.activeTextEditor) {
if (window.activeTextEditor.document.languageId === 'Log') return this._state;
if (this._state && window.activeTextEditor.document.fileName === this._lastKnownFile) {
if (this._state && window.activeTextEditor.document.fileName === this.lastKnownFile) {
return (this._state = {
...this._state,
details: await this._generateDetails(
@ -74,7 +73,7 @@ export default class Activity implements Disposable {
),
});
}
this._lastKnownFile = window.activeTextEditor.document.fileName;
this.lastKnownFile = window.activeTextEditor.document.fileName;
const filename = basename(window.activeTextEditor.document.fileName);
largeImageKey =
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
@ -238,7 +237,7 @@ export default class Activity implements Disposable {
public dispose() {
this._state = null;
this._lastKnownFile = '';
this.lastKnownFile = '';
}
private async _generateDetails(debugging: string, editing: string, idling: string, largeImageKey: any) {

View file

@ -2,14 +2,12 @@ import { OutputChannel, window } from 'vscode';
// eslint-disable-next-line
export default class Logger {
private static _output?: OutputChannel;
private static _setup() {
this._output = this._output ?? window.createOutputChannel('Discord Presence');
}
private static output?: OutputChannel;
public static log(message: string) {
if (!this._output) this._setup();
this._output?.appendLine(message);
if (!this.output) {
this.output = window.createOutputChannel('Discord Presence');
}
this.output.appendLine(message);
}
}

View file

@ -1,20 +1,25 @@
{
"compilerOptions": {
"strict": true,
"module": "commonjs",
"target": "es2017",
"lib": [
"esnext",
"esnext.array",
"esnext.asynciterable",
"esnext.intl",
"esnext.symbol"
],
"rootDir": "src",
"outDir": "dist",
"moduleResolution": "node",
"declaration": false,
"sourceMap": true,
"removeComments": false,
"experimentalDecorators": true
"alwaysStrict": true,
"pretty": true,
"target": "es2017",
"module": "commonjs",
"lib": [
"esnext"
],
"outDir": "dist",
"sourceRoot": "./",
"sourceMap": true,
"inlineSources": true,
"incremental": true,
"noEmitHelpers": true,
"importHelpers": true,
"skipLibCheck": true,
"esModuleInterop": true,
"resolveJsonModule": true
}
}