From 21ba0bc8881bf7c8ab304bab96026d3d9be8db4c Mon Sep 17 00:00:00 2001 From: Alex Simons Date: Sat, 28 Aug 2021 16:56:10 -0500 Subject: [PATCH] Auto Asset Restoration. (#110) * First pass asset restoration * Fixed sum bugs * Updated welcome page * Words are hard --- .vscode/settings.json | 8 +- CHANGELOG.md | 5 + README.md | 7 ++ package.json | 79 ++++++++------- src/AutoInstaller.ts | 192 +++++++++++++++++++++++++++++++++++++ src/NotificationService.ts | 2 +- src/StickerService.ts | 18 +++- src/ThemeManager.ts | 30 ++++-- src/extension.ts | 8 ++ 9 files changed, 296 insertions(+), 53 deletions(-) create mode 100644 src/AutoInstaller.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 30bf8c2..b17574a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,5 +7,11 @@ "out": true // set this to false to include "out" folder in search results }, // Turn off tsc task auto detection since we have the necessary tasks as npm scripts - "typescript.tsc.autoDetect": "off" + "typescript.tsc.autoDetect": "off", + "cSpell.words": [ + "accum", + "doki", + "unthrottled", + "waifu" + ] } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b2d55f..32e276d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +# 15.3.0 [Auto Restoration] + +- Added the **Restore Assets** action that allows you to quickly re-install assets after a VSCode update. Plugin will attempt to restore assets on first detection of VSCode update. +- Enhanced the wallpaper in the Welcome Screen. + # 15.2.0 [Hide Watermark] - Added the **Hide VSCode Watermark** command that...well... hides the VS Code watermark that shows when all editor tabs are closed. diff --git a/README.md b/README.md index b8b6061..4de4904 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ You can choose themes from various, Anime, Manga, or Visual Novels: - [Hide VSCode Watermark](#hide-watermark) - [Stickers](#sticker) - [Custom Assets](#custom-assets) + - [Asset Restoration](#asset-restoration) - [Suggestive Content](#suggestive-content) - [Asset Removal](#remove-assets) - [Show Changelog](#show-changelog) @@ -148,6 +149,12 @@ Value to be used for css 'background-position' for both the background & wallpap "doki.background.anchor": "center", ``` +## Asset Restoration + +Unfortunately, every time VSCode updates, you will lose your installed asset changes. +Thankfully, this plugin will remember what assets you installed, and attempt to restore them on the first detection of a VSCode update. + +If the restoration does not work the first time, you can fix the issue then run the **Restore Assets** command to quickly get you back to coding! ## Suggestive Content diff --git a/package.json b/package.json index 1c6ed8e..f5fae00 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "The Doki Theme", "description": "A bunch of themes with cute anime girls. Code with your waifu!", "publisher": "unthrottled", - "version": "15.2.0", + "version": "15.3.0", "license": "MIT", "icon": "Doki-Theme.png", "galleryBanner": { @@ -31,6 +31,7 @@ "activationEvents": [ "onStartupFinished", "onCommand:doki-theme.doki.changelog", + "onCommand:doki-theme.restore.assets", "onCommand:doki-theme.remove.sticker", "onCommand:doki-theme.remove.watermark", "onCommand:doki-theme.theme.Maika", @@ -222,6 +223,10 @@ "command": "doki-theme.remove.sticker", "title": "Doki-Theme: Remove Sticker/Background" }, + { + "command": "doki-theme.restore.assets", + "title": "Doki-Theme: Restore Assets" + }, { "command": "doki-theme.remove.watermark", "title": "Doki-Theme: Hide VSCode Watermark" @@ -840,12 +845,6 @@ "path": "./generatedThemes/Maika.theme.json", "uiTheme": "vs-dark" }, - { - "id": "0527c6fc-316a-4f80-9459-d92ced0e6492", - "label": "Doki Theme: BunnySenpai: Mai", - "path": "./generatedThemes/Mai Dark.theme.json", - "uiTheme": "vs-dark" - }, { "id": "98878c8e-9f91-4e25-930d-dd7d280d9e35", "label": "Doki Theme: BunnySenpai: Mai", @@ -853,9 +852,9 @@ "uiTheme": "vs" }, { - "id": "dce48196-ff46-470c-b5f9-d1e23f4a79d3", - "label": "Doki Theme: DDLC: Monika", - "path": "./generatedThemes/Monika Dark.theme.json", + "id": "0527c6fc-316a-4f80-9459-d92ced0e6492", + "label": "Doki Theme: BunnySenpai: Mai", + "path": "./generatedThemes/Mai Dark.theme.json", "uiTheme": "vs-dark" }, { @@ -865,9 +864,9 @@ "uiTheme": "vs" }, { - "id": "a7e0aa28-739a-4671-80ae-3980997e6b71", - "label": "Doki Theme: DDLC: Natsuki", - "path": "./generatedThemes/Natsuki Dark.theme.json", + "id": "dce48196-ff46-470c-b5f9-d1e23f4a79d3", + "label": "Doki Theme: DDLC: Monika", + "path": "./generatedThemes/Monika Dark.theme.json", "uiTheme": "vs-dark" }, { @@ -876,6 +875,12 @@ "path": "./generatedThemes/Natsuki Light.theme.json", "uiTheme": "vs" }, + { + "id": "a7e0aa28-739a-4671-80ae-3980997e6b71", + "label": "Doki Theme: DDLC: Natsuki", + "path": "./generatedThemes/Natsuki Dark.theme.json", + "uiTheme": "vs-dark" + }, { "id": "cb8ef4b7-0844-4a04-b08b-754086598de4", "label": "Doki Theme: DDLC: Sayori", @@ -954,18 +959,18 @@ "path": "./generatedThemes/Rei.theme.json", "uiTheme": "vs-dark" }, - { - "id": "8c99ec4b-fda0-4ab7-95ad-a6bf80c3924b", - "label": "Doki Theme: Franxx: Zero Two", - "path": "./generatedThemes/Zero Two Dark.theme.json", - "uiTheme": "vs-dark" - }, { "id": "4fd5cb34-d36e-4a3c-8639-052b19b26ba1", "label": "Doki Theme: Franxx: Zero Two", "path": "./generatedThemes/Zero Two Light.theme.json", "uiTheme": "vs" }, + { + "id": "8c99ec4b-fda0-4ab7-95ad-a6bf80c3924b", + "label": "Doki Theme: Franxx: Zero Two", + "path": "./generatedThemes/Zero Two Dark.theme.json", + "uiTheme": "vs-dark" + }, { "id": "5ec63d95-1e7d-4649-b3d0-0078af8f8740", "label": "Doki Theme: FutureDiary: Yuno", @@ -1002,18 +1007,18 @@ "path": "./generatedThemes/Aqua.theme.json", "uiTheme": "vs-dark" }, - { - "id": "774ec7ad-d6a0-4d9c-b195-2f54d72ab664", - "label": "Doki Theme: KonoSuba: Darkness", - "path": "./generatedThemes/Darkness Dark.theme.json", - "uiTheme": "vs-dark" - }, { "id": "8474d98d-7bb1-462c-82b1-dd7c512142a6", "label": "Doki Theme: KonoSuba: Darkness", "path": "./generatedThemes/Darkness Light.theme.json", "uiTheme": "vs" }, + { + "id": "774ec7ad-d6a0-4d9c-b195-2f54d72ab664", + "label": "Doki Theme: KonoSuba: Darkness", + "path": "./generatedThemes/Darkness Dark.theme.json", + "uiTheme": "vs-dark" + }, { "id": "63fe4617-4cac-47a5-9b93-6794514c35ad", "label": "Doki Theme: KonoSuba: Megumin", @@ -1068,18 +1073,18 @@ "path": "./generatedThemes/Coconut.theme.json", "uiTheme": "vs-dark" }, - { - "id": "13407818-da66-432b-94e3-fd2192e98118", - "label": "Doki Theme: NekoPara: Maple", - "path": "./generatedThemes/Maple Dark.theme.json", - "uiTheme": "vs-dark" - }, { "id": "ea7a8b00-2d86-4e06-81a2-5c14a46264d5", "label": "Doki Theme: NekoPara: Maple", "path": "./generatedThemes/Maple Light.theme.json", "uiTheme": "vs" }, + { + "id": "13407818-da66-432b-94e3-fd2192e98118", + "label": "Doki Theme: NekoPara: Maple", + "path": "./generatedThemes/Maple Dark.theme.json", + "uiTheme": "vs-dark" + }, { "id": "31d5574d-f56b-408f-81dc-9d44feeb62c2", "label": "Doki Theme: NekoPara: Vanilla", @@ -1116,18 +1121,18 @@ "path": "./generatedThemes/Echidna.theme.json", "uiTheme": "vs-dark" }, - { - "id": "696de7c1-3a8e-4445-83ee-3eb7e9dca47f", - "label": "Doki Theme: Re:Zero: Emilia", - "path": "./generatedThemes/Emilia Dark.theme.json", - "uiTheme": "vs-dark" - }, { "id": "e828aaae-aa8c-4084-8993-d64697146930", "label": "Doki Theme: Re:Zero: Emilia", "path": "./generatedThemes/Emilia Light.theme.json", "uiTheme": "vs" }, + { + "id": "696de7c1-3a8e-4445-83ee-3eb7e9dca47f", + "label": "Doki Theme: Re:Zero: Emilia", + "path": "./generatedThemes/Emilia Dark.theme.json", + "uiTheme": "vs-dark" + }, { "id": "ecb74f1c-8c84-40c4-916f-601039ba2af0", "label": "Doki Theme: Re:Zero: Ram", diff --git a/src/AutoInstaller.ts b/src/AutoInstaller.ts new file mode 100644 index 0000000..3f4c8d9 --- /dev/null +++ b/src/AutoInstaller.ts @@ -0,0 +1,192 @@ +import * as vscode from "vscode"; +import { fixCheckSums } from "./CheckSumService"; +import { DokiSticker, StickerType } from "./DokiTheme"; +import { Sticker } from "./extension"; +import { getHideIndex, getStickerIndex, getWallpaperIndex, hideWaterMark, InstallStatus, installStickers, installWallPaper, readCSS } from "./StickerService"; +import { getCurrentThemeAndSticker, handleInstallFailure, handleInstallMessage, showInstallNotification, showNetworkErrorMessage } from "./ThemeManager"; + +const previousVersionKey = "doki.vscode.version" +const stickerInstallKey = "doki.sticker.restore" +const wallpaperInstallKey = "doki.wallpaper.restore" +const watermarkKey = "doki.watermark.restore" + +enum AutoInstallStatus { + LUL_DUNNO, NOT_INSTALLED, INSTALLED +} + +export const attemptToPerformAutoInstall = ( + context: vscode.ExtensionContext, +) => { + const storedVSCodeVersion: string | undefined = context.globalState.get(previousVersionKey); + if (!storedVSCodeVersion) { + storeFirstConfig(context); + } else if (isVersionDifferent(storedVSCodeVersion)) { + restoreInstallation(context); + } +}; + +function isVersionDifferent( + storedVSCodeVersion: string, +): boolean { + return storedVSCodeVersion !== vscode.version; +} + +function storeFirstConfig(context: vscode.ExtensionContext) { + saveNewVersion(context); + + const vscodeCSS = readCSS(); + const { sticker } = getCurrentThemeAndSticker(); + const isStickerInstalled = getStickerIndex(vscodeCSS) > -1; + if (isStickerInstalled) { + saveStickerConfig(sticker, context); + } else { + clearStickerConfig(context) + } + + const isWallpaperInstalled = getWallpaperIndex(vscodeCSS) > -1; + if (isWallpaperInstalled) { + saveWallpaperConfig(sticker, context); + } else { + clearWallpaperConfig(context) + } + + const isWatermarkHidden = getHideIndex(vscodeCSS) > -1; + if (isWatermarkHidden) { + saveHiddenWatermarkConfig(context); + } else { + clearWatermarkConfig(context) + } +} + +function saveNewVersion(context: vscode.ExtensionContext) { + context.globalState.update(previousVersionKey, vscode.version); +} + +export function restoreInstallation( + context: vscode.ExtensionContext, +) { + saveNewVersion(context); + const stickerInstallStatus = autoInstallAsset(stickerInstallKey, context, installStickers); + const wallpaperInstallStatus = autoInstallAsset(wallpaperInstallKey, context, installWallPaper); + const hideWaterMarkStatus = autoInstallAsset(watermarkKey, context, () => hideWaterMark()); + vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: `Please wait, restoring installed assets.`, + cancellable: false, + }, () => { + return Promise.all([ + stickerInstallStatus, + wallpaperInstallStatus, + hideWaterMarkStatus, + ]).then(installStatuses => { + const { theme: dokiTheme } = getCurrentThemeAndSticker(); + const allWorked = installStatuses.reduce((accum, status) => + accum && (status == InstallStatus.INSTALLED || + status === InstallStatus.NOT_INSTALLED), true); + if (allWorked) { + fixCheckSums(); + const message = `Assets Restored! ${handleInstallMessage}`; + showInstallNotification(message) + } else if ( + !installStatuses.find(status => status === InstallStatus.NETWORK_FAILURE) + ) { + showNetworkErrorMessage(dokiTheme) + } else if ( + !installStatuses.find(status => status === InstallStatus.FAILURE) + ) { + handleInstallFailure(context, dokiTheme); + } + }); + }); +} + +function autoInstallAsset( + assetKey: string, + context: vscode.ExtensionContext, + assetInstaller: ( + sticker: Sticker, + context: vscode.ExtensionContext, + ) => Promise +): Promise { + const wasTheAssetInstalledYo = wasAssetInstalled(assetKey, context) + if (wasTheAssetInstalledYo) { + const { + sticker, + }: RestoreConfig = JSON.parse(context.globalState.get(assetKey) as string); + return assetInstaller(sticker.sticker, context); + } else { + return Promise.resolve(InstallStatus.NOT_INSTALLED); + } +} + +export function saveStickerConfig( + sticker: DokiSticker, + context: vscode.ExtensionContext, +) { + context.globalState.update( + stickerInstallKey, createAssetRestoreConfig(sticker) + ); +} + +export function saveWallpaperConfig( + sticker: DokiSticker, + context: vscode.ExtensionContext, +) { + context.globalState.update( + wallpaperInstallKey, createAssetRestoreConfig(sticker) + ) +} + +export function saveHiddenWatermarkConfig(context: vscode.ExtensionContext) { + const bestSticker: DokiSticker = { + sticker: { + anchoring: 'right', + name: "Zero Two", + path: "Is best girl", + }, + type: StickerType.DEFAULT, + } + context.globalState.update( + watermarkKey, createAssetRestoreConfig(bestSticker), + ) +} + +function clearStickerConfig(context: vscode.ExtensionContext) { + context.globalState.update(stickerInstallKey, AutoInstallStatus.NOT_INSTALLED); +} +function clearWallpaperConfig(context: vscode.ExtensionContext) { + context.globalState.update(wallpaperInstallKey, AutoInstallStatus.NOT_INSTALLED); +} +function clearWatermarkConfig(context: vscode.ExtensionContext) { + context.globalState.update(watermarkKey, AutoInstallStatus.NOT_INSTALLED); +} + +export function clearAssetConfig( + context: vscode.ExtensionContext, +) { + clearStickerConfig(context); + clearWallpaperConfig(context); + clearWatermarkConfig(context); +} + +type RestoreConfig = { + sticker: DokiSticker; +} + +function createAssetRestoreConfig(sticker: DokiSticker): string { + return JSON.stringify( + { + sticker + } as RestoreConfig + ) +} + +function wasAssetInstalled( + assetKey: string, + context: vscode.ExtensionContext, +): boolean { + const assetConfig = context.globalState.get(assetKey) + return assetConfig !== AutoInstallStatus.NOT_INSTALLED && + typeof assetConfig === 'string'; +} + diff --git a/src/NotificationService.ts b/src/NotificationService.ts index fecc9b1..9c32fa6 100644 --- a/src/NotificationService.ts +++ b/src/NotificationService.ts @@ -3,7 +3,7 @@ import { VSCodeGlobals } from "./VSCodeGlobals"; import { attemptToGreetUser } from "./WelcomeService"; const SAVED_VERSION = "doki.theme.version"; -const DOKI_THEME_VERSION = "v15.2.0"; +const DOKI_THEME_VERSION = "v15.3.0"; export function attemptToNotifyUpdates(context: vscode.ExtensionContext) { const savedVersion = VSCodeGlobals.globalState.get(SAVED_VERSION); diff --git a/src/StickerService.ts b/src/StickerService.ts index ffc88f3..93a7a22 100644 --- a/src/StickerService.ts +++ b/src/StickerService.ts @@ -18,11 +18,11 @@ const stickerComment = "/* Stickers */"; const hideComment = "/* Hide Watermark */"; const wallpaperComment = "/* Background Image */"; -const getStickerIndex = (currentCss: string) => +export const getStickerIndex = (currentCss: string) => currentCss.indexOf(stickerComment); -const getHideIndex = (currentCss: string) => +export const getHideIndex = (currentCss: string) => currentCss.indexOf(hideComment); -const getWallpaperIndex = (currentCss: string) => +export const getWallpaperIndex = (currentCss: string) => currentCss.indexOf(wallpaperComment); function buildWallpaperCss({ @@ -99,7 +99,11 @@ function buildWallpaperCss({ [id="workbench.view.explorer"] .monaco-icon-label-container, .explorer-folders-view > .monaco-list > .monaco-scrollable-element > .monaco-list-rows, .show-file-icons > .monaco-list > .monaco-scrollable-element > .monaco-list-rows, - .extensions-list > .monaco-list > .monaco-scrollable-element > .monaco-list-rows + .extensions-list > .monaco-list > .monaco-scrollable-element > .monaco-list-rows, + /* Welcome Page */ + .monaco-workbench .part.editor>.content .gettingStartedContainer .gettingStartedSlideCategories>.gettingStartedCategoriesContainer>.header, + .monaco-workbench .part.editor>.content .gettingStartedContainer .gettingStartedSlideCategories .getting-started-category + /* end welcome page */ { background-color: #00000000 !important; background-image: none !important; @@ -241,7 +245,7 @@ async function installStyles( } function getScrubbedCSS() { - const currentCss = fs.readFileSync(editorCss, "utf-8"); + const currentCss = readCSS(); return indexGetters.reduce( (trimmedCss, indexFinderDude) => trimCss(trimmedCss, indexFinderDude(trimmedCss)), currentCss @@ -250,6 +254,10 @@ function getScrubbedCSS() { type IndexFinderDude = (currentCss: string) => number; +export function readCSS() { + return fs.readFileSync(editorCss, "utf-8"); +} + function scrubCssOfAsset( getOtherAssets: IndexFinderDude[], getAssetToRemoveIndex: IndexFinderDude diff --git a/src/ThemeManager.ts b/src/ThemeManager.ts index 7526622..4035a0a 100644 --- a/src/ThemeManager.ts +++ b/src/ThemeManager.ts @@ -16,6 +16,7 @@ import { import DokiThemeDefinitions from "./DokiThemeDefinitions"; import { DokiThemeDefinition, Sticker } from "./extension"; import { fixCheckSums, restoreChecksum } from "./CheckSumService"; +import { clearAssetConfig, saveHiddenWatermarkConfig, saveStickerConfig, saveWallpaperConfig } from "./AutoInstaller"; export const ACTIVE_THEME = "doki.theme.active"; @@ -153,7 +154,8 @@ export function activateThemeSticker( currentSticker, context, "Sticker", - (sticker) => attemptToInstallSticker(sticker, context) + (sticker) => attemptToInstallSticker(sticker, context), + saveStickerConfig, ); } @@ -167,7 +169,8 @@ export function activateThemeWallpaper( currentSticker, context, "Wallpaper", - (sticker) => attemptToInstallWallpaper(sticker, context) + (sticker) => attemptToInstallWallpaper(sticker, context), + saveWallpaperConfig, ); } @@ -177,8 +180,10 @@ export function activateHideWatermark( return attemptToInstallHideWatermark(context).then( installStatus => { if (installStatus === InstallStatus.INSTALLED) { + fixCheckSums(); const message = `VSCode Watermark hidden! ${handleInstallMessage}`; showInstallNotification(message); + saveHiddenWatermarkConfig(context); } else if (installStatus === InstallStatus.FAILURE) { handleInstallFailure(context, getCurrentThemeAndSticker().theme); } @@ -193,7 +198,11 @@ export function activateThemeAsset( currentSticker: DokiSticker, context: vscode.ExtensionContext, assetType: string, - installer: (sticker: Sticker) => Promise + installer: (sticker: Sticker) => Promise, + configSaver: ( + sticker: DokiSticker, + context: vscode.ExtensionContext, + ) => void, ) { vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, @@ -208,17 +217,22 @@ export function activateThemeAsset( fixCheckSums(); const message = `${dokiTheme.name}'s ${assetType} installed! ${handleInstallMessage}`; showInstallNotification(message); + configSaver(currentSticker, context); } else if (didInstall === InstallStatus.FAILURE) { handleInstallFailure(context, dokiTheme); } else if (didInstall === InstallStatus.NETWORK_FAILURE) { - vscode.window.showErrorMessage( - `Unable to install ${dokiTheme.name}, please check your network connection.` - ); + showNetworkErrorMessage(dokiTheme); } }); }); } +export function showNetworkErrorMessage(dokiTheme: DokiTheme) { + vscode.window.showErrorMessage( + `Unable to install ${dokiTheme.name}, please check your network connection.` + ); +} + export function showInstallNotification(message: string) { vscode.window .showInformationMessage( @@ -246,6 +260,7 @@ export function uninstallImages(context: vscode.ExtensionContext) { stickersRemoved === InstallStatus.INSTALLED || stickersRemoved === InstallStatus.NOT_INSTALLED ) { + clearAssetConfig(context); restoreChecksum(); vscode.window .showInformationMessage( @@ -308,7 +323,4 @@ function isCultured( !context.globalState.get(CULTURED_STICKER_INSTALL) ); } -function hideWatermark(): InstallStatus | PromiseLike { - throw new Error("Function not implemented."); -} diff --git a/src/extension.ts b/src/extension.ts index a9ba2d1..527e2a6 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -16,6 +16,7 @@ import {showChanglog} from "./ChangelogService"; import {attemptToUpdateSticker} from "./StickerUpdateService"; import { watchConfigChanges } from "./ConfigWatcher"; import { cleanupOrigFiles as cleanupCheckSumRestorationFiles } from "./CheckSumService"; +import { attemptToPerformAutoInstall, restoreInstallation } from "./AutoInstaller"; export interface Sticker { path: string; @@ -72,6 +73,11 @@ export function activate(context: vscode.ExtensionContext) { ) ); + context.subscriptions.push( + vscode.commands.registerCommand("doki-theme.restore.assets", () => + restoreInstallation(context) + ) + ); VSCodeGlobals.globalState = context.globalState; @@ -110,6 +116,8 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push(watchConfigChanges(context)); cleanupCheckSumRestorationFiles(); + + attemptToPerformAutoInstall(context); } export function deactivate() {