├── qotd.png ├── styles.css ├── .editorconfig ├── .gitignore ├── versions.json ├── manifest.json ├── tsconfig.json ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── package.json ├── LICENSE ├── README.md ├── CODE_OF_CONDUCT.md └── src ├── settingsTab.ts └── main.ts /qotd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twentytwokhz/quote-of-the-day/HEAD/qotd.png -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | .settings_area { 2 | margin-left: 5px; 3 | margin-right: 5px; 4 | font-size: 14px; 5 | width: 100%; 6 | } 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | insert_final_newline = true 7 | indent_style = tab 8 | indent_size = 4 9 | tab_width = 4 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Intellij 2 | *.iml 3 | .idea 4 | 5 | # npm 6 | node_modules 7 | package-lock.json 8 | 9 | # build 10 | main.js 11 | *.js.map 12 | 13 | # obsidian 14 | data.json 15 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "1.1.2": "0.9.12", 3 | "1.1.1": "0.9.12", 4 | "1.1.0": "0.9.12", 5 | "1.0.5": "0.9.12", 6 | "1.0.4": "0.9.12", 7 | "1.0.3": "0.9.12", 8 | "1.0.2": "0.9.12", 9 | "1.0.1": "0.9.12", 10 | "1.0.0": "0.9.12", 11 | "0.1.1": "0.9.12", 12 | "0.1.0": "0.9.12" 13 | } 14 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "quote-of-the-day", 3 | "name": "Quote of the Day", 4 | "version": "1.1.2", 5 | "minAppVersion": "0.12.0", 6 | "description": "Inserts random quotes in the editor", 7 | "author": "Florin Bobis", 8 | "authorUrl": "https://florin.page", 9 | "isDesktopOnly": false 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "inlineSourceMap": true, 5 | "inlineSources": true, 6 | "module": "ESNext", 7 | "target": "ES6", 8 | "allowJs": true, 9 | "noImplicitAny": true, 10 | "moduleResolution": "node", 11 | "importHelpers": true, 12 | "lib": [ 13 | "dom", 14 | "es5", 15 | "scripthost", 16 | "es2015" 17 | ] 18 | }, 19 | "include": [ 20 | "**/*.ts" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: twentytwokhz 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: enhancement 6 | assignees: twentytwokhz 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "quote-of-the-day", 3 | "version": "1.1.1", 4 | "description": "Inserts random quotes in the editor", 5 | "main": "main.js", 6 | "scripts": { 7 | "dev": "esbuild src/main.ts --bundle --external:obsidian --outdir=. --target=es2016 --format=cjs --sourcemap=inline --watch", 8 | "build": "esbuild src/main.ts --bundle --external:obsidian --outdir=. --target=es2016 --format=cjs" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "@types/node": "^16.11.1", 15 | "esbuild": "0.13.8", 16 | "obsidian": "^0.16.0", 17 | "tslib": "2.3.1", 18 | "typescript": "4.4.4" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Florin Bobiș 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Logo 4 | 5 | 6 |

Quote of the Day

7 | 8 |

9 | An Obsidian plugin to insert random quotes as Markdown. 10 |
11 |
12 | Report a Bug 13 | · 14 | Request a Feature 15 |

16 |

