initial commit

This commit is contained in:
Emerson Almeida 2024-12-07 11:19:38 -03:00
commit f0ff95cd9b
8 changed files with 2800 additions and 0 deletions

34
.github/workflows/release.yaml vendored Normal file
View file

@ -0,0 +1,34 @@
name: Release Obsidian plugin
on:
push:
tags:
- "*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: "18.x"
- name: Build plugin
run: |
npm install
npm run build
- name: Create release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
tag="${GITHUB_REF#refs/tags/}"
gh release create "$tag" \
--title="$tag" \
--draft \
main.js manifest.json styles.css

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
main.js
node_modules
data.json

49
esbuild.config.mjs Normal file
View file

@ -0,0 +1,49 @@
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
const banner = `/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
`;
const prod = process.argv[2] === "production";
const context = await esbuild.context({
banner: {
js: banner,
},
entryPoints: ["main.ts"],
bundle: true,
external: [
"obsidian",
"electron",
"@codemirror/autocomplete",
"@codemirror/collab",
"@codemirror/commands",
"@codemirror/language",
"@codemirror/lint",
"@codemirror/search",
"@codemirror/state",
"@codemirror/view",
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins,
],
format: "cjs",
target: "es2018",
logLevel: "info",
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: "main.js",
minify: prod,
});
if (prod) {
await context.rebuild();
process.exit(0);
} else {
await context.watch();
}

261
main.ts Normal file
View file

