From e4570832e891cf8741f6db26a3f3d3e6f9e50d54 Mon Sep 17 00:00:00 2001 From: alexbcberio Date: Fri, 7 Jan 2022 17:24:17 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Create=20lightTheme=20action?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes vscode theme and windows color scheme --- .../actions/lightTheme/helpers.ts | 151 ++++++++++++++++++ .../pubSubClient/actions/lightTheme/index.ts | 107 +++++++++++++ .../pubSubClient/actions/lightTheme/types.ts | 15 ++ 3 files changed, 273 insertions(+) create mode 100644 src/backend/pubSubClient/actions/lightTheme/helpers.ts create mode 100644 src/backend/pubSubClient/actions/lightTheme/index.ts create mode 100644 src/backend/pubSubClient/actions/lightTheme/types.ts diff --git a/src/backend/pubSubClient/actions/lightTheme/helpers.ts b/src/backend/pubSubClient/actions/lightTheme/helpers.ts new file mode 100644 index 0000000..1e42053 --- /dev/null +++ b/src/backend/pubSubClient/actions/lightTheme/helpers.ts @@ -0,0 +1,151 @@ +import * as regedit from "regedit"; + +import { ColorTheme, RegeditListResult, RegisterColorTheme } from "./types"; +import { readFile, stat, writeFile } from "fs/promises"; +import { vsCodeDarkTheme, vsCodeLightTheme } from "."; + +import { platform } from "os"; +import { resolve } from "path"; + +const isWindows = platform() === "win32"; +const registerColorThemePath = + "HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; + +function vsCodeSettingsPath(): string { + if (!isWindows) { + throw new Error("This function only supports win32 platform"); + } + + const vsCodeSettings = resolve( + // @ts-expect-error Type string | undefined is not assignable + process.env.APPDATA, + "./Code/User/settings.json" + ); + + return vsCodeSettings; +} + +async function existsVsCodeSettings(): Promise { + try { + await stat(vsCodeSettingsPath()); + } catch (e) { + return false; + } + + return true; +} + +async function changeVsCodeColorTheme(colorTheme: ColorTheme): Promise { + let theme: string; + + switch (colorTheme) { + case "light": + theme = vsCodeLightTheme; + break; + case "dark": + default: + theme = vsCodeDarkTheme; + } + + if (!theme) { + return; + } + + const filePath = vsCodeSettingsPath(); + const settings = JSON.parse((await readFile(filePath)).toString()); + + const colorThemeSettingKey = "workbench.colorTheme"; + + if (settings[colorThemeSettingKey] === theme) { + return; + } + + settings[colorThemeSettingKey] = theme; + + await writeFile(filePath, JSON.stringify(settings)); +} + +function regeditList(keys: string | Array): Promise { + const listKeys = Array.isArray(keys) ? keys : [keys]; + + return new Promise((res, rej) => { + regedit.list(listKeys, (err, result) => { + if (err) { + rej(err); + } + + res(result); + }); + }); +} + +function regeditPut(values: regedit.RegistryItemPutCollection): Promise { + return new Promise((res, rej) => { + regedit.putValue(values, (err) => { + if (err) { + rej(err); + } + + res(); + }); + }); +} + +async function changeRegisterColorTheme( + value: RegisterColorTheme +): Promise { + const type = "REG_DWORD"; + + const values: regedit.RegistryItemPutCollection = { + [registerColorThemePath]: { + SystemUsesLightTheme: { + value, + type, + }, + AppsUseLightTheme: { + value, + type, + }, + }, + }; + + await regeditPut(values); +} + +function registerColorThemeValue(colorTheme: ColorTheme): RegisterColorTheme { + let registerValue: RegisterColorTheme; + + switch (colorTheme) { + case "light": + registerValue = RegisterColorTheme.Light; + break; + case "dark": + default: + registerValue = RegisterColorTheme.Dark; + } + + return registerValue; +} + +async function changeWindowsColorTheme(colorTheme: ColorTheme): Promise { + const registerValue = registerColorThemeValue(colorTheme); + const listResult = await regeditList(registerColorThemePath); + const keyValues = listResult[registerColorThemePath].values; + const { AppsUseLightTheme, SystemUsesLightTheme } = keyValues; + + if ( + AppsUseLightTheme.value === registerValue && + SystemUsesLightTheme.value === registerValue + ) { + return; + } + + await changeRegisterColorTheme(registerValue); +} + +export { + isWindows, + existsVsCodeSettings, + changeVsCodeColorTheme, + changeWindowsColorTheme, +}; diff --git a/src/backend/pubSubClient/actions/lightTheme/index.ts b/src/backend/pubSubClient/actions/lightTheme/index.ts new file mode 100644 index 0000000..4aaf90b --- /dev/null +++ b/src/backend/pubSubClient/actions/lightTheme/index.ts @@ -0,0 +1,107 @@ +import { + changeVsCodeColorTheme, + changeWindowsColorTheme, + existsVsCodeSettings, + isWindows, +} from "./helpers"; + +import { ColorTheme } from "./types"; +import { LOG_PREFIX } from "../.."; +import { RedemptionMessage } from "../../../../interfaces/RedemptionMessage"; +import { isProduction } from "../../../helpers/util"; + +const vsCodeLightTheme = "Min Light"; +const vsCodeDarkTheme = "Min Dark"; + +const minEventDuration = 10; + +let restoreAt = 0; +let restoreTimeout: NodeJS.Timeout | null; + +function calculateEventDurationMs(rewardCost: number): number { + const reduceBase = 200; + const ms = 1e3; + + const eventDuration = Math.max(minEventDuration, rewardCost - reduceBase); + + return eventDuration * ms; +} + +function msText(ms: number): string { + let amountTime: number; + let amountUnit: string; + + const second = 1e3; + const minute = 60e3; + + if (ms >= minute) { + amountTime = ms / minute; + amountUnit = "minuto"; + } else { + amountTime = ms / second; + amountUnit = "segundo"; + } + + const decimalPrecision = 1e2; + const roundedAmountTime = + Math.round(amountTime * decimalPrecision) / decimalPrecision; + // eslint-disable-next-line no-magic-numbers + const pluralize = roundedAmountTime > 1 ? "s" : ""; + + return `${roundedAmountTime} ${amountUnit}${pluralize}`; +} + +async function lightTheme(msg: RedemptionMessage): Promise { + if (!isWindows) { + throw new Error("Only available on Windows platform"); + } + + const colorTheme: ColorTheme = "light"; + + if (isProduction) { + if (await existsVsCodeSettings()) { + await changeVsCodeColorTheme(colorTheme); + } + + await changeWindowsColorTheme(colorTheme); + } else { + console.log( + `${LOG_PREFIX}Light Theme not changed to ${colorTheme} (not production)` + ); + } + + const eventDuration = calculateEventDurationMs(msg.rewardCost); + + if (restoreTimeout) { + clearTimeout(restoreTimeout); + + msg.message = `Aumentado el tiempo con tema claro por ${msText( + eventDuration + )}`; + } else { + restoreAt = Date.now(); + + msg.message = `Disfruta de un dia iluminado`; + } + + restoreAt += eventDuration; + + const timeoutTime = restoreAt - Date.now(); + + msg.message += ` (tiempo acumulado ${msText(timeoutTime)})`; + + restoreTimeout = setTimeout(async () => { + const colorTheme: ColorTheme = "dark"; + + restoreTimeout = null; + + await Promise.all([ + changeVsCodeColorTheme(colorTheme), + changeWindowsColorTheme(colorTheme), + ]); + }, timeoutTime); + + return msg; +} + +export { lightTheme, vsCodeDarkTheme, vsCodeLightTheme }; diff --git a/src/backend/pubSubClient/actions/lightTheme/types.ts b/src/backend/pubSubClient/actions/lightTheme/types.ts new file mode 100644 index 0000000..e574918 --- /dev/null +++ b/src/backend/pubSubClient/actions/lightTheme/types.ts @@ -0,0 +1,15 @@ +import * as regedit from "regedit"; + +type ColorTheme = "light" | "dark"; + +type RegeditListResult = Record; + +const registerDarkValue = 0; +const registerLightValue = 1; + +enum RegisterColorTheme { + Dark = registerDarkValue, + Light = registerLightValue, +} + +export { ColorTheme, RegeditListResult, RegisterColorTheme };