17 | 18 | ![GitHub release (latest by date)](https://img.shields.io/github/v/release/twentytwokhz/quote-of-the-day) 19 | ![GitHub Release Date](https://img.shields.io/github/release-date/twentytwokhz/quote-of-the-day) 20 | ![GitHub issues](https://img.shields.io/github/issues/twentytwokhz/quote-of-the-day) 21 | 22 | ![GitHub all releases](https://img.shields.io/github/downloads/twentytwokhz/quote-of-the-day/total) 23 | ![GitHub](https://img.shields.io/github/license/twentytwokhz/quote-of-the-day) 24 | 25 | 26 | 27 | ## About The Project 28 | 29 | This plugin allows you to insert a famous quote in Markdown format. It is relying on the [Quotable](https://github.com/lukePeavey/quotable) API. 30 | 31 | Courtesy to [Luke Peavey](https://github.com/lukePeavey) for providing this beautiful API for free. Thank you! 32 | 33 | ## Installing 34 | 35 | Find this plugin in the listing of community plugins in Obsidian and add it to your application. 36 | 37 | Or, if you'd like to install it manually, clone this repository to the `.obsidian/plugins/` directory in your vault, navigate to your newly cloned folder, run `npm i` or `yarn` to install dependencies, and run `npm run build` or `yarn build` to compile the plugin. 38 | 39 | 40 | 41 | The default hotkeys are: 42 | 43 | | Hotkey | Action | 44 | | --------------------------------------------------- | -------------------------------------------- | 45 | | Ctrl/Cmd + Alt + Q | **Insert Random Quote of the Day** | 46 | | Ctrl/Cmd + Alt + T | **Insert Tag-based Random Quote of the Day** | 47 | 48 | **Templates** are supported by using the `{{qotd}}` 49 | 50 | ## Options 51 | 52 | The following setting are available: 53 | 54 | | Setting | Description | 55 | | ---------------- | -------------------------------------------------------------- | 56 | | Quote Format | Format in which to display the quote | 57 | | Quote Tag Format | Format in which to display the tags in the footer of the quote | 58 | | Show Tags | Toggle display of tags in the footer of the quote | 59 | 60 | 61 | 62 | ## Contributing 63 | 64 | Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. 65 | 66 | ## License 67 | 68 | This project is licensed under the MIT License - see the [`LICENSE`](LICENSE) file for details 69 | 70 | 71 | 72 | ## Contact 73 | 74 | Florin Bobis - [@twentytwokhz](https://github.com/twentytwokhz) - florinbobis@gmail.com 75 | 76 | Project Link: [https://github.com/twentytwokhz/quote-of-the-day](https://github.com/twentytwokhz/quote-of-the-day) 77 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /src/settingsTab.ts: -------------------------------------------------------------------------------- 1 | import { App, Notice, PluginSettingTab, Setting } from "obsidian"; 2 | import QuoteOfTheDay from "./main"; 3 | 4 | export default class QotDSettingsTab extends PluginSettingTab { 5 | plugin: QuoteOfTheDay; 6 | 7 | constructor(app: App, plugin: QuoteOfTheDay) { 8 | super(app, plugin); 9 | this.plugin = plugin; 10 | } 11 | 12 | display(): void { 13 | let { containerEl } = this; 14 | 15 | containerEl.empty(); 16 | 17 | containerEl.createEl("h2", { text: "Quote of the Day Settings" }); 18 | 19 | new Setting(containerEl) 20 | .setName("Quote Format") 21 | .setDesc("Format the way the quote is displayed") 22 | .addTextArea((text) => { 23 | text.setPlaceholder("Quote format") 24 | .setValue(this.plugin.settings.quoteFormat) 25 | .onChange(async (value) => { 26 | console.log("New Quote format: " + value); 27 | //add quote format validation 28 | let valid = 29 | value.contains("{author}") && 30 | value.contains("{content}"); 31 | if (!valid) { 32 | new Notice( 33 | "Invalid format! Missing {author} or {content} field" 34 | ); 35 | return; 36 | } 37 | this.plugin.settings.quoteFormat = value; 38 | await this.plugin.saveSettings(); 39 | }); 40 | text.inputEl.setAttr("rows", 4); 41 | text.inputEl.addClass("settings_area"); 42 | }); 43 | 44 | new Setting(containerEl) 45 | .setName("Quote Tag Format") 46 | .setDesc("Format the way the quote tags are displayed") 47 | .addTextArea((text) => { 48 | text.setPlaceholder("Quote tag format") 49 | .setValue(this.plugin.settings.quoteTagFormat) 50 | .onChange(async (value) => { 51 | console.log("New Quote tag format: " + value); 52 | //add tag format validation 53 | let valid = value.contains("{tags}"); 54 | if (!valid) { 55 | new Notice("Invalid format! Missing {tags} field"); 56 | return; 57 | } 58 | this.plugin.settings.quoteTagFormat = value; 59 | await this.plugin.saveSettings(); 60 | }); 61 | text.inputEl.setAttr("rows", 4); 62 | text.inputEl.addClass("settings_area"); 63 | }); 64 | 65 | new Setting(containerEl) 66 | .setName("Quote Template Placeholder") 67 | .setDesc( 68 | "Format the way the quote placeholder is used when creating a note from template" 69 | ) 70 | .addText((text) => { 71 | text.setPlaceholder("Quote Template Placeholder") 72 | .setValue(this.plugin.settings.quoteTemplatePlaceholder) 73 | .onChange(async (value) => { 74 | console.log("New Quote template placeholder: " + value); 75 | this.plugin.settings.quoteTemplatePlaceholder = value; 76 | await this.plugin.saveSettings(); 77 | }); 78 | }); 79 | 80 | new Setting(containerEl) 81 | .setName("Filtered Quote Template Placeholder") 82 | .setDesc( 83 | "Format the way the filtered quote placeholder is used when creating a note from template" 84 | ) 85 | .addText((text) => { 86 | text.setPlaceholder("Filtered Quote Template Placeholder") 87 | .setValue(this.plugin.settings.filteredQuoteTemplatePlaceholder) 88 | .onChange(async (value) => { 89 | console.log("New Filtered Quote template placeholder: " + value); 90 | this.plugin.settings.filteredQuoteTemplatePlaceholder = value; 91 | await this.plugin.saveSettings(); 92 | }); 93 | }); 94 | 95 | new Setting(containerEl) 96 | .setName("Show Quote Tags") 97 | .setDesc("Display the quote tags") 98 | .addToggle((toggle) => 99 | toggle 100 | .setValue(this.plugin.settings.showTags) 101 | .onChange(async (value) => { 102 | console.log("New Show tags: " + value); 103 | this.plugin.settings.showTags = value; 104 | await this.plugin.saveSettings(); 105 | }) 106 | ); 107 | 108 | new Setting(containerEl) 109 | .setName("Show Quote Tags Hashtag") 110 | .setDesc("Display the quote tags with # symbol") 111 | .addToggle((toggle) => 112 | toggle 113 | .setValue(this.plugin.settings.showTagHash) 114 | .onChange(async (value) => { 115 | //console.log("New Show tags: " + value); 116 | this.plugin.settings.showTagHash = value; 117 | await this.plugin.saveSettings(); 118 | }) 119 | ); 120 | 121 | new Setting(containerEl) 122 | .setName("Quote Placeholder Interval") 123 | .setDesc( 124 | "Interval to check for quote placeholder presence and quote generation" 125 | ) 126 | .addSlider((toggle) => 127 | toggle 128 | .setLimits(5, 60, 1) 129 | .setValue(this.plugin.settings.placeholderInterval) 130 | .onChange(async (value) => { 131 | console.log("New placeholderInterval: " + value); 132 | this.plugin.settings.placeholderInterval = value; 133 | await this.plugin.saveSettings(); 134 | }) 135 | .setDynamicTooltip() 136 | ); 137 | 138 | const { moment } = window; 139 | const filters = filtersList; 140 | 141 | let dd = new Setting(this.containerEl) 142 | .setName("Quote of the Day Filters") 143 | .setDesc("Current filter: " + this.plugin.getFilters(" , ")) 144 | .addDropdown((dropdown) => { 145 | dropdown.addOption("None", "None"); 146 | filters.forEach((filter, i) => { 147 | dropdown.addOption(filters[i], filter); 148 | }); 149 | //dropdown.setValue(this.plugin.settings.filter2); 150 | dropdown.onChange((val) => { 151 | if (val == "None") { 152 | this.plugin.settings.filter = ["None"]; 153 | } 154 | else 155 | { 156 | if (this.plugin.settings.filter.includes(val)) { 157 | this.plugin.settings.filter = 158 | this.plugin.settings.filter.filter((i) => i !== val); 159 | } else { 160 | this.plugin.settings.filter.push(val); 161 | } 162 | } 163 | 164 | console.log(this.plugin.settings.filter); 165 | dd.setDesc("Current filter: " + this.plugin.getFilters(" , ")) 166 | this.plugin.saveSettings(); 167 | }); 168 | dropdown.selectEl.setAttr("multiple", null); 169 | }); 170 | } 171 | } 172 | 173 | const filtersList = [ 174 | "Age", 175 | "Athletics", 176 | "Business", 177 | "Change", 178 | "Character", 179 | "Competition", 180 | "Conservative", 181 | "Courage", 182 | "Creativity", 183 | "Education", 184 | "Ethics", 185 | "Failure", 186 | "Faith", 187 | "Family", 188 | "Famous Quotes", 189 | "Film", 190 | "Freedom", 191 | "Friendship", 192 | "Future", 193 | "Generosity", 194 | "Genius", 195 | "Gratitude", 196 | "Happiness", 197 | "Health", 198 | "History", 199 | "Honor", 200 | "Humor", 201 | "Humorous", 202 | "Imagination", 203 | "Inspirational", 204 | "Knowledge", 205 | "Leadership", 206 | "Life", 207 | "Literature", 208 | "Love", 209 | "Mathematics", 210 | "Motivational", 211 | "Nature", 212 | "Opportunity", 213 | "Pain", 214 | "Perseverance", 215 | "Philosophy", 216 | "Politics", 217 | "Power Quotes", 218 | "Proverb", 219 | "Religion", 220 | "Sadness", 221 | "Science", 222 | "Self", 223 | "Self Help", 224 | "Social Justice", 225 | "Society", 226 | "Spirituality", 227 | "Sports", 228 | "Stupidity", 229 | "Success", 230 | "Technology", 231 | "Time", 232 | "Tolerance", 233 | "Truth", 234 | "Virtue", 235 | "War", 236 | "Weakness", 237 | "Wellness", 238 | "Wisdom", 239 | "Work" 240 | ] -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { Editor, MarkdownView, Notice, Plugin } from "obsidian"; 2 | import QotDSettingsTab from "./settingsTab"; 3 | 4 | interface QotDSettings { 5 | quoteFormat: string; 6 | quoteTagFormat: string; 7 | quoteTemplatePlaceholder: string; 8 | filteredQuoteTemplatePlaceholder: string; 9 | filter: Array; 10 | showTags: boolean; 11 | showTagHash: boolean; 12 | placeholderInterval: number; 13 | } 14 | 15 | interface QuoteOfDay { 16 | quoteText: string; 17 | author: string; 18 | categories: string; 19 | } 20 | 21 | const QUOTE_API_URL = "https://florinbobis-quotes-net.hf.space/quotes"; 22 | const MAX_TAG_CHARS = 25; 23 | 24 | const DEFAULT_SETTINGS: QotDSettings = { 25 | quoteFormat: `>[!quote] Quote of the Day 26 | > {content} 27 | > — {author}✍️`, 28 | quoteTagFormat: `> --- 29 | > {tags} 30 | `, 31 | quoteTemplatePlaceholder: "{{qotd}}", 32 | filteredQuoteTemplatePlaceholder: "{{fqotd}}", 33 | showTags: false, 34 | showTagHash: true, 35 | placeholderInterval: 5, 36 | filter: [], 37 | }; 38 | 39 | export default class QuoteOfTheDay extends Plugin { 40 | settings: QotDSettings; 41 | started: boolean; 42 | 43 | getMarkdownFromQuote = (qod: QuoteOfDay) => { 44 | let text = this.settings.quoteFormat 45 | .replace("{content}", qod.quoteText) 46 | .replace("{author}", qod.author); 47 | if (this.settings.showTags) { 48 | let tagSymb = ""; 49 | if (this.settings.showTagHash) 50 | { 51 | tagSymb = "#" 52 | } 53 | let tags = ""; 54 | if (qod.categories) { 55 | tags = qod.categories.split(",").map((t) => `${tagSymb}${t}`).join(", "); 56 | } 57 | 58 | let quoteTags = this.settings.quoteTagFormat.replace( 59 | "{tags}", 60 | tags 61 | ); 62 | text = text + "\n" + quoteTags; 63 | } 64 | return text; 65 | }; 66 | 67 | sleep = (delay: number) => { 68 | return new Promise((resolve) => setTimeout(resolve, delay)); 69 | }; 70 | 71 | updateQuotePlaceholder = async () => { 72 | //replace with what is needed 73 | if (this.started) { 74 | return; 75 | } 76 | this.started = true; 77 | const file = this.app.workspace.getActiveFile(); 78 | let t = await this.app.vault.read(file); 79 | if (t.includes(this.settings.quoteTemplatePlaceholder)) { 80 | while (t.search(this.settings.quoteTemplatePlaceholder) !== -1) { 81 | await this.sleep(500); 82 | let qod = await this.getRandomQuote(); 83 | let quote = this.getMarkdownFromQuote(qod); 84 | t = t.replace(this.settings.quoteTemplatePlaceholder, quote); 85 | } 86 | this.app.vault.modify(file, t); 87 | } 88 | if (t.includes(this.settings.filteredQuoteTemplatePlaceholder)) { 89 | while (t.search(this.settings.filteredQuoteTemplatePlaceholder) !== -1) { 90 | await this.sleep(500); 91 | let qod = await this.getFilteredQuote(); 92 | let quote = this.getMarkdownFromQuote(qod); 93 | t = t.replace(this.settings.filteredQuoteTemplatePlaceholder, quote); 94 | } 95 | this.app.vault.modify(file, t); 96 | } 97 | this.started = false; 98 | }; 99 | 100 | getRandomQuote = async () => { 101 | let qod: QuoteOfDay = { 102 | quoteText: "Oops, I did it again 🙊", 103 | author: "Britney Error 😢", 104 | categories: "error", 105 | }; 106 | try { 107 | let response = await fetch(`${QUOTE_API_URL}/random?dataset=quotable`); 108 | let result = await response.json(); 109 | if (!result.statusCode) { 110 | qod = result; 111 | } 112 | } catch (err) { 113 | console.log(err); 114 | new Notice(err.message); 115 | } 116 | return qod; 117 | }; 118 | 119 | getFilteredQuote = async () => { 120 | let qod: QuoteOfDay = { 121 | quoteText: "Oops, I did it again 🙊", 122 | author: "Britney Error 😢", 123 | categories: "error", 124 | }; 125 | try { 126 | let filters = this.getFilters("|"); 127 | let response = await fetch(`${QUOTE_API_URL}/random?dataset=quotable&tags=${filters}`); 128 | let result = await response.json(); 129 | if (!result.statusCode) { 130 | qod = result; 131 | } 132 | } catch (err) { 133 | console.log(err); 134 | new Notice(err.message); 135 | } 136 | return qod; 137 | }; 138 | 139 | getFilters = (sep: string) => { 140 | let f = this.settings.filter.filter((i) => i !== "None"); 141 | return f.join(sep); 142 | } 143 | 144 | async onload() { 145 | console.log("Loading Quote of the Day plugin..."); 146 | await this.loadSettings(); 147 | 148 | // highlight-start 149 | this.registerInterval( 150 | window.setInterval( 151 | () => this.updateQuotePlaceholder(), 152 | this.settings.placeholderInterval * 1000 153 | ) 154 | ); 155 | 156 | // This adds an editor command that can perform some operation on the current editor instance 157 | this.addCommand({ 158 | id: "qotd-random", 159 | name: "Insert Random Quote of the Day", 160 | editorCallback: async (editor: Editor, view: MarkdownView) => { 161 | let qod = await this.getRandomQuote(); 162 | editor.replaceSelection(this.getMarkdownFromQuote(qod)); 163 | }, 164 | }); 165 | 166 | this.addCommand({ 167 | id: "qotd-tag", 168 | name: "Insert Random Quote of the Day by selected tag", 169 | checkCallback: (checking: boolean) => { 170 | // Conditions to check 171 | let markdownView = 172 | this.app.workspace.getActiveViewOfType(MarkdownView); 173 | if (markdownView) { 174 | // If checking is true, we're simply "checking" if the command can be run. 175 | // If checking is false, then we want to actually perform the operation. 176 | if (!checking) { 177 | const sel = markdownView.editor.getSelection(); 178 | const validSelection = sel && sel.length > 2; 179 | if (!validSelection) { 180 | return false; 181 | } 182 | } 183 | 184 | // This command will only show up in Command Palette when the check function returns true 185 | return true; 186 | } 187 | 188 | return true; 189 | }, 190 | editorCallback: async (editor: Editor, view: MarkdownView) => { 191 | let qod: QuoteOfDay = { 192 | quoteText: "Oops, cannot find that tag 🙊", 193 | author: "Tag Error 😢", 194 | categories: "error", 195 | }; 196 | try { 197 | const sel = editor.getSelection(); 198 | const validSelection = sel && sel.length > 2; 199 | if (!validSelection) { 200 | //retrieve random quote 201 | throw new Error("Invalid tag"); 202 | } 203 | const tag = sel.substr(0, MAX_TAG_CHARS).trim(); 204 | let response = await fetch( 205 | `${QUOTE_API_URL}/random?tags=${tag}` 206 | ); 207 | let result = await response.json(); 208 | if (!result.statusCode) { 209 | qod = result; 210 | } 211 | } catch (err) { 212 | console.log(err); 213 | new Notice(err.message); 214 | } 215 | editor.replaceSelection(this.getMarkdownFromQuote(qod)); 216 | }, 217 | }); 218 | 219 | // This adds a settings tab so the user can configure various aspects of the plugin 220 | this.addSettingTab(new QotDSettingsTab(this.app, this)); 221 | } 222 | 223 | onunload() {} 224 | 225 | async loadSettings() { 226 | this.settings = Object.assign( 227 | {}, 228 | DEFAULT_SETTINGS, 229 | await this.loadData() 230 | ); 231 | } 232 | 233 | async saveSettings() { 234 | await this.saveData(this.settings); 235 | } 236 | } 237 | --------------------------------------------------------------------------------