Skip to content

Latest commit

 

History

History
114 lines (102 loc) · 4.87 KB

File metadata and controls

114 lines (102 loc) · 4.87 KB

kit:: daysUntilNextAppointment description:: replacement for nextAppointment. Can be called from logseq.kits.daysUntilNextAppointment created-on:: [[Saturday, Jul 27th, 2024]]

  • /**
     * @file daysUntilNextAppointment.md
     * @author DeadBranch
     * @see {@link https://github.com/DeadBranches/logseq-queries-and-scripts/daysUntilNextAppointment.md|GitHub Repository}
     * @see {@link https://discuss.logseq.com/t/edit-and-run-javascript-code-inside-logseq-itself/20763|Logseq Kits Installation}
     * @description This Logseq Kits script calculates the number of days until 
     * the next appointment or event in your Logseq graph. It's useful for quickly 
     * seeing when your next scheduled activity is coming up, helping you stay on top 
     * of your calendar. The script queries your Logseq database for future events, 
     * sorts them, and returns the number of days until the nearest one.
     * 
     * Technically, it uses Logseq's advanced query system to find blocks with a :date 
     * property, starting from today's date. It then sorts these events and calculates 
     * the number of days until the earliest one.
     * 
     * @function daysUntilNextAppointment
     * @returns {Promise<number>} A promise that resolves to the number of days until 
     * the next appointment. Returns -1 if no future activities are found.
     * 
     * @example
     * // Example usage with kitButton:
     * {{kitButton upcoming appointment in |daysUntilNextAppointment| day,collapseBlock,ea53,-button-style full-width}}
     * 
     * @howto
     * 1. Create a new page in your Logseq graph named "daysUntilNextAppointment"
     * 2. Add this script to that page inside a JavaScript markdown code block
     * 3. Use the kitButton macro or call `logseq.kits.daysUntilNextAppointment()` to
     *  invoke the function
     * 
     * @requires Logseq Kits
     */
    
    logseq.kits.setStatic(function daysUntilNextAppointment() {
    
        const todaysDate = new Date();
        const appointmentsArrayPromise = (async (startDate = todaysDate) => {
    
            // Start counting from startDate date into the future. You probably want 
            // to start from today.
            // Logseq's :journal-day uses the format date YYYYMMDD.
            const startFromDateLogseq = ((date = startDate) => {
                const month = (date.getMonth() + 1).toString()
                    .padStart(2, '0'),
                    day = date.getDate().toString()
                        .padStart(2, '0'),
                    year = date.getFullYear().toString();
                return [year, month, day].join('');
            })();
    
            const futureActivities = await (async (startDate = startFromDateLogseq) => {
                if (typeof startDate != 'string') {
                    console.log(16, `function futureActivities: Expected startDate
                        to be a string, but it was not.`);
                };
                const advancedQuery = `
                    [:find (min ?day) ?date ?day ?content ?props
                    :keys min-day date day content properties
                    :where
                    [?e :block/properties ?props]
                    [(get ?props :event) ?event]
                    [(get ?props :date) ?date] 
                    [?e :block/refs ?refs]
                    [?e :block/content ?content]
                    [?refs :block/journal-day ?day]
                    [(>= ?day ${startDate})]
                    ]`;
    
                const queryResults = await logseq.api.datascript_query(advancedQuery);
                return queryResults?.flat();
            })();
    
            const sortedActivities = ((activities = futureActivities) => {
                if (typeof activities.sort != 'function') {
                    console.log(37, `function sortedActivities: Expected futureActivities
                        to be an object, but it was not.`);
                    return [];
                }
                return [...activities].sort((a, b) => {
                    return a.day - b.day;
                });
            })();
    
            return daysUntilNextActivity = ((
                activities = sortedActivities,
                earlierDate = startDate) => {
                if (activities.length == 0) {
                    return -1; // No future activities.
                }
    
                const nextActivityDay = activities[0].day.toString();
                const year = parseInt(nextActivityDay.slice(0, 4), 10);
                const month = parseInt(nextActivityDay.slice(4, 6), 10) - 1; // Adjust for zero-indexed months
                const day = parseInt(nextActivityDay.slice(6, 8), 10);
                const nextActivityDate = new Date(year, month, day);
    
                return Math.ceil((nextActivityDate - earlierDate) / (1000 * 60 * 60 * 24));
            })();
    
            //return daysUntilNextActivity;
        })();
    
        return new Promise(async (resolve) => {
            resolve(await appointmentsArrayPromise);
        })
        //return JSON.stringify();
    });
    • {{evalparent}}