From b93df30cb1af45c51043ced3eb0b53a6a3752620 Mon Sep 17 00:00:00 2001 From: Francis Kafieh Date: Sun, 27 Mar 2022 18:37:00 -0400 Subject: [PATCH] release 0.0.1 --- README.md | 6 +- main.ts | 205 ++++++++++++++++++++++++++------------------------ manifest.json | 12 +-- styles.css | 4 - 4 files changed, 116 insertions(+), 111 deletions(-) delete mode 100644 styles.css diff --git a/README.md b/README.md index a7e3bad..ec29c5e 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ This plugin lists all modified files meeting certain criteria to a daily note. As of now, dataview queries cannot be "frozen" in time. There are hacky solutions using templater, but I wanted something really simple. # How? -When a file is modified, the plugin checks if there is already a link to the modified file inside of your daily note. If not, it appends a link to the daily note. +When a file is modified, the plugin checks if there is already a link to the modified file inside of your daily note. If so, it doesn't need to link it again. Otherwise, If this modified file meets the criteria, it appends a link to the daily note. # Criteria supported -- tags +- blacklist tags # Usage -TODO +The only things you need to configure are in the settings tab! After this is configured, the plugin should be automatically working. Read the descriptions of each setting carefully! diff --git a/main.ts b/main.ts index 9b24b2f..70e0b41 100644 --- a/main.ts +++ b/main.ts @@ -1,137 +1,146 @@ -import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian'; +import { App, CachedMetadata, Notice, Plugin, PluginSettingTab, Setting, TFile, moment, TagCache, FrontMatterCache } from 'obsidian' -// Remember to rename these classes and interfaces! - -interface MyPluginSettings { - mySetting: string; +interface ListModifiedSettings { + dailyNoteFormat: string + outputFormat: string + tags: string } -const DEFAULT_SETTINGS: MyPluginSettings = { - mySetting: 'default' +const DEFAULT_SETTINGS: ListModifiedSettings = { + dailyNoteFormat: '', + outputFormat: '- [[link]]', + tags: '' } -export default class MyPlugin extends Plugin { - settings: MyPluginSettings; +export default class ListModified extends Plugin { + settings: ListModifiedSettings async onload() { - await this.loadSettings(); - - // This creates an icon in the left ribbon. - const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => { - // Called when the user clicks the icon. - new Notice('This is a notice!'); - }); - // Perform additional things with the ribbon - ribbonIconEl.addClass('my-plugin-ribbon-class'); - - // This adds a status bar item to the bottom of the app. Does not work on mobile apps. - const statusBarItemEl = this.addStatusBarItem(); - statusBarItemEl.setText('Status Bar Text'); - - // This adds a simple command that can be triggered anywhere - this.addCommand({ - id: 'open-sample-modal-simple', - name: 'Open sample modal (simple)', - callback: () => { - new SampleModal(this.app).open(); - } - }); - // This adds an editor command that can perform some operation on the current editor instance - this.addCommand({ - id: 'sample-editor-command', - name: 'Sample editor command', - editorCallback: (editor: Editor, view: MarkdownView) => { - console.log(editor.getSelection()); - editor.replaceSelection('Sample Editor Command'); + await this.loadSettings() + + this.registerEvent(this.app.vault.on('modify', async (file) => { + const currentFile: TFile = file as TFile + const tags: string[] = this.settings.tags.replace(/\s/g, '').split(',') + const dailyNoteFormat: string = this.settings.dailyNoteFormat + const dailyFile: TFile = this.app.vault.getAbstractFileByPath( + moment().format(dailyNoteFormat) + '.md' + ) as TFile + const outputFormat: string = this.settings.outputFormat + const resolvedOutputFormat: string = outputFormat.replace('[[link]]', `[[${currentFile.basename}]]`) + + if (dailyFile === null) { + new Notice(`A daily file with format ${dailyNoteFormat} doesn't exist! Cannot append link`) + return } - }); - // This adds a complex command that can check whether the current state of the app allows execution of the command - this.addCommand({ - id: 'open-sample-modal-complex', - name: 'Open sample modal (complex)', - checkCallback: (checking: boolean) => { - // Conditions to check - const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView); - if (markdownView) { - // If checking is true, we're simply "checking" if the command can be run. - // If checking is false, then we want to actually perform the operation. - if (!checking) { - new SampleModal(this.app).open(); - } - - // This command will only show up in Command Palette when the check function returns true - return true; - } - } - }); - // This adds a settings tab so the user can configure various aspects of the plugin - this.addSettingTab(new SampleSettingTab(this.app, this)); + if (currentFile === dailyFile) return + + if (this.fileIsLinked(dailyFile, currentFile.basename)) return + + if (tags[0] !== '' && !this.fileMeetsTagRequirements(currentFile, tags)) return - // If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin) - // Using this function will automatically remove the event listener when this plugin is disabled. - this.registerDomEvent(document, 'click', (evt: MouseEvent) => { - console.log('click', evt); - }); + await this.app.vault.append(dailyFile, '\n' + resolvedOutputFormat) + })) - // When registering intervals, this function will automatically clear the interval when the plugin is disabled. - this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000)); + this.addSettingTab(new ListModifiedSettingTab(this.app, this)) } - onunload() { + fileIsLinked(fileToCheck: TFile, link: string): boolean { + const cache: CachedMetadata = this.app.metadataCache.getFileCache(fileToCheck) + return cache.links.some(l => l.link === link) + } + fileMeetsTagRequirements(file: TFile, tags: string[]): boolean { + for (const tag of tags) { + if (this.fileHasTag(file, tag)) return false + } + return true } - async loadSettings() { - this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()); + fileHasTag(file: TFile, tag: string): boolean { + const cache: CachedMetadata = this.app.metadataCache.getFileCache(file) + return this.tagMetadataContainsTag(cache, tag) || + this.frontmatterMetadataContainsTag(cache, tag) } - async saveSettings() { - await this.saveData(this.settings); + tagMetadataContainsTag(cache: CachedMetadata, tagToMatch: string): boolean { + const tagCache: TagCache[] = cache.tags + if (tagCache === undefined) return false + return tagCache.some(tag => tag.tag === tagToMatch) } -} -class SampleModal extends Modal { - constructor(app: App) { - super(app); + frontmatterMetadataContainsTag(cache: CachedMetadata, tagToMatch: string): boolean { + const frontmatterCache: FrontMatterCache = cache.frontmatter + if (frontmatterCache === undefined) return false + return frontmatterCache.tags.some((tag: string) => tag === tagToMatch) } - onOpen() { - const {contentEl} = this; - contentEl.setText('Woah!'); + onunload() { + } - onClose() { - const {contentEl} = this; - contentEl.empty(); + async loadSettings() { + this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()) + } + + async saveSettings() { + await this.saveData(this.settings) } } -class SampleSettingTab extends PluginSettingTab { - plugin: MyPlugin; +class ListModifiedSettingTab extends PluginSettingTab { + plugin: ListModified - constructor(app: App, plugin: MyPlugin) { - super(app, plugin); - this.plugin = plugin; + constructor(app: App, plugin: ListModified) { + super(app, plugin) + this.plugin = plugin } display(): void { - const {containerEl} = this; + const {containerEl} = this + + containerEl.empty() + + containerEl.createEl('h2', {text: 'Formatting'}) + + new Setting(containerEl) + .setName('Daily Note Format') + .setDesc('You can find this in your daily note settings menu. ' + + 'BE SURE TO include the folder path in square brackets if your ' + + 'daily notes do not reside in the root folder of your vault!') + .addText(text => text + .setPlaceholder('e.g. [Daily/]YYYY-MM-DD') + .setValue(this.plugin.settings.dailyNoteFormat) + .onChange(async (value) => { + this.plugin.settings.dailyNoteFormat = value + await this.plugin.saveSettings() + })) + + new Setting(containerEl) + .setName('Output Format') + .setDesc('The format to output added links. Use [[link]] as a placeholder to represent a link.') + .addText(text => text + .setPlaceholder('e.g. - [[link]]') + .setValue(this.plugin.settings.outputFormat) + .onChange(async (value) => { + this.plugin.settings.outputFormat = value + await this.plugin.saveSettings() + })) - containerEl.empty(); - containerEl.createEl('h2', {text: 'Settings for my awesome plugin.'}); + containerEl.createEl('h2', {text: 'Criteria'}) new Setting(containerEl) - .setName('Setting #1') - .setDesc('It\'s a secret') + .setName('Blacklisted Tags') + .setDesc('Comma-separated list of tags. ' + + 'If a file has a tag present on this list, it won\'t be linked to! ' + + 'Leave this blank to disable this feature.') .addText(text => text - .setPlaceholder('Enter your secret') - .setValue(this.plugin.settings.mySetting) + .setPlaceholder('e.g. #daily, #💡, #🔧') + .setValue(this.plugin.settings.tags) .onChange(async (value) => { - console.log('Secret: ' + value); - this.plugin.settings.mySetting = value; - await this.plugin.saveSettings(); - })); + this.plugin.settings.tags = value + await this.plugin.saveSettings() + })) } } diff --git a/manifest.json b/manifest.json index 30ec656..711fa69 100644 --- a/manifest.json +++ b/manifest.json @@ -1,10 +1,10 @@ { - "id": "obsidian-sample-plugin", - "name": "Sample Plugin", - "version": "1.0.1", + "id": "obsidian-list-modfied", + "name": "List Modified", + "version": "0.0.1", "minAppVersion": "0.12.0", - "description": "This is a sample plugin for Obsidian. This plugin demonstrates some of the capabilities of the Obsidian API.", - "author": "Obsidian", - "authorUrl": "https://obsidian.md", + "description": "A simple obsidian plugin that links all modified files meeting certain criteria to a daily note", + "author": "Francis Kafieh", + "authorUrl": "https://github.com/franciskafieh", "isDesktopOnly": false } diff --git a/styles.css b/styles.css deleted file mode 100644 index cfd0fd7..0000000 --- a/styles.css +++ /dev/null @@ -1,4 +0,0 @@ -/* Sets all the text color to red! */ -body { - color: red; -}