diff --git a/.gitignore b/.gitignore index 8cc1b675e1..82ab3d27ae 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,6 @@ pids # Miscellaneous .tmp/ !.vscode/launch.json + +.vscode/setting.json +.idea diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000..b4af70f2e4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "github_info.currentFollowers": 99 +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index de9864e189..b32612702c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "utf-8-validate": "^5.0.9" }, "devDependencies": { + "@types/discord-rpc": "^4.0.4", "@types/lodash-es": "^4.17.6", "@types/node": "^17.0.41", "@types/vscode": "^1.67.0", @@ -147,6 +148,12 @@ "node": ">= 8" } }, + "node_modules/@types/discord-rpc": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/discord-rpc/-/discord-rpc-4.0.4.tgz", + "integrity": "sha512-Afr+3GqUqZnQ6bA/WzNp388heVAmfKuNxy6giAde2ZZP7sNr6mnl+ELpcecD/z2sndfKSauLB89ABNqjWWCZlg==", + "dev": true + }, "node_modules/@types/eslint": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", @@ -2914,6 +2921,7 @@ }, "node_modules/register-scheme": { "resolved": "git+ssh://git@github.com/devsnek/node-register-scheme.git#e7cc9a63a1f512565da44cb57316d9fb10750e17", + "hasInstallScript": true, "optional": true, "dependencies": { "bindings": "^1.3.0", @@ -3885,6 +3893,12 @@ "fastq": "^1.6.0" } }, + "@types/discord-rpc": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/discord-rpc/-/discord-rpc-4.0.4.tgz", + "integrity": "sha512-Afr+3GqUqZnQ6bA/WzNp388heVAmfKuNxy6giAde2ZZP7sNr6mnl+ELpcecD/z2sndfKSauLB89ABNqjWWCZlg==", + "dev": true + }, "@types/eslint": { "version": "8.4.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.1.tgz", diff --git a/package.json b/package.json index 0145bd43a4..7262598a04 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,8 @@ "scripts": { "build": "npm run lint && webpack --mode production", "lint": "prettier --check . && eslint src --ext mjs,js,ts", - "format": "prettier --write . && eslint src --ext mjs,js,ts --fix" + "format": "prettier --write . && eslint src --ext mjs,js,ts --fix", + "watch": "webpack --watch" }, "activationEvents": [ "*" @@ -191,6 +192,7 @@ "utf-8-validate": "^5.0.9" }, "devDependencies": { + "@types/discord-rpc": "^4.0.4", "@types/lodash-es": "^4.17.6", "@types/node": "^17.0.41", "@types/vscode": "^1.67.0", @@ -211,4 +213,4 @@ "engines": { "vscode": "^1.63.1" } -} +} \ No newline at end of file diff --git a/src/activity.ts b/src/activity.ts index a3544565ec..ed494c3ea1 100644 --- a/src/activity.ts +++ b/src/activity.ts @@ -1,39 +1,21 @@ import { basename, parse, sep } from 'path'; import { debug, env, Selection, TextDocument, window, workspace } from 'vscode'; +import { log } from './logger'; +import { getConfig, getGit, resolveFileIcon, toLower, toTitle, toUpper } from './util'; +import type { ActivityPayload } from './interfaces/activityPayload.interface'; +import { LogLevel } from './constants/logLevel.constant'; import { CONFIG_KEYS, DEBUG_IMAGE_KEY, - EMPTY, - FAKE_EMPTY, - FILE_SIZES, IDLE_IMAGE_KEY, REPLACE_KEYS, - UNKNOWN_GIT_BRANCH, - UNKNOWN_GIT_REPO_NAME, VSCODE_IMAGE_KEY, VSCODE_INSIDERS_IMAGE_KEY, -} from './constants'; -import { log, LogLevel } from './logger'; -import { getConfig, getGit, resolveFileIcon, toLower, toTitle, toUpper } from './util'; - -interface ActivityPayload { - details?: string | undefined; - state?: string | undefined; - startTimestamp?: number | null | undefined; - largeImageKey?: string | undefined; - largeImageText?: string | undefined; - smallImageKey?: string | undefined; - smallImageText?: string | undefined; - partyId?: string | undefined; - partySize?: number | undefined; - partyMax?: number | undefined; - matchSecret?: string | undefined; - joinSecret?: string | undefined; - spectateSecret?: string | undefined; - buttons?: { label: string; url: string }[] | undefined; - instance?: boolean | undefined; -} +} from './constants/keys.constant'; +import { FILE_SIZES } from './constants/sizes.constant'; +import { UNKNOWN_GIT_BRANCH, UNKNOWN_GIT_REPO_NAME } from './constants/git.constant'; +import { EMPTY, FAKE_EMPTY } from './constants/empty.constant'; async function fileDetails(_raw: string, document: TextDocument, selection: Selection) { let raw = _raw.slice(); diff --git a/src/constants.ts b/src/constants.ts deleted file mode 100644 index 84ddb8b0ff..0000000000 --- a/src/constants.ts +++ /dev/null @@ -1,61 +0,0 @@ -import LANG from './data/languages.json'; - -export const CLIENT_ID = '383226320970055681' as const; - -export const KNOWN_EXTENSIONS: { [key: string]: { image: string } } = LANG.KNOWN_EXTENSIONS; -export const KNOWN_LANGUAGES: { language: string; image: string }[] = LANG.KNOWN_LANGUAGES; - -export const EMPTY = '' as const; -export const FAKE_EMPTY = '\u200b\u200b' as const; -export const FILE_SIZES = [' bytes', 'KB', 'MB', 'GB', 'TB'] as const; - -export const IDLE_IMAGE_KEY = 'vscode-big' as const; -export const DEBUG_IMAGE_KEY = 'debug' as const; -export const VSCODE_IMAGE_KEY = 'vscode' as const; -export const VSCODE_INSIDERS_IMAGE_KEY = 'vscode-insiders' as const; - -export const UNKNOWN_GIT_BRANCH = 'Unknown' as const; -export const UNKNOWN_GIT_REPO_NAME = 'Unknown' as const; - -export const enum REPLACE_KEYS { - Empty = '{empty}', - FileName = '{file_name}', - DirName = '{dir_name}', - FullDirName = '{full_dir_name}', - Workspace = '{workspace}', - VSCodeWorkspace = '(Workspace)', - WorkspaceFolder = '{workspace_folder}', - WorkspaceAndFolder = '{workspace_and_folder}', - LanguageLowerCase = '{lang}', - LanguageTitleCase = '{Lang}', - LanguageUpperCase = '{LANG}', - TotalLines = '{total_lines}', - CurrentLine = '{current_line}', - CurrentColumn = '{current_column}', - FileSize = '{file_size}', - AppName = '{app_name}', - GitRepoName = '{git_repo_name}', - GitBranch = '{git_branch}', -} - -export const enum CONFIG_KEYS { - Enabled = 'enabled', - DetailsIdling = 'detailsIdling', - DetailsEditing = 'detailsEditing', - DetailsDebugging = 'detailsDebugging', - LowerDetailsIdling = 'lowerDetailsIdling', - LowerDetailsEditing = 'lowerDetailsEditing', - LowerDetailsDebugging = 'lowerDetailsDebugging', - LowerDetailsNoWorkspaceFound = 'lowerDetailsNoWorkspaceFound', - LargeImageIdling = 'largeImageIdling', - LargeImage = 'largeImage', - SmallImage = 'smallImage', - SuppressNotifications = 'suppressNotifications', - WorkspaceExcludePatterns = 'workspaceExcludePatterns', - SwapBigAndSmallImage = 'swapBigAndSmallImage', - RemoveDetails = 'removeDetails', - RemoveLowerDetails = 'removeLowerDetails', - RemoveTimestamp = 'removeTimestamp', - RemoveRemoteRepository = 'removeRemoteRepository', - IdleTimeout = 'idleTimeout', -} diff --git a/src/constants/client.constant.ts b/src/constants/client.constant.ts new file mode 100644 index 0000000000..6c714ded27 --- /dev/null +++ b/src/constants/client.constant.ts @@ -0,0 +1 @@ +export const CLIENT_ID = '383226320970055681' as const; diff --git a/src/constants/commands.constant.ts b/src/constants/commands.constant.ts new file mode 100644 index 0000000000..c2e5e00668 --- /dev/null +++ b/src/constants/commands.constant.ts @@ -0,0 +1,5 @@ +export enum CommandsId { + Enable = 'discord.enable', + Disable = 'discord.disable', + Reconnect = 'discord.reconnect', +} diff --git a/src/constants/empty.constant.ts b/src/constants/empty.constant.ts new file mode 100644 index 0000000000..dae5b7dbd1 --- /dev/null +++ b/src/constants/empty.constant.ts @@ -0,0 +1,2 @@ +export const EMPTY = '' as const; +export const FAKE_EMPTY = '\u200b\u200b' as const; diff --git a/src/constants/git.constant.ts b/src/constants/git.constant.ts new file mode 100644 index 0000000000..a3d56a84d6 --- /dev/null +++ b/src/constants/git.constant.ts @@ -0,0 +1,2 @@ +export const UNKNOWN_GIT_BRANCH = 'Unknown' as const; +export const UNKNOWN_GIT_REPO_NAME = 'Unknown' as const; diff --git a/src/constants/keys.constant.ts b/src/constants/keys.constant.ts new file mode 100644 index 0000000000..3ea3b949bd --- /dev/null +++ b/src/constants/keys.constant.ts @@ -0,0 +1,48 @@ +export const enum REPLACE_KEYS { + Empty = '{empty}', + FileName = '{file_name}', + DirName = '{dir_name}', + FullDirName = '{full_dir_name}', + Workspace = '{workspace}', + VSCodeWorkspace = '(Workspace)', + WorkspaceFolder = '{workspace_folder}', + WorkspaceAndFolder = '{workspace_and_folder}', + LanguageLowerCase = '{lang}', + LanguageTitleCase = '{Lang}', + LanguageUpperCase = '{LANG}', + TotalLines = '{total_lines}', + CurrentLine = '{current_line}', + CurrentColumn = '{current_column}', + FileSize = '{file_size}', + AppName = '{app_name}', + GitRepoName = '{git_repo_name}', + GitBranch = '{git_branch}', +} + +export const enum CONFIG_KEYS { + Enabled = 'enabled', + DetailsIdling = 'detailsIdling', + DetailsEditing = 'detailsEditing', + DetailsDebugging = 'detailsDebugging', + LowerDetailsIdling = 'lowerDetailsIdling', + LowerDetailsEditing = 'lowerDetailsEditing', + LowerDetailsDebugging = 'lowerDetailsDebugging', + LowerDetailsNoWorkspaceFound = 'lowerDetailsNoWorkspaceFound', + LargeImageIdling = 'largeImageIdling', + LargeImage = 'largeImage', + SmallImage = 'smallImage', + SuppressNotifications = 'suppressNotifications', + WorkspaceExcludePatterns = 'workspaceExcludePatterns', + SwapBigAndSmallImage = 'swapBigAndSmallImage', + RemoveDetails = 'removeDetails', + RemoveLowerDetails = 'removeLowerDetails', + RemoveTimestamp = 'removeTimestamp', + RemoveRemoteRepository = 'removeRemoteRepository', + IdleTimeout = 'idleTimeout', +} + + +export const IDLE_IMAGE_KEY = 'vscode-big' as const; +export const DEBUG_IMAGE_KEY = 'debug' as const; +export const VSCODE_IMAGE_KEY = 'vscode' as const; +export const VSCODE_INSIDERS_IMAGE_KEY = 'vscode-insiders' as const; diff --git a/src/constants/languages.constant.ts b/src/constants/languages.constant.ts new file mode 100644 index 0000000000..8a815fbe22 --- /dev/null +++ b/src/constants/languages.constant.ts @@ -0,0 +1,4 @@ +import LANG from '../data/languages.json'; + +export const KNOWN_EXTENSIONS: { [key: string]: { image: string } } = LANG.KNOWN_EXTENSIONS; +export const KNOWN_LANGUAGES: { language: string; image: string }[] = LANG.KNOWN_LANGUAGES; diff --git a/src/constants/logLevel.constant.ts b/src/constants/logLevel.constant.ts new file mode 100644 index 0000000000..ba57aea967 --- /dev/null +++ b/src/constants/logLevel.constant.ts @@ -0,0 +1,7 @@ +export const enum LogLevel { + Trace = 'TRACE', + Debug = 'DEBUG', + Info = 'INFO', + Warn = 'WARN', + Error = 'ERROR', +} diff --git a/src/constants/sizes.constant.ts b/src/constants/sizes.constant.ts new file mode 100644 index 0000000000..5adeb862da --- /dev/null +++ b/src/constants/sizes.constant.ts @@ -0,0 +1 @@ +export const FILE_SIZES = [' bytes', 'KB', 'MB', 'GB', 'TB'] as const; diff --git a/src/extension.ts b/src/extension.ts index 4f58f447b9..5e7696e548 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,13 +1,17 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access */ -const { Client } = require('discord-rpc'); // eslint-disable-line +import { Client } from 'discord-rpc'; import { commands, ExtensionContext, StatusBarAlignment, StatusBarItem, window, workspace, debug } from 'vscode'; import throttle from 'lodash-es/throttle'; import { activity } from './activity'; -import { CLIENT_ID, CONFIG_KEYS } from './constants'; -import { log, LogLevel } from './logger'; +import { log } from './logger'; import { getConfig, getGit } from './util'; +import { LogLevel } from './constants/logLevel.constant'; +import { CONFIG_KEYS } from './constants/keys.constant'; +import { CLIENT_ID } from './constants/client.constant'; +import type { ActivityPayload } from './interfaces/activityPayload.interface'; +import { CommandsId } from './constants/commands.constant'; const statusBarIcon: StatusBarItem = window.createStatusBarItem(StatusBarAlignment.Left); statusBarIcon.text = '$(pulse) Connecting to Discord...'; @@ -27,10 +31,11 @@ export function cleanUp() { } async function sendActivity() { + const activityPayload: ActivityPayload = await activity(state); state = { - ...(await activity(state)), + ...activityPayload, }; - rpc.setActivity(state); + await rpc.setActivity(state); } async function login() { @@ -57,22 +62,21 @@ async function login() { cleanUp(); rpc.destroy(); statusBarIcon.text = '$(pulse) Reconnect to Discord'; - statusBarIcon.command = 'discord.reconnect'; + statusBarIcon.command = CommandsId.Reconnect; }); - try { await rpc.login({ clientId: CLIENT_ID }); } catch (error) { log(LogLevel.Error, `Encountered following error while trying to login:\n${error as string}`); cleanUp(); - rpc.destroy(); + await rpc.destroy(); if (!config[CONFIG_KEYS.SuppressNotifications]) { // @ts-expect-error 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}`); } statusBarIcon.text = '$(pulse) Reconnect to Discord'; - statusBarIcon.command = 'discord.reconnect'; + statusBarIcon.command = CommandsId.Reconnect; } } @@ -117,18 +121,18 @@ export async function activate(context: ExtensionContext) { statusBarIcon.hide(); }; - const enabler = commands.registerCommand('discord.enable', async () => { + const enabler = commands.registerCommand(CommandsId.Enable, async () => { await disable(); await enable(); await window.showInformationMessage('Enabled Discord Presence for this workspace'); }); - const disabler = commands.registerCommand('discord.disable', async () => { + const disabler = commands.registerCommand(CommandsId.Disable, async () => { await disable(); await window.showInformationMessage('Disabled Discord Presence for this workspace'); }); - const reconnecter = commands.registerCommand('discord.reconnect', async () => { + const reconnecter = commands.registerCommand(CommandsId.Reconnect, async () => { await disable(false); await enable(false); }); @@ -136,7 +140,7 @@ export async function activate(context: ExtensionContext) { const disconnect = commands.registerCommand('discord.disconnect', async () => { await disable(false); statusBarIcon.text = '$(pulse) Reconnect to Discord'; - statusBarIcon.command = 'discord.reconnect'; + statusBarIcon.command = CommandsId.Reconnect; statusBarIcon.show(); }); diff --git a/src/interfaces/activityPayload.interface.ts b/src/interfaces/activityPayload.interface.ts new file mode 100644 index 0000000000..49bb6cb012 --- /dev/null +++ b/src/interfaces/activityPayload.interface.ts @@ -0,0 +1,17 @@ +export interface ActivityPayload { + details?: string | undefined; + state?: string | undefined; + startTimestamp?: number | null | undefined; + largeImageKey?: string | undefined; + largeImageText?: string | undefined; + smallImageKey?: string | undefined; + smallImageText?: string | undefined; + partyId?: string | undefined; + partySize?: number | undefined; + partyMax?: number | undefined; + matchSecret?: string | undefined; + joinSecret?: string | undefined; + spectateSecret?: string | undefined; + buttons?: { label: string; url: string }[] | undefined; + instance?: boolean | undefined; +} diff --git a/src/logger.ts b/src/logger.ts index 5e8e8c58ed..a22d036ceb 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,33 +1,28 @@ -import { window } from 'vscode'; +import {window} from 'vscode'; import dayjs from 'dayjs'; +import type {LogLevel} from "./constants/logLevel.constant"; const outputChannel = window.createOutputChannel('Discord Presence'); -export const enum LogLevel { - Trace = 'TRACE', - Debug = 'DEBUG', - Info = 'INFO', - Warn = 'WARN', - Error = 'ERROR', -} function send(level: string, message: string) { - outputChannel.appendLine(`[${dayjs().format('DD/MM/YYYY HH:mm:ss')} - ${level}] ${message}`); + outputChannel.appendLine(`[${dayjs().format('DD/MM/YYYY HH:mm:ss')} - ${level}] ${message}`); } export function log(level: LogLevel, message: string | Error) { - if (typeof message === 'string') { - send(level, message); - } else if (message instanceof Error) { - send(level, message.message); + if (typeof message === 'string') { + send(level, message); + } else if (message instanceof Error) { + send(level, message.message); - if (message.stack) { - send(level, message.stack); - } - } else if (typeof message === 'object') { - try { - const json = JSON.stringify(message, null, 2); - send(level, json); - } catch {} - } + if (message.stack) { + send(level, message.stack); + } + } else if (typeof message === 'object') { + try { + const json = JSON.stringify(message, null, 2); + send(level, json); + } catch { + } + } } diff --git a/src/util.ts b/src/util.ts index 9bd24dfd22..41cc2c64ab 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,13 +1,14 @@ import { basename } from 'path'; import { TextDocument, workspace, extensions, WorkspaceConfiguration } from 'vscode'; -import { KNOWN_EXTENSIONS, KNOWN_LANGUAGES } from './constants'; import type { API, GitExtension } from './git'; -import { log, LogLevel } from './logger'; +import { log } from './logger'; +import { LogLevel } from './constants/logLevel.constant'; +import { KNOWN_EXTENSIONS, KNOWN_LANGUAGES } from './constants/languages.constant'; let git: API | null | undefined; -type WorkspaceExtensionConfiguration = WorkspaceConfiguration & { +export type WorkspaceExtensionConfiguration = WorkspaceConfiguration & { enabled: boolean; detailsIdling: string; detailsEditing: string; @@ -29,7 +30,7 @@ type WorkspaceExtensionConfiguration = WorkspaceConfiguration & { idleTimeout: number; }; -export function getConfig() { +export function getConfig(): WorkspaceExtensionConfiguration { return workspace.getConfiguration('discord') as WorkspaceExtensionConfiguration; }