@ -0,0 +1,261 @@
import {
App,
Plugin,
PluginSettingTab,
Setting,
requestUrl,
type RequestUrlParam,
Editor,
} from "obsidian";
import moment from "moment";
interface ImportTodoistSettings {
todoistApiKey: string;
}
const IMPORT_TODOIST_SETTINGS: ImportTodoistSettings = {
todoistApiKey: "apiKey",
};
const makeToDoistRequest = async (
url: string,
method: string,
apiKey: string,
body?: string,
) => {
const requestParams: RequestUrlParam = {
url: url,
method: method,
body: body,
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
};
if (method === "GET") {
return requestUrl(requestParams)
.then((res) => res.json)
.then((res) => {
return res;
});
} else {
requestUrl(requestParams);
}
};
interface Task {
creator_id: string;
created_at: string;
assignee_id: string;
assigner_id: string;
is_completed: boolean;
content: string;
description: string;
due: {
date: string;
is_recurring: boolean;
datetime: string;
string: string;
timezone: string;
};
duration: any;
id: string;
labels: string[];
priority: number;
project_id: string;
section_id: string;
parent_id: string;
comments: Comment[];
}
interface Comment {
content: string;
id: string;
posted_at: string;
task_id: string;
}
interface Project {
id: string;
name: string;
comment_count: number;
order: number;
color: string;
is_shared: boolean;
is_favorite: boolean;
parent_id: string;
is_inbox_project: boolean;
is_team_inbox: boolean;
view_style: string;
url: string;
}
class ImportAllTasksCommand {
todoistApiKey: string;
projects: Record<string, Project> = {};
constructor(todoistApiKey: string) {
this.todoistApiKey = todoistApiKey;
}
async execute(editor: Editor) {
return this.loadData().then((tasks) => {
console.log("Importing all tasks");
const markdownTasks = tasks.map((task: Task) => {
return this.transformToMarkdown(task);
});
editor.replaceRange(markdownTasks.join("\n---\n"), editor.getCursor());
});
}
private loadData() {
return this.loadProjects()
.then(() => {
return this.allTasks();
})
.then((tasks) => {
const enrichTasksWithComments = tasks.map((task: Task) => {
return this.withComments(task);
});
return Promise.all(enrichTasksWithComments);
});
}
private transformToMarkdown(task: Task) {
const tags = task.labels
.map((label: string) => {
return `#${label}`;
})
.join(" ");
const created = `[created:: ${moment(task.created_at).format("YYYY-MM-DD")}]`;
const status = task.is_completed ? "x" : " ";
const description = task.description ? `\n${task.description}\n` : "";
const comments = task.comments
.map((comment) => {
return `${comment.content}`;
})
.join("\n\n");
const due = task.due
? `[due:: ${moment(task.due.datetime).format("YYYY-MM-DD")}]`
: "";
const id = `[id:: ${task.id}]`;
const priority = `[priority:: ${["none", "low", "medium", "high", "highest"][task.priority]}]`;
const project = `#project-${this.projects[task.project_id].name.replace(" ", "-")}`;
return `- [${status}] ${task.content} ${tags} ${project} ${created} ${priority} ${id} ${due} ${description}\n${comments}\n`;
}
private loadProjects() {
return makeToDoistRequest(
`https://api.todoist.com/rest/v2/projects`,
"GET",
this.todoistApiKey,
).then((projects: Project[]) => {
projects.forEach((project) => {
this.projects[project.id] = project;
});
});
}
private allTasks(): Promise<Task[]> {
return makeToDoistRequest(
"https://api.todoist.com/rest/v2/tasks",
"GET",
this.todoistApiKey,
);
}
private withComments(task: Task): Promise<Task> {
return makeToDoistRequest(
`https://api.todoist.com/rest/v2/comments?task_id=${task.id}`,
"GET",
this.todoistApiKey,
).then((comments) => {
task.comments = comments;
return task;
});
}
}
class Commander {
settings: ImportTodoistSettings;
constructor(settings: ImportTodoistSettings) {
this.settings = settings;
}
async importAllTasks(editor: Editor) {
const command = new ImportAllTasksCommand(this.settings.todoistApiKey);
return command.execute(editor);
}
}
export default class ImportTodoistPlugin extends Plugin {
settings: ImportTodoistSettings;
commander: Commander;
async onload() {
await this.loadSettings();
this.commander = new Commander(this.settings);
this.addCommand({
id: "import-todoist-all",
name: "Import all tasks",
editorCallback: (editor: Editor) => {
return this.commander.importAllTasks(editor);
},
});
this.addSettingTab(new ImportTodoistSettingsTab(this.app, this));
}
onunload() {}
async loadSettings() {
this.settings = Object.assign(
{},
IMPORT_TODOIST_SETTINGS,
await this.loadData(),
);
}
async saveSettings() {
await this.saveData(this.settings);
}
}
class ImportTodoistSettingsTab extends PluginSettingTab {
plugin: ImportTodoistPlugin;
constructor(app: App, plugin: ImportTodoistPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const { containerEl } = this;
containerEl.empty();
new Setting(containerEl)
.setName("Todoist API Key")
.setDesc("")
.addText((text) =>
text
.setPlaceholder("Enter your secret")
.setValue(this.plugin.settings.todoistApiKey)
.onChange(async (value) => {
this.plugin.settings.todoistApiKey = value;
await this.plugin.saveSettings();
}),
);
}
}

11
manifest.json Normal file
View file

@ -0,0 +1,11 @@
{
"id": "import-todoist",
"name": "Import Todoist",
"version": "1.0.0",
"minAppVersion": "0.15.0",
"description": "Import Todoist tasks to Obsidian",
"author": "Duke",
"authorUrl": "https://empersonalmeidax.wtf",
"fundingUrl": "https://github.com/sponsors/dukex",
"isDesktopOnly": false
}

2398
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

27
package.json Normal file
View file

@ -0,0 +1,27 @@
{
"name": "import-todoist",
"version": "1.0.0",
"description": "This import todoist tasks to obsidian",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.17.3",
"obsidian": "latest",
"tslib": "2.4.0",
"typescript": "4.7.4"
},
"dependencies": {
"moment": "^2.30.1"
}
}

17
tsconfig.json Normal file
View file

@ -0,0 +1,17 @@
{
"compilerOptions": {
"baseUrl": ".",
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
"target": "ES6",
"allowJs": true,
"noImplicitAny": true,
"moduleResolution": "node",
"importHelpers": true,
"isolatedModules": true,
"strictNullChecks": true,
"lib": ["DOM", "ES5", "ES6", "ES7"]
},
"include": ["**/*.ts"]
}