feat: migration to TypeScript FINAL

Co-authored-by: Su-Yong <simssy2205@gmail.com>
This commit is contained in:
JellyBrick
2023-09-04 02:27:53 +09:00
parent c0d7972da3
commit 53f5bda382
72 changed files with 1290 additions and 693 deletions

View File

@ -1,17 +1,20 @@
const path = require('node:path');
import path from 'node:path';
const { globalShortcut } = require('electron');
import { globalShortcut, BrowserWindow } from 'electron';
const { injectCSS } = require('../utils');
import { injectCSS } from '../utils';
import type { ConfigType } from '../../config/dynamic';
/*
This is used to determine if plugin is actually active
(not if its only enabled in options)
(not if it's only enabled in options)
*/
let enabled = false;
let isEnabled = false;
module.exports = (win, options) => {
enabled = true;
export const enabled = () => isEnabled;
export default (win: BrowserWindow, options: ConfigType<'precise-volume'>) => {
isEnabled = true;
injectCSS(win.webContents, path.join(__dirname, 'volume-hud.css'));
if (options.globalShortcuts?.volumeUp) {
@ -22,5 +25,3 @@ module.exports = (win, options) => {
globalShortcut.register((options.globalShortcuts.volumeDown), () => win.webContents.send('changeVolume', false));
}
};
module.exports.enabled = () => enabled;

View File

@ -1,22 +1,25 @@
const { ipcRenderer } = require('electron');
import { ipcRenderer } from 'electron';
const { setOptions, setMenuOptions, isEnabled } = require('../../config/plugins');
import { setOptions, setMenuOptions, isEnabled } from '../../config/plugins';
import { debounce } from '../../providers/decorators';
function $(selector) {
import { YoutubePlayer } from '../../types/youtube-player';
import type { ConfigType } from '../../config/dynamic';
function $(selector: string) {
return document.querySelector(selector);
}
const { debounce } = require('../../providers/decorators');
let api: YoutubePlayer;
let options: ConfigType<'precise-volume'>;
let api;
let options;
module.exports = (_options) => {
export default (_options: ConfigType<'precise-volume'>) => {
options = _options;
document.addEventListener('apiLoaded', (e) => {
api = e.detail;
ipcRenderer.on('changeVolume', (_, toIncrease) => changeVolume(toIncrease));
ipcRenderer.on('setVolume', (_, value) => setVolume(value));
ipcRenderer.on('changeVolume', (_, toIncrease: boolean) => changeVolume(toIncrease));
ipcRenderer.on('setVolume', (_, value: number) => setVolume(value));
firstRun();
}, { once: true, passive: true });
};
@ -26,23 +29,22 @@ const writeOptions = debounce(() => {
setOptions('precise-volume', options);
}, 1000);
const moveVolumeHud = debounce((showVideo) => {
const volumeHud = $('#volumeHud');
export const moveVolumeHud = debounce((showVideo: boolean) => {
const volumeHud = $('#volumeHud') as HTMLElement | undefined;
if (!volumeHud) {
return;
}
volumeHud.style.top = showVideo
? `${($('ytmusic-player').clientHeight - $('video').clientHeight) / 2}px`
: 0;
? `${($('ytmusic-player')!.clientHeight - $('video')!.clientHeight) / 2}px`
: '0';
}, 250);
module.exports.moveVolumeHud = moveVolumeHud;
const hideVolumeHud = debounce((volumeHud) => {
volumeHud.style.opacity = 0;
const hideVolumeHud = debounce((volumeHud: HTMLElement) => {
volumeHud.style.opacity = '0';
}, 2000);
const hideVolumeSlider = debounce((slider) => {
const hideVolumeSlider = debounce((slider: HTMLElement) => {
slider.classList.remove('on-hover');
}, 2500);
@ -61,14 +63,15 @@ function firstRun() {
setupLocalArrowShortcuts();
const noVid = $('#main-panel')?.computedStyleMap().get('display').value === 'none';
// Workaround: computedStyleMap().get(string) returns CSSKeywordValue instead of CSSStyleValue
const noVid = ($('#main-panel')?.computedStyleMap().get('display') as CSSKeywordValue)?.value === 'none';
injectVolumeHud(noVid);
if (!noVid) {
setupVideoPlayerOnwheel();
if (!isEnabled('video-toggle')) {
// Video-toggle handles hud positioning on its own
const videoMode = () => api.getPlayerResponse().videoDetails?.musicVideoType !== 'MUSIC_VIDEO_TYPE_ATV';
$('video').addEventListener('srcChanged', () => moveVolumeHud(videoMode()));
$('video')?.addEventListener('srcChanged', () => moveVolumeHud(videoMode()));
}
}
@ -79,51 +82,55 @@ function firstRun() {
});
}
function injectVolumeHud(noVid) {
function injectVolumeHud(noVid: boolean) {
if (noVid) {
const position = 'top: 18px; right: 60px;';
const mainStyle = 'font-size: xx-large;';
$('.center-content.ytmusic-nav-bar').insertAdjacentHTML('beforeend',
$('.center-content.ytmusic-nav-bar')?.insertAdjacentHTML('beforeend',
`<span id="volumeHud" style="${position + mainStyle}"></span>`);
} else {
const position = 'top: 10px; left: 10px;';
const mainStyle = 'font-size: xxx-large; webkit-text-stroke: 1px black; font-weight: 600;';
$('#song-video').insertAdjacentHTML('afterend',
$('#song-video')?.insertAdjacentHTML('afterend',
`<span id="volumeHud" style="${position + mainStyle}"></span>`);
}
}
function showVolumeHud(volume) {
const volumeHud = $('#volumeHud');
function showVolumeHud(volume: number) {
const volumeHud = $('#volumeHud') as HTMLElement | undefined;
if (!volumeHud) {
return;
}
volumeHud.textContent = `${volume}%`;
volumeHud.style.opacity = 1;
volumeHud.style.opacity = '1';
hideVolumeHud(volumeHud);
}
/** Add onwheel event to video player */
function setupVideoPlayerOnwheel() {
$('#main-panel').addEventListener('wheel', (event) => {
const panel = $('#main-panel') as HTMLElement | undefined;
if (!panel) return;
panel.addEventListener('wheel', (event) => {
event.preventDefault();
// Event.deltaY < 0 means wheel-up
changeVolume(event.deltaY < 0);
});
}
function saveVolume(volume) {
function saveVolume(volume: number) {
options.savedVolume = volume;
writeOptions();
}
/** Add onwheel event to play bar and also track if play bar is hovered */
function setupPlaybar() {
const playerbar = $('ytmusic-player-bar');
const playerbar = $('ytmusic-player-bar') as HTMLElement | undefined;
if (!playerbar) return;
playerbar.addEventListener('wheel', (event) => {
event.preventDefault();
@ -148,23 +155,28 @@ function setupSliderObserver() {
const sliderObserver = new MutationObserver((mutations) => {
for (const mutation of mutations) {
// This checks that volume-slider was manually set
if (mutation.oldValue !== mutation.target.value
&& (typeof options.savedVolume !== 'number' || Math.abs(options.savedVolume - mutation.target.value) > 4)) {
const target = mutation.target as HTMLInputElement;
const targetValueNumeric = Number(target.value);
if (mutation.oldValue !== target.value
&& (typeof options.savedVolume !== 'number' || Math.abs(options.savedVolume - targetValueNumeric) > 4)) {
// Diff>4 means it was manually set
setTooltip(mutation.target.value);
saveVolume(mutation.target.value);
setTooltip(targetValueNumeric);
saveVolume(targetValueNumeric);
}
}
});
const slider = $('#volume-slider');
if (!slider) return;
// Observing only changes in 'value' of volume-slider
sliderObserver.observe($('#volume-slider'), {
sliderObserver.observe(slider, {
attributeFilter: ['value'],
attributeOldValue: true,
});
}
function setVolume(value) {
function setVolume(value: number) {
api.setVolume(value);
// Save the new volume
saveVolume(value);
@ -181,7 +193,7 @@ function setVolume(value) {
}
/** If (toIncrease = false) then volume decrease */
function changeVolume(toIncrease) {
function changeVolume(toIncrease: boolean) {
// Apply volume change if valid
const steps = Number(options.steps || 1);
setVolume(toIncrease
@ -190,17 +202,20 @@ function changeVolume(toIncrease) {
}
function updateVolumeSlider() {
const savedVolume = options.savedVolume ?? 0;
// Slider value automatically rounds to multiples of 5
for (const slider of ['#volume-slider', '#expand-volume-slider']) {
$(slider).value
= options.savedVolume > 0 && options.savedVolume < 5
($(slider) as HTMLInputElement).value
= String(savedVolume > 0 && savedVolume < 5
? 5
: options.savedVolume;
: savedVolume);
}
}
function showVolumeSlider() {
const slider = $('#volume-slider');
const slider = $('#volume-slider') as HTMLElement | null;
if (!slider) return;
// This class display the volume slider if not in minimized mode
slider.classList.add('on-hover');
@ -215,16 +230,16 @@ const tooltipTargets = [
'#expand-volume',
];
function setTooltip(volume) {
function setTooltip(volume: number) {
for (const target of tooltipTargets) {
$(target).title = `${volume}%`;
($(target) as HTMLElement).title = `${volume}%`;
}
}
function setupLocalArrowShortcuts() {
if (options.arrowsShortcut) {
window.addEventListener('keydown', (event) => {
if ($('ytmusic-search-box').opened) {
if (($('ytmusic-search-box') as (HTMLElement & { opened: boolean }) | null)?.opened) {
return;
}

View File

@ -1,15 +1,20 @@
const prompt = require('custom-electron-prompt');
import prompt, { KeybindOptions } from 'custom-electron-prompt';
const { enabled } = require('./back');
import { BrowserWindow, MenuItem } from 'electron';
const { setMenuOptions } = require('../../config/plugins');
const promptOptions = require('../../providers/prompt-options');
import { enabled } from './back';
function changeOptions(changedOptions, options, win) {
import { setMenuOptions } from '../../config/plugins';
import promptOptions from '../../providers/prompt-options';
import { MenuTemplate } from '../../menu';
import type { ConfigType } from '../../config/dynamic';
function changeOptions(changedOptions: Partial<ConfigType<'precise-volume'>>, options: ConfigType<'precise-volume'>, win: BrowserWindow) {
for (const option in changedOptions) {
options[option] = changedOptions[option];
// HACK: Weird TypeScript error
(options as Record<string, unknown>)[option] = (changedOptions as Record<string, unknown>)[option];
}
// Dynamically change setting if plugin is enabled
if (enabled()) {
win.webContents.send('setOptions', changedOptions);
@ -18,7 +23,7 @@ function changeOptions(changedOptions, options, win) {
}
}
module.exports = (win, options) => [
export default (win: BrowserWindow, options: ConfigType<'precise-volume'>): MenuTemplate => [
{
label: 'Local Arrowkeys Controls',
type: 'checkbox',
@ -40,9 +45,9 @@ module.exports = (win, options) => [
];
// Helper function for globalShortcuts prompt
const kb = (label_, value_, default_) => ({ value: value_, label: label_, default: default_ || undefined });
const kb = (label_: string, value_: string, default_: string): KeybindOptions => ({ 'value': value_, 'label': label_, 'default': default_ || undefined });
async function promptVolumeSteps(win, options) {
async function promptVolumeSteps(win: BrowserWindow, options: ConfigType<'precise-volume'>) {
const output = await prompt({
title: 'Volume Steps',
label: 'Choose Volume Increase/Decrease Steps',
@ -58,7 +63,7 @@ async function promptVolumeSteps(win, options) {
}
}
async function promptGlobalShortcuts(win, options, item) {
async function promptGlobalShortcuts(win: BrowserWindow, options: ConfigType<'precise-volume'>, item: MenuItem) {
const output = await prompt({
title: 'Global Volume Keybinds',
label: 'Choose Global Volume Keybinds:',
@ -71,9 +76,12 @@ async function promptGlobalShortcuts(win, options, item) {
}, win);
if (output) {
const newGlobalShortcuts = {};
const newGlobalShortcuts: {
volumeUp: string;
volumeDown: string;
} = { volumeUp: '', volumeDown: '' };
for (const { value, accelerator } of output) {
newGlobalShortcuts[value] = accelerator;
newGlobalShortcuts[value as keyof typeof newGlobalShortcuts] = accelerator;
}
changeOptions({ globalShortcuts: newGlobalShortcuts }, options, win);

View File

@ -1,32 +0,0 @@
const is = require('electron-is');
let ignored = {
id: ['volume-slider', 'expand-volume-slider'],
types: ['mousewheel', 'keydown', 'keyup'],
};
function overrideAddEventListener() {
// Save native addEventListener
Element.prototype._addEventListener = Element.prototype.addEventListener;
// Override addEventListener to Ignore specific events in volume-slider
Element.prototype.addEventListener = function (type, listener, useCapture = false) {
if (!(
ignored.id.includes(this.id)
&& ignored.types.includes(type)
)) {
this._addEventListener(type, listener, useCapture);
} else if (is.dev()) {
console.log(`Ignoring event: "${this.id}.${type}()"`);
}
};
}
module.exports = () => {
overrideAddEventListener();
// Restore original function after finished loading to avoid keeping Element.prototype altered
window.addEventListener('load', () => {
Element.prototype.addEventListener = Element.prototype._addEventListener;
Element.prototype._addEventListener = undefined;
ignored = undefined;
}, { once: true });
};

View File

@ -0,0 +1,41 @@
/* what */
/* eslint-disable @typescript-eslint/ban-ts-comment */
import is from 'electron-is';
const ignored = {
id: ['volume-slider', 'expand-volume-slider'],
types: ['mousewheel', 'keydown', 'keyup'],
};
function overrideAddEventListener() {
// YO WHAT ARE YOU DOING NOW?!?!
// Save native addEventListener
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/unbound-method
Element.prototype._addEventListener = Element.prototype.addEventListener;
// Override addEventListener to Ignore specific events in volume-slider
Element.prototype.addEventListener = function (type: string, listener: (event: Event) => void, useCapture = false) {
if (!(
ignored.id.includes(this.id)
&& ignored.types.includes(type)
)) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access
(this as any)._addEventListener(type, listener, useCapture);
} else if (is.dev()) {
console.log(`Ignoring event: "${this.id}.${type}()"`);
}
};
}
export default () => {
overrideAddEventListener();
// Restore original function after finished loading to avoid keeping Element.prototype altered
window.addEventListener('load', () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-member-access
Element.prototype.addEventListener = (Element.prototype as any)._addEventListener;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-member-access
(Element.prototype as any)._addEventListener = undefined;
}, { once: true });
};