From 3bb428557ea1800c26d49e8e86637c25dbfa3a10 Mon Sep 17 00:00:00 2001 From: YukiGasai Date: Mon, 8 Aug 2022 17:17:55 +0200 Subject: [PATCH] Added caching + Templater support + Color Fix --- manifest.json | 2 +- package-lock.json | 2 +- package.json | 2 +- src/GoogleCalendarPlugin.ts | 23 ++++-- src/googleApi/GoogleColors.ts | 83 ++++++++----------- src/googleApi/GoogleListEvents.ts | 44 ++++++++-- src/googleApi/GoogleUpdateEvent.ts | 3 - src/helper/AutoEventNoteCreator.ts | 127 ++++++++++++++++++++++------- src/helper/types.ts | 9 ++ src/svelte/CalendarComp.svelte | 7 +- src/svelte/EventDetailsComp.svelte | 64 ++++++++++----- src/svelte/TimeLineComp.svelte | 5 +- src/svelte/TimeLineViewComp.svelte | 19 +++-- 13 files changed, 262 insertions(+), 128 deletions(-) diff --git a/manifest.json b/manifest.json index 1bfb889..ae509fd 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-google-calendar", "name": "Obsidian Google Calendar", - "version": "1.1.1", + "version": "1.1.2", "minAppVersion": "0.12.0", "description": "Interact with your Google Calendar from Inside Obsidian", "author": "YukiGasai", diff --git a/package-lock.json b/package-lock.json index d3bdad5..e032325 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "obsidian-google-calendar", - "version": "1.1.0", + "version": "1.1.2", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 381b81f..2c32251 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-sample-plugin", - "version": "1.1.1", + "version": "1.1.2", "description": "Interact with your Google Calendar from Inside Obsidian", "main": "main.js", "scripts": { diff --git a/src/GoogleCalendarPlugin.ts b/src/GoogleCalendarPlugin.ts index 8f156cf..03196a0 100644 --- a/src/GoogleCalendarPlugin.ts +++ b/src/GoogleCalendarPlugin.ts @@ -7,6 +7,7 @@ import { import { googleListCalendars } from "./googleApi/GoogleListCalendars"; import { CalendarsListModal } from "./modal/CalendarsListModal"; import { googleListTodayEvents } from "./googleApi/GoogleListEvents"; +import { getGoogleColors } from "./googleApi/GoogleColors"; import { checkEditorForCodeBlocks } from "./helper/CheckEditorForCodeBlocks"; import { DayCalendarView, VIEW_TYPE_GOOGLE_CALENDAR_DAY } from "./view/DayCalendarView"; import { MonthCalendarView, VIEW_TYPE_GOOGLE_CALENDAR_MONTH } from "./view/MonthCalendarView"; @@ -32,9 +33,11 @@ const DEFAULT_SETTINGS: GoogleCalendarPluginSettings = { export default class GoogleCalendarPlugin extends Plugin { settings: GoogleCalendarPluginSettings; - + overwriteCache = false; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + coreTemplatePlugin:any; // eslint-disable-next-line @typescript-eslint/no-explicit-any - templatePlugin:any; + templaterPlugin:any; initView = async (viewId:string): Promise => { if ( @@ -55,9 +58,15 @@ export default class GoogleCalendarPlugin extends Plugin { onLayoutReady = ():void => { //Get the template plugin to run their commands // eslint-disable-next-line @typescript-eslint/no-explicit-any - const templatePlugin = (this.app as any).internalPlugins?.plugins["templates"]; - if(templatePlugin && templatePlugin.enabled){ - this.templatePlugin = templatePlugin; + const coreTemplatePlugin = (this.app as any).internalPlugins?.plugins["templates"]; + if(coreTemplatePlugin && coreTemplatePlugin.enabled){ + this.coreTemplatePlugin = coreTemplatePlugin; + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const templaterPlugin = (this.app as any).plugins.plugins["templater-obsidian"]; + if(templaterPlugin && templaterPlugin._loaded){ + this.templaterPlugin = templaterPlugin; } checkForEventNotes(this); @@ -65,7 +74,7 @@ export default class GoogleCalendarPlugin extends Plugin { async onload(): Promise { await this.loadSettings(); - + await getGoogleColors(this); this.app.workspace.onLayoutReady(this.onLayoutReady); @@ -168,7 +177,7 @@ export default class GoogleCalendarPlugin extends Plugin { name: "Insert Google Event CodeBlock", editorCallback: (editor: Editor) => { editor.replaceRange( - "```gEvent\ndate:today\ntype:self\n```", + "```gEvent\ndate:"+window.moment().format("YYYY-MM-DD")+"\ntype:day\n```", editor.getCursor() ); }, diff --git a/src/googleApi/GoogleColors.ts b/src/googleApi/GoogleColors.ts index b11fd3a..fd2eff6 100644 --- a/src/googleApi/GoogleColors.ts +++ b/src/googleApi/GoogleColors.ts @@ -8,52 +8,37 @@ */ import type { GoogleEvent } from "../helper/types"; +import type GoogleCalendarPlugin from './../GoogleCalendarPlugin'; -export function googleCalendarColors(): string[] { - return [ - "#ac725e", - "#d06b64", - "#f83a22", - "#fa573c", - "#ff7537", - "#ffad46", - "#42d692", - "#16a765", - "#7bd148", - "#b3dc6c", - "#fbe983", - "#fad165", - "#92e1c0", - "#9fe1e7", - "#9fc6e7", - "#4986e7", - "#9a9cff", - "#b99aff", - "#c2c2c2", - "#cabdbf", - "#cca6ac", - "#f691b2", - "#cd74e6", - "#a47ae2", - ]; -} +const calendarColors = new Map(); +const eventColors = new Map(); -export function googleEventColors(): string[] { - return [ - "#a4bdfc", - "#7ae7bf", - "#dbadff", - "#ff887c", - "#fbd75b", - "#ffb878", - "#46d6db", - "#e1e1e1", - "#5484ed", - "#51b749", - "#dc2127", - ]; -} +export async function getGoogleColors(plugin:GoogleCalendarPlugin):Promise { + const requestHeaders: HeadersInit = new Headers(); + requestHeaders.append("Content-Type", "application/json"); + + const response = await fetch(`https://www.googleapis.com/calendar/v3/colors?key=${plugin.settings.googleApiToken}`, { + method: "GET", + headers: requestHeaders + }); + + const colorData = await response.json(); + + for (let i = 1; ; i++) { + const color = colorData.calendar[i+""]?.background; + if(!color)break; + + calendarColors.set(i+"", color) + } + + for (let i = 1; ; i++) { + const color = colorData.event[i+""]?.background; + if(!color)break; + + eventColors.set(i+"", color) + } +} /** * This function just returns the true color of an event @@ -61,12 +46,14 @@ export function googleEventColors(): string[] { * @returns a hex color string */ export function getColorFromEvent(event: GoogleEvent): string { - if(event.colorId) { - return googleEventColors()[event.colorId] - }else if( event.parent.colorId ){ - return googleCalendarColors()[event.parent.colorId] + + if(event.colorId && eventColors.has(event.colorId)) { + return eventColors.get(event.colorId); + + }else if( event.parent.colorId && calendarColors.has(event.parent.colorId)){ + return calendarColors.get(event.parent.colorId); + } else { return "#a4bdfc" } - } \ No newline at end of file diff --git a/src/googleApi/GoogleListEvents.ts b/src/googleApi/GoogleListEvents.ts index 1da1168..52239b8 100644 --- a/src/googleApi/GoogleListEvents.ts +++ b/src/googleApi/GoogleListEvents.ts @@ -1,4 +1,5 @@ import type { + EventCacheValue, GoogleCalander, GoogleEvent, GoogleEventList, @@ -12,6 +13,9 @@ import { googleListCalendars } from "./GoogleListCalendars"; import ct from 'countries-and-timezones' +const cachedEvents = new Map(); + + const dateToTimeParam = (date:string, tz:string) :string => { return encodeURIComponent(`${date}T00:00:00${tz}`); } @@ -32,6 +36,30 @@ export async function googleListEventsByCalendar( endDate?: moment.Moment ): Promise { + //Turn dates into strings for request and caching + const timezone = ct.getTimezone(googleCalander.timeZone); + + const startString = dateToTimeParam(date.format('YYYY-MM-DD'),timezone.dstOffsetStr); + let endString = ""; + if(endDate){ + + endString = dateToTimeParam(endDate.format('YYYY-MM-DD'),timezone.dstOffsetStr); + + }else{ + endDate = date.clone().add(1, "day") + endString = dateToTimeParam(endDate.format('YYYY-MM-DD'),timezone.dstOffsetStr) + } + + const cacheKey:string = JSON.stringify({start: startString, end: endString, calendar: googleCalander.id}); + + + if(cachedEvents.has(cacheKey) && !plugin.overwriteCache){ + const {events, updated} = cachedEvents.get(cacheKey); + if(updated.clone().add(plugin.settings.refreshInterval, "second").isAfter(moment())){ + return events; + } + } + let totalEventList: GoogleEvent[] = []; let tmpRequestResult: GoogleEventList; const resultSizes = 2500; @@ -43,7 +71,7 @@ export async function googleListEventsByCalendar( ); requestHeaders.append("Content-Type", "application/json"); - const timezone = ct.getTimezone(googleCalander.timeZone); + try { do { @@ -54,12 +82,8 @@ export async function googleListEventsByCalendar( requestUrl += `&maxResults=${resultSizes}`; requestUrl += `&singleEvents=True`; requestUrl += `&orderBy=startTime`; - requestUrl += `&timeMin=${dateToTimeParam(date.format('YYYY-MM-DD'), timezone.dstOffsetStr)}` - - // TODO This could lead to problems displaying events at the wrong dates - - const tomorrow = (endDate ?? date).clone().add(1, "day").format('YYYY-MM-DD'); - requestUrl += `&timeMax=${dateToTimeParam(tomorrow, timezone.dstOffsetStr)}`; + requestUrl += `&timeMin=${startString}` + requestUrl += `&timeMax=${endString}`; if (tmpRequestResult && tmpRequestResult.nextPageToken) { @@ -91,6 +115,8 @@ export async function googleListEventsByCalendar( return startA.isBefore(startB, "minute") ? -1 : 1; }) + cachedEvents.set(cacheKey, {events: totalEventList, updated:moment()}) + return totalEventList; } catch (error) { console.log(error); @@ -127,6 +153,10 @@ export async function googleListEvents( return startA.isBefore(startB, "minute") ? -1 : 1; }) + if(plugin.overwriteCache){ + plugin.overwriteCache = false; + } + return eventList; } catch (error) { console.log(error); diff --git a/src/googleApi/GoogleUpdateEvent.ts b/src/googleApi/GoogleUpdateEvent.ts index 523c41f..3dbacef 100644 --- a/src/googleApi/GoogleUpdateEvent.ts +++ b/src/googleApi/GoogleUpdateEvent.ts @@ -33,9 +33,6 @@ export async function googleUpdateEvent( id = event.id; } - - console.log(id); - //clean the event object to send it to the api directly const calenderId = event.parent.id; delete event.parent; diff --git a/src/helper/AutoEventNoteCreator.ts b/src/helper/AutoEventNoteCreator.ts index e9194de..37376a5 100644 --- a/src/helper/AutoEventNoteCreator.ts +++ b/src/helper/AutoEventNoteCreator.ts @@ -1,10 +1,8 @@ import type GoogleCalendarPlugin from "../GoogleCalendarPlugin"; import { googleListEvents } from "../googleApi/GoogleListEvents"; -import { normalizePath } from "obsidian"; +import { normalizePath, TFile } from "obsidian"; import { createNotice } from "./NoticeHelper"; -import path from "path"; - /** * This function implements the automatic creation of notes from a google calendar event @@ -40,12 +38,12 @@ export const checkForEventNotes = async (plugin: GoogleCalendarPlugin) :Promise< // check every event from the trigger text :obsidian: events.forEach(event => { //regex will check for text and extract a template name if it exists - const match = event.description?.match(/:obsidian-?(.*)?:/) ?? []; + const match = event.description?.match(/:(.*-)?obsidian-?(.*)?:/) ?? []; - if(match.length == 2){ + if(match.length == 3){ //the trigger text was found and a new note will be created const filename = event.summary; - createNoteFromEvent(plugin, filename, match[1]); + createNoteFromEvent(plugin, filename, match[1], match[2]); } }) } @@ -58,16 +56,29 @@ export const checkForEventNotes = async (plugin: GoogleCalendarPlugin) :Promise< * @param fileName The name of the new Note * @param templateName The used Template to fill the file */ -const createNoteFromEvent = async (plugin:GoogleCalendarPlugin, fileName: string, templateName?:string): Promise => { +const createNoteFromEvent = async (plugin:GoogleCalendarPlugin, fileName: string, folderName?:string, templateName?:string): Promise => { const { vault } = plugin.app; const { adapter } = vault; - const newFileFolderPath = app.fileManager.getNewFileParent("").path; - const filePath = path.join(newFileFolderPath, `${fileName}.md`); + + let folderPath = app.fileManager.getNewFileParent("").path; + + if(folderName){ + if(folderName.endsWith("-")){ + folderName = folderName.slice(0, -1); + } + + folderName = "/" + folderName.split(/[/\\]/).join("/"); + if(await adapter.exists(folderName)){ + folderPath = folderName; + } + } + + const filePath = normalizePath(`${folderPath}/${fileName}.md`); //check if file already exists - if(await adapter.exists(normalizePath(filePath))){ + if(await adapter.exists(filePath)){ return; } @@ -76,12 +87,9 @@ const createNoteFromEvent = async (plugin:GoogleCalendarPlugin, fileName: string createNotice(plugin, `EventNote ${fileName} created.`) //check if the template plugin is active - if(!plugin.templatePlugin || !templateName){ + if((!plugin.coreTemplatePlugin && !plugin.templaterPlugin) || !templateName){ return; } - - //Get the folder where the templates are stored from the template plugin - const coreTemplateFolderPath = normalizePath(plugin.templatePlugin.instance.options.folder); //Check if the template name has a file extension if(!templateName.match(/.*\.md/)){ @@ -89,20 +97,83 @@ const createNoteFromEvent = async (plugin:GoogleCalendarPlugin, fileName: string } - //Get Path to template file and check if it exists - const templateFilePath = path.join(coreTemplateFolderPath, templateName); - if(!await adapter.exists(templateFilePath)){ - createNotice(plugin, `Template ${templateName} doesn't exit.`) - return; + //Open file in active panel needed to insert template + await plugin.app.workspace.activeLeaf.openFile(File); + + if(plugin.templaterPlugin && plugin.coreTemplatePlugin){ + if(!(await insertTemplaterTemplate())){ + if(!(await insertCoreTemplate())){ + createNotice(plugin, "Template not compatable") + } + } + }else if(plugin.templaterPlugin){ + if(!(await insertTemplaterTemplate())){ + createNotice(plugin, "Template not compatable") + } + }else if(plugin.coreTemplatePlugin){ + if(!(await insertCoreTemplate())){ + createNotice(plugin, "Template not compatable") + } } - //Get the file from the path - const templateFile = vault.getAbstractFileByPath(normalizePath(templateFilePath)); + async function insertCoreTemplate() : Promise{ + //Get the folder where the templates are stored from the template plugin + const coreTemplateFolderPath = normalizePath(plugin.coreTemplatePlugin.instance.options.folder); + + //Get Path to template file and check if it exists + const templateFilePath = `${coreTemplateFolderPath}/${templateName}`; + if(!await adapter.exists(templateFilePath)){ + createNotice(plugin, `Template ${templateName} doesn't exit.`) + return false; + } + + //Get the file from the path + const templateFile = vault.getAbstractFileByPath((templateFilePath)); + + const result = await adapter.read(normalizePath(templateFilePath)); + + if(result.contains("<%") && result.contains("%>")){ + return false; + } + + //Insert the template by calling the command from the plugin + try{ + await plugin.coreTemplatePlugin.instance.insertTemplate(templateFile); + }catch{ + return false + } + //Close the file to allow multiples inserts + await plugin.app.workspace.activeLeaf.detach(); + return true; + } + + async function insertTemplaterTemplate(){ + const templaterFolderPath = normalizePath(plugin.templaterPlugin.settings.templates_folder); + + //Get Path to template file and check if it exists + const templateFilePath = `${templaterFolderPath}/${templateName}`; + if(!await adapter.exists(templateFilePath)){ + createNotice(plugin, `Template ${templateName} doesn't exit.`) + return false; + } + + const templateFile = plugin.app.vault.getAbstractFileByPath(templateFilePath); + + const result = await adapter.read(normalizePath(templateFilePath)); + + if(result.contains("{{") && result.contains("}}")){ + return false; + } + + if (templateFile instanceof TFile) { + try{ + await plugin.templaterPlugin.templater.append_template_to_active_file(templateFile); + }catch{ + return false; + } + } + return true; + } +} + - //Open file in active panel needed to insert template - await plugin.app.workspace.activeLeaf.openFile(File); - //Insert the template by calling the command from the plugin - await plugin.templatePlugin.instance.insertTemplate(templateFile); - //Close the file to allow multiples inserts - await plugin.app.workspace.activeLeaf.detach(); -} \ No newline at end of file diff --git a/src/helper/types.ts b/src/helper/types.ts index f307b7b..061b175 100644 --- a/src/helper/types.ts +++ b/src/helper/types.ts @@ -253,3 +253,12 @@ export interface TimeLineOptions { height: number; } +export interface EventCacheKey { + start: string; + end: string; + calendar: string; +} +export interface EventCacheValue { + events: GoogleEvent[]; + updated: moment.Moment; +} \ No newline at end of file diff --git a/src/svelte/CalendarComp.svelte b/src/svelte/CalendarComp.svelte index 610a2ba..9aaa9c1 100644 --- a/src/svelte/CalendarComp.svelte +++ b/src/svelte/CalendarComp.svelte @@ -51,7 +51,10 @@ const onClickDay = (date: moment.Moment, isMenu:boolean) => { - new EventListModal(plugin, getEventsOfDay(events, date), false, () => displayedMonth = displayedMonth).open(); + new EventListModal(plugin, getEventsOfDay(events, date), false, () => { + plugin.overwriteCache = true; + displayedMonth = displayedMonth + }).open(); } @@ -59,7 +62,7 @@ if(interval){ clearInterval(interval); } - interval = setInterval(() => getSource(displayedMonth), plugin.settings.refreshInterval * 1000) + interval = setInterval(() => getSource(displayedMonth), 5000) getSource(displayedMonth) } diff --git a/src/svelte/EventDetailsComp.svelte b/src/svelte/EventDetailsComp.svelte index 111509d..a06945f 100644 --- a/src/svelte/EventDetailsComp.svelte +++ b/src/svelte/EventDetailsComp.svelte @@ -181,22 +181,13 @@
- - {#if event.recurringEventId } -

Google Calendar Recurring Event

- {:else} -

Google Calendar Event

- {/if} - - - + - {#if loading} Loading {:else} @@ -208,6 +199,7 @@ {/each} + {/if}
@@ -229,18 +221,30 @@
{#if event.id} - - - - + {#if event.recurringEventId } - - - + Recurring Event + {:else} + Single Event {/if} - + +
+ + + +
+ {#if event.recurringEventId } +
+ + + +
+ {/if} + {:else} - +
+ +
{/if}
@@ -248,6 +252,24 @@