Show Total Lesson Count - WaniKani

Changes the count of lessons on the Today's Lessons tile to show the total number of available lessons in addition to the number selected for you

Verze ze dne 05. 08. 2024. Zobrazit nejnovější verzi.

// ==UserScript==
// @name         Show Total Lesson Count - WaniKani
// @namespace    http://tampermonkey.net/
// @version      0.4.8
// @description  Changes the count of lessons on the Today's Lessons tile to show the total number of available lessons in addition to the number selected for you
// @license      MIT
// @author       LupoMikti
// @match        https://www.wanikani.com/*
// @grant        none
// @supportURL   https://community.wanikani.com/t/userscript-show-total-lesson-count/66776
// ==/UserScript==

(async function() {
    'use strict';

    /* global wkof */

    let scriptId = 'show_total_lesson_count';
    let scriptName = 'Show Total Lesson Count';
    let wkBatchSize = 0;
    let initial_load = true;
    let todaysLessonsCount;
    let settings;
    let stateStarting = false;
    let todaysLessonsFrameLoaded = false;
    let navBarCountFrameLoaded = false;

    let debugLogText = `START: ${scriptName} Debug Log:\n`;
    let hasOutputLog = false;
    let mainSource = '';
    const INTERNAL_FORCE_DEBUG_OUTPUT = false;

    function addToDebugLog(message) {
        debugLogText += `${new Date().toISOString()}: ${message}\n`;
    }

    function printDebugLog(force = false) {
        console.log(`${scriptName}: Outputting a debug log to console.debug()`)
        if (!hasOutputLog || force) console.debug(debugLogText);
        hasOutputLog = true;
        debugLogText = `START: ${scriptName} Debug Log:\n`;
    }

    if (!window.wkof) {
        if (confirm(scriptName + ' requires Wanikani Open Framework.\nDo you want to be forwarded to the installation instructions?')) {
            window.location.href = 'https://community.wanikani.com/t/instructions-installing-wanikani-open-framework/28549';
        }
        return;
    }

    const wkofTurboEventsScriptUrl = 'https://update.greasyfork.org/scripts/501980/1422360/Wanikani%20Open%20Framework%20Turbo%20Events.user.js';
    addToDebugLog(`Attempting to load the TurboEvents library script...`)
    await wkof.load_script(wkofTurboEventsScriptUrl, /* use_cache */ true);
    addToDebugLog(`Checking if TurboEvents library script is loaded in...`)
    let injectedDependency = document.head.querySelector('script[uid*="Turbo"]');
    addToDebugLog(`Turbo Events library ${injectedDependency ? 'is' : 'is NOT' } loaded.`);

    if (INTERNAL_FORCE_DEBUG_OUTPUT) {
        window.addEventListener('turbo:load', () => { console.log(`DEBUG: turbo:load has fired`); });
        window.addEventListener('turbo:before-frame-render', () => { console.log(`DEBUG: turbo:before-frame-render has fired`); });
        window.addEventListener('turbo:frame-load', () => { console.log(`DEBUG: turbo:frame-load has fired`); });
    }

    wkof.ready('TurboEvents').then(() => {
        addToDebugLog(`Start of TurboEvents ready callback`)
        let urlList = [wkof.turbo.common.locations.dashboard, wkof.turbo.common.locations.items_pages, /^https:\/\/www\.wanikani\.com\/(settings|level|radicals|kanji|vocabulary)(\/|\?difficulty=).+\/?$/];

        wkof.turbo.on.common.urls(() => {
            addToDebugLog(`turbo:load has fired, setting globals and calling _start()`)
            initial_load = stateStarting = true;
            hasOutputLog = todaysLessonsFrameLoaded = navBarCountFrameLoaded = false;
            _start();
        }, urlList);

        wkof.turbo.on.event.before_frame_render((e) => {
            addToDebugLog(`turbo:before-frame-render has fired for "#${e.target.id}"`)
            if (['todays-lessons-frame', 'lesson-and-review-count-frame'].includes(e.target.id)) {
                todaysLessonsFrameLoaded = navBarCountFrameLoaded = false;
            }
        }, { urls: urlList, noTimeout: true });

        wkof.turbo.on.event.frame_load(async (e) => {
            addToDebugLog('turbo:frame-load has fired')
            if (e.target.id === 'todays-lessons-frame') {
                addToDebugLog('turbo:frame-load is for "#todays-lessons-frame"')
                todaysLessonsFrameLoaded = true;
                mainSource = `turbo:frame-load for "#todays-lessons-frame"`;
                await main();
            }
            else if (e.target.id === 'lesson-and-review-count-frame') {
                addToDebugLog('turbo:frame-load is for "#lesson-and-review-count-frame"')
                navBarCountFrameLoaded = true;
                mainSource = `turbo:frame-load for "#lesson-and-review-count-frame"`;
                await main();
            }
            else {
                addToDebugLog(`turbo:frame-load was for "#${e.target.id}", doing nothing...`)
            }
            mainSource = '';
        }, { urls: urlList });

        addToDebugLog(`All turbo callbacks have been sent to TurboEvents library to be registered`)

        if (INTERNAL_FORCE_DEBUG_OUTPUT) printDebugLog(INTERNAL_FORCE_DEBUG_OUTPUT);
    });

    if (INTERNAL_FORCE_DEBUG_OUTPUT) printDebugLog(INTERNAL_FORCE_DEBUG_OUTPUT);

    function _start() {
        addToDebugLog(`Starting...`)
        wkof.include('Settings, Menu, Apiv2');
        wkof.ready('Settings, Menu, Apiv2').then(loadSettings).then(insertMenu).then(main);
    }

    function loadSettings() {
        addToDebugLog(`Loading settings...`)
        wkBatchSize = wkof.user.preferences.lessons_batch_size;

        let defaults = {
            showTotalOnly: false,
            setOwnPreferredDaily: false,
            preferredDailyAmount: wkBatchSize * 3,
            enableDebugging: true,
        };

        return wkof.Settings.load(scriptId, defaults).then(function(wkof_settings) {settings = wkof_settings;});
    }

    function insertMenu() {
        addToDebugLog(`Inserting menu...`)
        let config = {
            name: scriptId,
            submenu: 'Settings',
            title: scriptName,
            on_click: openSettings
        };

        wkof.Menu.insert_script_link(config);
        mainSource = `_start() -> loadSettings() -> insertMenu()`;
    }

    function openSettings() {
        let config = {
            script_id: scriptId,
            title: scriptName,
            on_save: main,
            content: {
                showTotalOnly: {
                    type: 'checkbox',
                    label: 'Show Only Total Lesson Count',
                    hover_tip: `Changes display between "<today's lesson count> / <total lesson count>" and just "<total lesson count>"`,
                    default: false,
                },
                setOwnPreferredDaily: {
                    type: 'checkbox',
                    label: 'Set Your Own Daily Lesson Count',
                    hover_tip: `Choose whether to display the value you set as your daily lesson count or not`,
                    default: false,
                },
                preferredDailyAmount: {
                    type: 'number',
                    label: 'Preferred Daily Lesson Amount',
                    hover_tip: `The number you want displayed for "Today's Lessons". If you use the Wanikani setting, set this to match. Maximum of 100. NOTE: this does not actually change the number of available lessons.`,
                    default: wkBatchSize * 3,
                    min: 0,
                    max: 100,
                },
                enableDebugging: {
                    type: 'checkbox',
                    label: 'Enable console debugging',
                    hover_tip: `Enable output of debugging info to console.debug()`,
                    default: true,
                }
            }
        };

        let dialog = new wkof.Settings(config);
        dialog.open();
    }

    async function getCountContainers() {
        let dashboardTileCountContainer = document.querySelector('.todays-lessons__count-text .count-bubble');
        let navBarCountContainer = document.querySelector('.lesson-and-review-count__count');

        if (initial_load && (dashboardTileCountContainer || navBarCountContainer)) {
            let container = dashboardTileCountContainer ?? navBarCountContainer;
            todaysLessonsCount = parseInt(container.textContent);
            initial_load = false;
        }

        return [dashboardTileCountContainer, navBarCountContainer];
    }

    async function main() {
        addToDebugLog(`Main function is executing... source of start: ${mainSource}`)
        if (!settings) {
            addToDebugLog('We do not have settings, setting timeout on _start()');
            if (!stateStarting) { stateStarting = true; setTimeout(_start, 50); }
            else addToDebugLog(`Did not set timeout due to already being in starting state`);
            return;
        }
        addToDebugLog(`We have settings`)
        stateStarting = false;
        let summary_data = await wkof.Apiv2.get_endpoint('summary');
        let totalLessonCount = summary_data.lessons[0].subject_ids.length;
        let lessonCountContainers;
        if (todaysLessonsFrameLoaded || navBarCountFrameLoaded) {
            addToDebugLog(`Frame(s) loaded`)
            lessonCountContainers = await getCountContainers();
        }
        else {
            addToDebugLog('No frames loaded')
            return;
        }
        let todaysCountForDisplay = todaysLessonsCount;

        if (lessonCountContainers.every(node => node == null)) {
            addToDebugLog('No nodes in containers')
            return;
        }
        addToDebugLog('At least one container exists')

        if (isNaN(todaysLessonsCount)) {
            todaysCountForDisplay = 0;
        }
        else {
            if (settings.setOwnPreferredDaily) todaysCountForDisplay = todaysLessonsCount - (wkBatchSize * 3 - settings.preferredDailyAmount);
        }
        addToDebugLog(`Setting display amount for Today's Lessons count, set to: ${todaysCountForDisplay}`)

        if (lessonCountContainers[0]) lessonCountContainers[0].textContent = settings.showTotalOnly ? totalLessonCount : todaysCountForDisplay + ' / ' + totalLessonCount;
        if (lessonCountContainers[1]) lessonCountContainers[1].textContent = settings.showTotalOnly ? totalLessonCount : todaysCountForDisplay;

        if (todaysCountForDisplay === 0) {
            addToDebugLog(`Hiding start button due to having 0 lessons with configured count source`)
            // hide the start button if it is not already, TODO: disable nav bar button if it is not already
            let startButton = document.querySelector('.todays-lessons-button--start')
            if (startButton && startButton.checkVisibility()) {
                startButton.style.display = 'none';
            }
        }

        addToDebugLog(`Hiding the "Today's" subtitle on the lesson tile`)
        // hide "Today's" subtitle
        let lessonSubtitle = document.querySelector('.todays-lessons__subtitle');

        if (lessonSubtitle && lessonSubtitle.checkVisibility()) {
            lessonSubtitle.style.display = 'none';
        }

        addToDebugLog(`Main function has finished executing`)

        if (settings.enableDebugging) printDebugLog();
    }
})();