Compare commits

..

90 commits

Author SHA1 Message Date
John Mavrick
a3e378f219
Merge pull request #37 from Ferrhat/main
Update packages
2024-04-05 14:54:18 -07:00
Ferrhat
c63f039b11 Update packages 2024-01-12 00:23:48 +01:00
ransurf
3c7d2099bd
Merge pull request #31 from Ethanil/main
added all-Tab
2022-04-20 03:09:30 -07:00
Ethanil
44893508dc
added all-Tab 2022-04-13 16:36:56 +02:00
SuperChamp234
64d045bbc5 upped version to 1.0.1 2022-01-15 12:27:25 +05:30
SuperChamp234
fc6506525c Fixed Indent issue 2022-01-15 12:26:29 +05:30
SuperChamp234
b80d2f8dc1 Bug: Indent CSS bug 2022-01-15 11:50:27 +05:30
SuperChamp234
67463a648d Merge branch 'main' of https://github.com/SuperChamp234/habitica-sync 2022-01-15 11:26:57 +05:30
Zain
b2c93a4747
Merge pull request #25 from SuperChamp234/dev
Update README.md
2022-01-15 11:25:51 +05:30
ransurf
3d09f20d84
Update README.md 2022-01-14 23:53:19 -06:00
SuperChamp234
eadd8d9f8c fixed messages 2022-01-15 11:23:00 +05:30
Zain
e7b4572d2e
Merge pull request #24 from SuperChamp234/dev
Version 1.0.0 update
2022-01-15 09:51:24 +05:30
Zain
350ab38135
Merge branch 'main' into dev 2022-01-15 09:51:09 +05:30
SuperChamp234
615f3e8a78 Bumped up the version no(1.0.0) 2022-01-15 09:47:49 +05:30
SuperChamp234
c1851c2fc6 Minor error fixes 2022-01-15 09:45:50 +05:30
SuperChamp234
82780ba589 CSS changes for buttons, fixed numbers in stats 2022-01-15 09:23:35 +05:30
SuperChamp234
e4d3c78a9f Small CSS changes to cron button 2022-01-15 09:00:21 +05:30
ransurf
932562ab2e fix flexbox issue for empty tabs 2022-01-14 15:53:03 -08:00
SuperChamp234
22616cbed9 Commit to dev branch includes
- Due date on Todos
- Dailies segragation to not include non-due Dailies
- Checklist-items are now working
- Made a completely new wrapper for markdown+emojis, can render emojis in description as well as task name
2022-01-14 17:55:47 +05:30
SuperChamp234
88fdde519d Added a wrapper Markdown Rendered to render BOTH Emojis and Mardown as HTML 2022-01-14 14:39:21 +05:30
ransurf
916236db5d Implement subtasks view and settings option,
clean up CSS
2022-01-13 12:47:36 -08:00
ransurf
e31d103dd7
Update README.md 2021-11-21 02:15:53 -06:00
ransurf
0ac05ef6d3
Update README.md 2021-11-21 02:14:41 -06:00
Zain
6865d4f080
Fixed merge conflict error made by Ran lol 2021-11-21 13:18:07 +05:30
ransurf
4878ed8aa8
Semi-update readme
visuals are outdated
2021-11-21 01:47:32 -06:00
ransurf
dfc4e55cb5 Merge branch 'dev' of https://github.com/SuperChamp234/habitica-sync into dev 2021-11-20 23:40:02 -08:00
ransurf
6384c98181 Added "Show Task Description" Setting 2021-11-20 23:39:09 -08:00
Zain
54e6000eb8
Fixed empty space at the bottom, fixed warning at main.ts 2021-11-21 12:47:21 +05:30
Zain
d10a2ca747
Fixed cron and minor CSS tweaks 2021-11-21 12:17:46 +05:30
Zain
8deee0ecc6
Merge pull request #17 from kkzzhizhou/main
Show task notes and add rewards panel. UI Overhaul. Credit to @kkzzhizhou!
2021-11-21 11:43:37 +05:30
kkzzhizhou
e45182547f add markdown render 2021-11-20 17:11:13 +08:00
kkzzhizhou
9b15bb1236 add Rewardview Panel 2021-11-20 13:49:24 +08:00
kkzzhizhou
bc97bcfc3f add rewards panels and change styles 2021-11-18 14:16:20 +08:00
Zain
ffa79454a0
Removed unnecessary logs 2021-11-16 19:15:25 +05:30
Zain
d29bfd9995
Merge pull request #13 from SuperChamp234/dev
Version 0.9.15
2021-11-16 19:11:00 +05:30
Zain
86dc33e57c
Merge branch 'main' into dev 2021-11-16 19:10:49 +05:30
Zain
288f0a0b09
updated version number 2021-11-16 19:06:15 +05:30
Zain
4361341f56
Merge pull request #12 from SuperChamp234/emoji-fix
Emoji fix
2021-11-16 19:01:51 +05:30
Zain
9d6c50ef88
fixed css problem: habit content width too thin 2021-11-16 18:59:11 +05:30
Zain
3d61b94932
Added Emoji Support 2021-11-16 18:53:03 +05:30
Zain
f1915876ba
Merge pull request #11 from SuperChamp234/CronImplementation
Cron implementation
2021-11-16 18:32:34 +05:30
Zain
25a1505fd0
Cron-Implementation: Tested! 2021-11-16 18:29:31 +05:30
Zain
412f9ed431
MAdded getter and setter methods for username and credentials 2021-11-01 11:53:35 +05:30
Zain
e0f400b761
CronImplementation 3: tested 2021-11-01 11:41:31 +05:30
Zain
b3282a360c
Cron Implementation - tested 2021-10-31 16:39:47 +05:30
Zain
af3b2fe53b Cron implementation 2: Untested 2021-10-31 16:33:58 +05:30
Zain
04f207db5d
Implemented Cron - untested 2021-10-30 22:34:41 +05:30
Zain
b7b1c3848f
Changed Application name in Plugin and Added function to run Cron 2021-10-30 21:10:09 +05:30
Zain
af6f93d997
Merge pull request #9 from SuperChamp234/dev
Sync Syntax Change
2021-10-30 10:08:02 +05:30
Zain
e8b010a3c5
Changed sync syntax and removed uncessary function 2021-10-30 10:04:59 +05:30
ransurf
faa33a5655 Encapsulated user information into App class 2021-10-28 21:53:15 -07:00
ransurf
6a7651c04a
Revert CSS Changes 2021-10-24 13:50:42 -06:00
SuperChamp234
f996379243
Merge pull request #8 from SuperChamp234/dev
CSS improvements
2021-10-23 23:35:42 -07:00
Leoh
86f4fd0d74 updated version patch no 2021-10-24 12:03:11 +05:30
Leoh
f1a130077a CSS change - fixed rogue li symbols 2021-10-24 12:02:02 +05:30
ransurf
2aa6c949fb
Update README.md 2021-10-23 14:54:50 -06:00
Leoh
68eff40b6b Updated release number 2021-10-23 18:08:16 +05:30
Leoh
a8a4e32f7b Removed repeated code and added reusability for scoring tasks 2021-10-23 18:06:04 +05:30
Leoh
977cacb8f6 removed extra files 2021-10-23 16:48:29 +05:30
Leoh
2e0ec9d021 Merge branch 'main' of https://github.com/SuperChamp234/habitica-sync 2021-10-23 16:42:03 +05:30
Leoh
bc77246e44 Removed extra build files 2021-10-23 16:41:46 +05:30
Leoh
4a9f52df3a Removed unnecessary Notice method 2021-10-23 16:41:11 +05:30
SuperChamp234
dc05ce086e
Added License 2021-10-23 00:15:30 -07:00
Leoh
f3e8047322 merge commit 1 2021-10-23 10:14:26 +05:30
ransurf
24f22d28ca
updated readme, add kofi link 2021-10-19 21:40:28 -06:00
ransurf
103620f3ef Fixed icons sizing, started habit menu css 2021-10-19 20:36:10 -07:00
Leoh
a3cc611dbc Removed unnecessary console logs 2021-10-19 19:29:31 +05:30
SuperChamp234
75b18f2f2a
Merge pull request #6 from SuperChamp234/task-menu
Refractored and features added
2021-10-19 06:45:21 -07:00
Leoh
25312a6d93 Cleaned up Taskview Index 2021-10-19 19:13:48 +05:30
Leoh
0280d5bd3a Refractoring: Removed unnecessary imports 2021-10-19 19:10:14 +05:30
Leoh
0cd5fde19c Refractoring: Removed unnecessary component Reactview 2021-10-19 19:08:05 +05:30
Leoh
30dd6590ec Multiple Changes 2021-10-19 10:54:06 +05:30
ransurf
a4ebd7aab3 Created task menu, started habits menu 2021-10-17 21:58:59 -07:00
ransurf
9fe8405bab Fixed command name 2021-10-17 19:36:48 -07:00
ransurf
048a6725ce Fixed command name 2021-10-17 19:36:30 -07:00
ransurf
a8e28b2ab7
Update readme 2021-10-17 18:18:08 -06:00
SuperChamp234
bb4e7cdb92
Update README.md 2021-10-17 05:51:51 -07:00
Leoh
470157b672 Added source code files which were left out 2021-10-17 18:18:25 +05:30
Leoh
32f0e6bc98 Organized all files. Code goes in src/ Added icon 2021-10-17 18:16:52 +05:30
Leoh
6b715682f9 Removed all Example variables, added credential fail handler 2021-10-17 17:40:37 +05:30
Leoh
cd3c81cf5c Icon added lol 2021-10-17 13:16:32 +05:30
ransurf
08664f2aaf changed icon, fixed plugin name 2021-10-16 21:52:57 -07:00
ransurf
a2d3067b75 yes 2021-10-16 21:49:44 -07:00
Leoh
f195546524 CSS Added 2021-10-17 00:00:39 +05:30
SuperChamp234
e3f1d13ee0
Merge pull request #4 from SuperChamp234/todo-tabs
CSS and Tabs Added
2021-10-16 11:04:24 -07:00
Leoh
9c58769a99 CSS and Tabs Added 2021-10-16 23:33:38 +05:30
SuperChamp234
964829cbb7
Update README.md 2021-10-16 09:08:30 -07:00
Leoh
a84b981019 Cleaned up files 2021-10-16 15:49:23 +05:30
Leoh
30dfdeaee7 build files added 2021-10-16 14:25:08 +05:30
SuperChamp234
45a760e172
Merge pull request #3 from SuperChamp234/checkboxes-sync
Multiple Updates
2021-10-16 01:50:29 -07:00
42 changed files with 1371 additions and 568 deletions

9
.gitignore vendored
View file

@ -6,12 +6,17 @@
node_modules node_modules
package-lock.json package-lock.json
# build # yarn
main.js yarn.lock
*.js.map *.js.map
# obsidian # obsidian
data.json data.json
#build_files
main.js
#vscode #vscode
.vscode .vscode

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Zain Siddavatam and John Mavrick Reyes
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,57 +1,46 @@
## Obsidian Sample Plugin # Habitica Sync in Obsidian
This plugin for Obsidian incorporates a view to display and interact with the task management app Habitica.
This is a sample plugin for Obsidian (https://obsidian.md). Please open issues for any bugs/functionality requests :)
This project uses Typescript to provide type checking and documentation. ## Usage
The repo depends on the latest plugin API (obsidian.d.ts) in Typescript Definition format, which contains TSDoc comments describing what it does. The plugin's view is enabled by clicking on the "Open Habitica Pane" option in the side ribbon (default hotkey is `Ctrl+Shift+H`).
**Note:** The Obsidian API is still in early alpha and is subject to change at any time! To sync your Habitica account, go to the settings page of the plugin and enter your user ID and API token credentials.
## Features
### Pane View
#### View stats (HP, XP, coins)
#### Views
Task Information:
- Title, description, subtasks
- Markdown and emoji support
This sample plugin demonstrates some of the basic functionality the plugin API can do. Tabs:
- Changes the default font color to red using `styles.css`. - To Do's
- Adds a ribbon icon, which shows a Notice when clicked. - Active/Completed
- Adds a command "Open Sample Modal" which opens a Modal. - Dailies
- Adds a plugin setting tab to the settings page. - Due/Not Due/Completed
- Registers a global click event and output 'click' to the console. - [![Image from Gyazo](https://i.gyazo.com/1966b17f954dcffa954922570e860a06.png)](https://gyazo.com/1966b17f954dcffa954922570e860a06)
- Registers a global interval which logs 'setInterval' to the console. - Habits
- [![Image from Gyazo](https://i.gyazo.com/280494e620fc91548838d5b29a62652b.png)](https://gyazo.com/280494e620fc91548838d5b29a62652b)
- Rewards
#### Interactivity
- Check off tasks/dailies in the view
- Can uncheck completed habits/todos
- [![Image from Gyazo](https://i.gyazo.com/efb858cd9d54f9d9df936da1bd5858ed.gif)](https://gyazo.com/efb858cd9d54f9d9df936da1bd5858ed)
- modify habit counters (+/-)
### First time developing plugins? ### Settings
Quick starting guide for new plugin devs: The following two inputs help fetch your user data to be displayed in the Obsidian view:
- **Habitica User ID:** You can find this by clicking on the "User" icon in the top right of the Habitica webapp, "Settings", then "API"
- **Habitica Token API:** You can find this by clicking on the "User" icon in the top right of the Habitica webapp, "Settings", then "API"
- **Show Task Descriptions:** Toggles whether description/notes for tasks will be shown or not
- **Show Subtasks:** Toggles whether subtasks for to do's/dailies will be shown or not
- Make a copy of this repo as a template with the "Use this template" button (login to GitHub if you don't see it). ## Roadmap
- Clone your repo to a local development folder. For convenience, you can place this folder in your `.obsidian/plugins/your-plugin-name` folder.
- Install NodeJS, then run `npm i` in the command line under your repo folder.
- Run `npm run dev` to compile your plugin from `main.ts` to `main.js`.
- Make changes to `main.ts` (or create new `.ts` files). Those changes should be automatically compiled into `main.js`.
- Reload Obsidian to load the new version of your plugin.
- Enable plugin in settings window.
- For updates to the Obsidian API run `npm update` in the command line under your repo folder.
### Releasing new releases *Feel free to support us and donate!*
- Update your `manifest.json` with your new version number, such as `1.0.1`, and the minimum Obsidian version required for your latest release. <a href='https://ko-fi.com/leonardandran' target='_blank'><img height='35' style='border:0px;height:46px;' src='https://az743702.vo.msecnd.net/cdn/kofi3.png?v=0' border='0' alt='Buy Me a Coffee at ko-fi.com' />
- Update your `versions.json` file with `"new-plugin-version": "minimum-obsidian-version"` so older versions of Obsidian can download an older version of your plugin that's compatible.
- Create new GitHub release using your new version number as the "Tag version". Use the exact version number, don't include a prefix `v`. See here for an example: https://github.com/obsidianmd/obsidian-sample-plugin/releases
- Upload the files `manifest.json`, `main.js`, `styles.css` as binary attachments.
- Publish the release.
### Adding your plugin to the community plugin list
- Publish an initial version.
- Make sure you have a `README.md` file in the root of your repo.
- Make a pull request at https://github.com/obsidianmd/obsidian-releases to add your plugin.
### How to use
- Clone this repo.
- `npm i` or `yarn` to install dependencies
- `npm run dev` to start compilation in watch mode.
### Manually installing the plugin
- Copy over `main.js`, `styles.css`, `manifest.json` to your vault `VaultFolder/.obsidian/plugins/your-plugin-id/`.
### API Documentation
See https://github.com/obsidianmd/obsidian-api

View file

@ -1,5 +0,0 @@
import * as React from "react";
import App from "./view/App";
export const ReactView = () => {
return React.createElement(App, null);
};

View file

@ -1,8 +0,0 @@
import * as React from "react";
import App from "./view/App";
export default function ReactView(props: any){
return(
<App username={props.userID} apiToken={props.tokenAPI} plugin={props.plugin}/>
)
}

57
main.ts
View file

@ -1,57 +0,0 @@
import { Notice, Plugin } from "obsidian";
import { ExampleSettingsTab } from "./settings";
import { ExampleView, VIEW_TYPE_EXAMPLE} from "./view"
interface ExamplePluginSettings {
userID: string
apiToken: string
}
const DEFAULT_SETTINGS: Partial<ExamplePluginSettings> = {
userID: "",
apiToken: ""
}
export default class ExamplePlugin extends Plugin {
settings: ExamplePluginSettings;
view: ExampleView;
displayNotice(message: string){
new Notice(message)
}
async onload() {
await this.loadSettings();
this.addSettingTab(new ExampleSettingsTab(this.app, this));
this.registerView(
VIEW_TYPE_EXAMPLE,
(leaf) => (this.view = new ExampleView(leaf, this))
);
this.addRibbonIcon("dice", "Open Habitica Pane", () => { //activate view
this.activateView();
});
}
async loadSettings() {
this.settings = Object.assign(DEFAULT_SETTINGS, await this.loadData())
}
async saveSettings() {
await this.saveData(this.settings);
}
async onunload() {
await this.view.onClose();
this.app.workspace
.getLeavesOfType(VIEW_TYPE_EXAMPLE)
.forEach((leaf) => leaf.detach());
}
async activateView() {
this.app.workspace.detachLeavesOfType(VIEW_TYPE_EXAMPLE);
await this.app.workspace.getRightLeaf(false).setViewState({
type: VIEW_TYPE_EXAMPLE,
active: true,
});
this.app.workspace.revealLeaf(
this.app.workspace.getLeavesOfType(VIEW_TYPE_EXAMPLE)[0]
);
}
}

View file

@ -1,10 +1,11 @@
{ {
"id": "obsidian-habitica-integration", "id": "obsidian-habitica-integration",
"name": "AAA Obsidian Habitica Integration", "name": "Habitica Sync",
"version": "0.0.1", "version": "1.0.2",
"minAppVersion": "0.9.12", "minAppVersion": "0.9.12",
"description": "This plugin helps integrate Habitica user tasks and stats into Obsidian", "description": "This plugin helps integrate Habitica user tasks and stats into Obsidian",
"author": "Leoh and Ran", "author": "Leoh and Ran",
"authorUrl": "", "authorUrl": "",
"isDesktopOnly": false "isDesktopOnly": false,
"js": "main.js"
} }

View file

@ -1,34 +0,0 @@
import { ItemView,WorkspaceLeaf } from "obsidian";
import * as React from "react";
import * as ReactDOM from "react-dom";
import ReactView from "./ReactView";
import ExamplePlugin from "main";
export const VIEW_TYPE_EXAMPLE = "example-view"
export class ExampleView extends ItemView {
plugin: ExamplePlugin;
constructor(leaf: WorkspaceLeaf) {
super(leaf)
}
getViewType() {
return VIEW_TYPE_EXAMPLE
}
getDisplayText() {
return "Example View"
}
async onOpen() {
ReactDOM.render(
React.createElement(ReactView),
this.containerEl.children[1]
)
}
async onClose(){
ReactDOM.unmountComponentAtNode(this.containerEl.children[1]);
}
}

View file

@ -1,34 +1,51 @@
{ {
"name": "obsidian-habitica-integration", "name": "obsidian-habitica-integration",
"version": "0.12.0", "version": "1.0.0",
"description": "This plugin allows for Habitica integration into Obsidian", "description": "This plugin allows for Habitica integration into Obsidian",
"main": "main.js", "main": "main.js",
"scripts": { "scripts": {
"dev": "rollup --config rollup.config.js -w", "dev": "rollup --config rollup.config.mjs -w",
"build": "rollup --config rollup.config.js --environment BUILD:production" "build": "rollup --config rollup.config.mjs --environment BUILD:production",
"dev2": "obsidian-plugin dev src/main.ts"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "Leonard and Ran",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@rollup/plugin-commonjs": "^18.0.0", "@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^11.2.1", "@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-typescript": "^8.2.1", "@rollup/plugin-node-resolve": "^15.2.3",
"@types/node": "^14.14.37", "@rollup/plugin-typescript": "^11.1.6",
"@types/react": "^17.0.27", "@types/markdown-it": "^13.0.7",
"@types/react-dom": "^17.0.9", "@types/markdown-it-emoji": "^2.0.4",
"css-loader": "^6.4.0", "@types/node": "^20.11.0",
"extract-text-webpack-plugin": "^2.1.2", "@types/node-emoji": "^1.8.2",
"obsidian": "^0.12.0", "@types/react": "^18.2.47",
"rollup": "^2.32.1", "@types/react-dom": "^18.2.18",
"style-loader": "^3.3.0", "@types/react-tabs": "^5.0.4",
"tslib": "^2.2.0", "@types/twemoji": "^13.1.1",
"typescript": "^4.2.4" "css-loader": "^6.9.0",
"mini-css-extract-plugin": "^2.7.7",
"obsidian": "^1.4.11",
"obsidian-plugin-cli": "^0.0.5",
"rollup": "^4.9.4",
"style-loader": "^3.3.4",
"tslib": "^2.6.2",
"typescript": "^5.3.3",
"webpack": "^5.89.0"
}, },
"dependencies": { "dependencies": {
"node": "^16.10.0", "markdown-it": "^14.0.0",
"node-fetch": "^3.0.0", "markdown-it-emoji": "^2.0.2",
"react": "^17.0.2", "moment": "^2.30.1",
"react-dom": "^17.0.2" "node": "^21.2.0",
"node-emoji": "^2.1.3",
"node-fetch": "^3.3.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-emoji-render": "^2.0.1",
"react-markdown": "^9.0.1",
"react-tabs": "^6.0.2",
"twemoji": "^14.0.2"
} }
} }

View file

@ -1,6 +1,7 @@
import typescript from '@rollup/plugin-typescript'; import typescript from '@rollup/plugin-typescript';
import {nodeResolve} from '@rollup/plugin-node-resolve'; import {nodeResolve} from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs'; import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
const isProd = (process.env.BUILD === 'production'); const isProd = (process.env.BUILD === 'production');
@ -12,7 +13,7 @@ if you want to view the source visit the plugins github repository
`; `;
export default { export default {
input: 'main.ts', input: 'src/main.ts',
output: { output: {
dir: '.', dir: '.',
sourcemap: 'inline', sourcemap: 'inline',
@ -26,5 +27,6 @@ export default {
typescript(), typescript(),
nodeResolve({browser: true}), nodeResolve({browser: true}),
commonjs(), commonjs(),
json(),
] ]
}; };

View file

@ -1,23 +0,0 @@
import { __awaiter } from "tslib";
import { PluginSettingTab, Setting } from "obsidian";
export class ExampleSettingsTab extends PluginSettingTab {
constructor(app, plugin) {
super(app, plugin);
this.plugin = plugin;
}
display() {
let { containerEl } = this;
containerEl.empty();
new Setting(containerEl)
.setName("Date format")
.setDesc("Default date format")
.addText((text) => text
.setPlaceholder("MMMM dd, yyyy")
.setValue(this.plugin.settings.dateFormat)
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
this.plugin.settings.dateFormat = value;
yield this.plugin.saveSettings();
})));
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2V0dGluZ3MuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJzZXR0aW5ncy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQ0EsT0FBTyxFQUFPLGdCQUFnQixFQUFFLE9BQU8sRUFBRSxNQUFNLFVBQVUsQ0FBQztBQUUxRCxNQUFNLE9BQU8sa0JBQW1CLFNBQVEsZ0JBQWdCO0lBR3BELFlBQVksR0FBUSxFQUFFLE1BQXFCO1FBQ3ZDLEtBQUssQ0FBQyxHQUFHLEVBQUUsTUFBTSxDQUFDLENBQUE7UUFDbEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUE7SUFDeEIsQ0FBQztJQUVELE9BQU87UUFDSCxJQUFJLEVBQUUsV0FBVyxFQUFFLEdBQUcsSUFBSSxDQUFDO1FBQzNCLFdBQVcsQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUVwQixJQUFJLE9BQU8sQ0FBQyxXQUFXLENBQUM7YUFDbkIsT0FBTyxDQUFDLGFBQWEsQ0FBQzthQUN0QixPQUFPLENBQUMscUJBQXFCLENBQUM7YUFDOUIsT0FBTyxDQUFDLENBQUMsSUFBSSxFQUFFLEVBQUUsQ0FFZCxJQUFJO2FBQ0MsY0FBYyxDQUFDLGVBQWUsQ0FBQzthQUMvQixRQUFRLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDO2FBQ3pDLFFBQVEsQ0FBQyxDQUFPLEtBQUssRUFBRSxFQUFFO1lBQ3RCLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLFVBQVUsR0FBRyxLQUFLLENBQUM7WUFDeEMsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLFlBQVksRUFBRSxDQUFDO1FBQ3JDLENBQUMsQ0FBQSxDQUFDLENBQ1QsQ0FBQztJQUNWLENBQUM7Q0FDSiIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBFeGFtcGxlUGx1Z2luIGZyb20gXCJtYWluXCI7XHJcbmltcG9ydCB7IEFwcCwgUGx1Z2luU2V0dGluZ1RhYiwgU2V0dGluZyB9IGZyb20gXCJvYnNpZGlhblwiO1xyXG5cclxuZXhwb3J0IGNsYXNzIEV4YW1wbGVTZXR0aW5nc1RhYiBleHRlbmRzIFBsdWdpblNldHRpbmdUYWIge1xyXG4gICAgcGx1Z2luOiBFeGFtcGxlUGx1Z2luO1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKGFwcDogQXBwLCBwbHVnaW46IEV4YW1wbGVQbHVnaW4pIHtcclxuICAgICAgICBzdXBlcihhcHAsIHBsdWdpbilcclxuICAgICAgICB0aGlzLnBsdWdpbiA9IHBsdWdpblxyXG4gICAgfVxyXG5cclxuICAgIGRpc3BsYXkoKTogdm9pZCB7XHJcbiAgICAgICAgbGV0IHsgY29udGFpbmVyRWwgfSA9IHRoaXM7XHJcbiAgICAgICAgY29udGFpbmVyRWwuZW1wdHkoKTtcclxuICAgICAgICBcclxuICAgICAgICBuZXcgU2V0dGluZyhjb250YWluZXJFbClcclxuICAgICAgICAgICAgLnNldE5hbWUoXCJEYXRlIGZvcm1hdFwiKVxyXG4gICAgICAgICAgICAuc2V0RGVzYyhcIkRlZmF1bHQgZGF0ZSBmb3JtYXRcIilcclxuICAgICAgICAgICAgLmFkZFRleHQoKHRleHQpID0+IFxyXG4gICAgICAgICAgICBcclxuICAgICAgICAgICAgICAgIHRleHRcclxuICAgICAgICAgICAgICAgICAgICAuc2V0UGxhY2Vob2xkZXIoXCJNTU1NIGRkLCB5eXl5XCIpXHJcbiAgICAgICAgICAgICAgICAgICAgLnNldFZhbHVlKHRoaXMucGx1Z2luLnNldHRpbmdzLmRhdGVGb3JtYXQpXHJcbiAgICAgICAgICAgICAgICAgICAgLm9uQ2hhbmdlKGFzeW5jICh2YWx1ZSkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnBsdWdpbi5zZXR0aW5ncy5kYXRlRm9ybWF0ID0gdmFsdWU7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGF3YWl0IHRoaXMucGx1Z2luLnNhdmVTZXR0aW5ncygpO1xyXG4gICAgICAgICAgICAgICAgICAgIH0pXHJcbiAgICAgICAgICAgICk7XHJcbiAgICB9XHJcbn0iXX0=

View file

@ -1,42 +0,0 @@
import ExamplePlugin from "main";
import { App, PluginSettingTab, Setting } from "obsidian";
export class ExampleSettingsTab extends PluginSettingTab {
plugin: ExamplePlugin;
constructor(app: App, plugin: ExamplePlugin) {
super(app, plugin)
this.plugin = plugin
}
display(): void {
let { containerEl } = this;
containerEl.empty();
new Setting(containerEl)
.setName("Habitica User ID")
.setDesc("Can be found in Settings > API")
.addText((text) =>
text
.setPlaceholder("User ID")
.setValue(this.plugin.settings.userID)
.onChange(async (value) => {
this.plugin.settings.userID = value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName("Habitica API Token")
.setDesc("Can be found in Settings > API")
.addText((text) =>
text
.setPlaceholder("API Token")
.setValue(this.plugin.settings.apiToken)
.onChange(async (value) => {
this.plugin.settings.apiToken = value;
await this.plugin.saveSettings();
})
);
}
}

71
src/main.ts Normal file
View file

@ -0,0 +1,71 @@
import { Plugin } from "obsidian";
import { HabiticaSyncSettingsTab } from "./settings";
import { HabiticaSyncView, VIEW_TYPE} from "./view"
interface HabiticaSyncSettings {
userID: string
apiToken: string
showTaskDescription: boolean
showSubTasks: boolean
dueDateFormat: string
}
const DEFAULT_SETTINGS: Partial<HabiticaSyncSettings> = {
userID: "",
apiToken: "",
showTaskDescription: true,
showSubTasks: true,
dueDateFormat: "DD-MM-YYYY"
}
export default class HabiticaSync extends Plugin {
settings: HabiticaSyncSettings;
view: HabiticaSyncView;
async onload() {
console.log("load plugin: habitica-sync")
await this.loadSettings();
this.addSettingTab(new HabiticaSyncSettingsTab(this.app, this));
this.registerView(
VIEW_TYPE,
(leaf) => (new HabiticaSyncView(leaf, this))
);
this.addRibbonIcon("popup-open", "Open Habitica Pane", () => {
this.activateView();
});
this.addCommand({
id: "habitica-view-open",
name: "Open Pane",
hotkeys: [{ modifiers: ["Mod", "Shift"], key: "h"}],
callback: () => {
this.activateView();
}
});
}
async loadSettings() {
this.settings = Object.assign(DEFAULT_SETTINGS, await this.loadData())
}
async saveSettings() {
await this.saveData(this.settings);
}
async onunload() {
await this.view.onClose();
this.app.workspace
.getLeavesOfType(VIEW_TYPE)
.forEach((leaf) => leaf.detach());
}
async activateView() {
this.app.workspace.detachLeavesOfType(VIEW_TYPE);
await this.app.workspace.getRightLeaf(false).setViewState({
type: VIEW_TYPE,
active: true,
});
this.app.workspace.revealLeaf(
this.app.workspace.getLeavesOfType(VIEW_TYPE)[0]
);
}
}

80
src/settings.ts Normal file
View file

@ -0,0 +1,80 @@
import HabiticaSync from "./main";
import { App, PluginSettingTab, Setting } from "obsidian";
import moment from "moment";
export class HabiticaSyncSettingsTab extends PluginSettingTab {
plugin: HabiticaSync;
constructor(app: App, plugin: HabiticaSync) {
super(app, plugin)
this.plugin = plugin
}
display(): void {
let { containerEl } = this;
containerEl.empty();
new Setting(containerEl)
.setName("Habitica User ID")
.setDesc("Can be found in Settings > API")
.addText((text) =>
text
.setPlaceholder("User ID")
.setValue(this.plugin.settings.userID)
.onChange(async (value) => {
this.plugin.settings.userID = value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName("Habitica API Token")
.setDesc("Can be found in Settings > API")
.addText((text) =>
text
.setPlaceholder("API Token")
.setValue(this.plugin.settings.apiToken)
.onChange(async (value) => {
this.plugin.settings.apiToken = value;
await this.plugin.saveSettings();
})
);
new Setting(containerEl)
.setName("Show Task Descriptions")
.setDesc("Updates require pane re-opening")
.addToggle(cb => {
cb
.setValue(this.plugin.settings.showTaskDescription)
.onChange(async (isEnable) => {
this.plugin.settings.showTaskDescription = isEnable;
await this.plugin.saveSettings();
})
});
new Setting(containerEl)
.setName("Show Sub-Tasks")
.setDesc("Updates require pane re-opening")
.addToggle(cb => {
cb
.setValue(this.plugin.settings.showSubTasks)
.onChange(async (isEnable) => {
this.plugin.settings.showSubTasks = isEnable;
await this.plugin.saveSettings();
})
});
new Setting(containerEl)
.setName("Due Date Format")
.setDesc("Update requires pane re-opening, check moment.js docs for formatting. Current Format: " + moment().format(this.plugin.settings.dueDateFormat))
.addText((text) =>
text
.setPlaceholder("DD-MM-YYYY")
.setValue(this.plugin.settings.dueDateFormat)
.onChange(async (value) => {
this.plugin.settings.dueDateFormat = value;
await this.plugin.saveSettings();
})
);
}
}

37
src/view.tsx Normal file
View file

@ -0,0 +1,37 @@
import { ItemView,WorkspaceLeaf } from "obsidian";
import * as React from "react";
import * as ReactDOM from "react-dom";
import App from "./view/App"
import HabiticaSync from "./main";
export const VIEW_TYPE = "example-view"
export class HabiticaSyncView extends ItemView {
plugin: HabiticaSync;
constructor(leaf: WorkspaceLeaf, plugin: HabiticaSync) {
super(leaf)
this.plugin = plugin
}
getViewType() {
return VIEW_TYPE
}
getDisplayText() {
return "Habitica Pane"
}
getIcon(): string {
return "popup-open"
}
async onOpen() {
ReactDOM.render(
<App plugin={this.plugin}/>,
this.containerEl.children[1]
)
}
async onClose(){
ReactDOM.unmountComponentAtNode(this.containerEl.children[1]);
}
}

227
src/view/App.tsx Normal file
View file

@ -0,0 +1,227 @@
import * as React from "react";
import { Notice } from "obsidian";
import { getStats, scoreTask, makeCronReq, costReward, scoreChecklistItem } from "./habiticaAPI"
import Statsview from "./Components/Statsview"
import Taskview from "./Components/Taskview"
import ReactDOM from "react-dom";
class App extends React.Component<any, any> {
private _username = "";
public get username() {
return this._username;
}
public set username(value) {
this._username = value;
}
private _credentials = "";
public get credentials() {
return this._credentials;
}
public set credentials(value) {
this._credentials = value;
}
constructor(props: any) {
super(props)
this.username = this.props.plugin.settings.userID
this.credentials = this.props.plugin.settings.apiToken
this.state = {
needCron: false,
isLoaded: false,
user_data: {
profile: {
name: "",
},
stats: {
hp: 0,
lvl: 0,
gold: 0,
},
lastCron: "",
},
todos: [],
dailys: [],
habits: [],
}
this.handleChangeTodos = this.handleChangeTodos.bind(this);
this.handleChangeDailys = this.handleChangeDailys.bind(this);
this.handleChangeHabits = this.handleChangeHabits.bind(this);
this.handleChangeRewards = this.handleChangeRewards.bind(this);
this.handleChangeChecklistItem = this.handleChangeChecklistItem.bind(this);
this.runCron = this.runCron.bind(this);
}
CheckCron(lastCron: string) {
let cronDate = new Date(lastCron);
let now = new Date();
if (cronDate.getDate() != now.getDate() || (cronDate.getMonth() != now.getMonth() || cronDate.getFullYear() != now.getFullYear())) {
return (
<div className="cron">
<div id="cronMessage"> Welcome back! Please check your tasks for the last day and hit continue to get your daily rewards.</div>
<button id="cronButton" onClick={this.runCron}>Continue</button>
</div>
);
}
else {
return null
};
}
async runCron() {
console.log("running cron");
try {
let response = await makeCronReq(this.username, this.credentials);
this.setState({
needCron: false,
})
} catch (error) {
console.log(error);
new Notice("There was an error running the cron. Please try again later.");
}
this.reloadData();
}
async reloadData() {
try {
let response = await getStats(this.username, this.credentials);
let result = await response.json();
if (result.success === false) {
new Notice('Login Failed, Please check credentials and try again!');
}
else {
this.setState({
isLoaded: true,
user_data: result,
tasks: result.tasks,
});
}
} catch (e) {
console.log(e);
new Notice("API Error: Please check credentials")
}
}
componentDidMount() {
this.reloadData()
}
async sendScore(id: string, score: string, message: string) {
try {
let response = await scoreTask(this.username, this.credentials, id, score);
let result = await response.json();
if (result.success === true) {
new Notice(message);
this.reloadData();
} else {
new Notice("Resyncing, please try again");
this.reloadData();
}
} catch (e) {
console.log(e);
new Notice("API Error: Please check credentials")
}
}
async sendReward(id: string, score: string, message: string) {
try {
let response = await costReward(this.username, this.credentials, id, score);
let result = await response.json();
if (result.success === true) {
new Notice(message);
this.reloadData();
} else {
new Notice("Resyncing, please try again");
this.reloadData();
}
} catch (e) {
console.log(e);
new Notice("API Error: Please check credentials")
}
}
handleChangeTodos(event: any) {
this.state.tasks.todos.forEach((element: any) => {
if (element.id == event.target.id) {
if (!element.completed) {
this.sendScore(event.target.id, "up", "Checked!")
} else {
this.sendScore(event.target.id, "down", "Un-Checked!")
}
}
})
}
handleChangeDailys(event: any) {
this.state.tasks.dailys.forEach((element: any) => {
if (element.id == event.target.id) {
if (element.id == event.target.id) {
if (!element.completed) {
this.sendScore(event.target.id, "up", "Checked!")
} else {
this.sendScore(event.target.id, "down", "Un-Checked!")
}
}
}
})
}
handleChangeHabits(event: any) {
const target_id = event.target.id.slice(4)
if (event.target.id.slice(0, 4) == "plus") {
this.state.tasks.habits.forEach((element: any) => {
if (element.id == target_id) {
this.sendScore(target_id, "up", "Plus!")
}
})
}
else {
this.state.tasks.habits.forEach((element: any) => {
if (element.id == target_id) {
this.sendScore(target_id, "down", "Minus :(")
}
})
}
}
handleChangeRewards(event: any) {
const target_id = event.target.id
this.state.tasks.rewards.forEach((element: any) => {
if (element.id == event.target.id) {
if (element.id == target_id) {
this.sendReward(target_id, "down", "Redeemed!")
}
}
})
}
async handleChangeChecklistItem(event: any){
let parentID = event.target.parentNode.parentNode.parentNode.getAttribute("id")
let targetID = event.target.id
console.log(parentID+ " , " + targetID)
try{
let response = await scoreChecklistItem(this.username, this.credentials, targetID, parentID);
let result = await response.json();
if (result.success === true) {
new Notice("Checked!");
this.reloadData();
} else {
new Notice("Resyncing, please try again");
this.reloadData();
}
} catch (e) {
console.log(e);
new Notice("API Error: Please check credentials")
}
}
render() {
let content = this.CheckCron(this.state.user_data.lastCron);
if (this.state.error)
return (<div className="loading">Loading....</div>)
else if (!this.state.isLoaded)
return <div className="loading">Loading....</div>
else {
return (<div className="plugin-root">
{content}
<Statsview className ="stats-view" user_data={this.state.user_data} />
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<Taskview data={this.state.tasks} handleChangeTodos={this.handleChangeTodos} settings = {this.props.plugin.settings} handleChangeDailys={this.handleChangeDailys} handleChangeHabits={this.handleChangeHabits} handleChangeRewards={this.handleChangeRewards} handleChangeChecklistItem={this.handleChangeChecklistItem}/>
</div>
);
}
}
}
export default App

View file

View file

@ -0,0 +1,15 @@
import * as React from 'react';
export default function Index(props: any) {
return(
<div className="stats">
{/* <div id="profile-name">{props.user_data.profile.name}</div> */}
<div className = "substats" id="hp">HP: {numberWithCommas((props.user_data.stats.hp).toFixed(0))}</div>
<div className = "substats" id="lvl">LEVEL: {props.user_data.stats.lvl}</div>
<div className = "substats" id="gold">GOLD: {numberWithCommas(props.user_data.stats.gp.toFixed(2))}</div>
</div>
);
}
function numberWithCommas(x: any) {
return x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
}

View file

@ -0,0 +1,22 @@
import * as React from "react";
import DailySubTasks from "./DailySubTasks";
import renderMarkdown from "../markdownRender";
function DailyItem(props: any) {
var text_html = renderMarkdown(props.daily_text);
var note_html = renderMarkdown(props.daily_notes);
return (
<div className="todo-item" id={props.id}>
<input type="checkbox" className="checkbox" id={props.id} onChange={props.onChange} checked={props.completed} />
<div>
<p><span dangerouslySetInnerHTML={{__html: text_html}}></span></p>
<div className="description" dangerouslySetInnerHTML={{__html: note_html}}></div>
{/* {console.log(props.checklist)} */}
<DailySubTasks key={props.daily_subtasks.id} subtasks={props.daily_subtasks} onChangeChecklistItem={props.onChangeChecklistItem}></DailySubTasks>
</div>
</div>
)
}
export default DailyItem

View file

@ -0,0 +1,22 @@
import * as React from "react";
import renderMarkdown from "../markdownRender";
function DailySubTasks(props: any) {
if (props.subtasks) {
const subtasks = props.subtasks.map((subtask: any) => {
let subtask_text = renderMarkdown(subtask.text);
return (
<div className="subtask" id={subtask.id} key={subtask.id} >
<input id={subtask.id} type="checkbox" className="checkbox-checklist" onChange={props.onChangeChecklistItem} checked={subtask.completed} />
<p id={subtask.id}><span dangerouslySetInnerHTML={{__html: subtask_text}}></span></p>
</div>
)
});
return subtasks
}
else {
return <div></div>
}
}
export default DailySubTasks

View file

@ -0,0 +1,107 @@
import * as React from "react";
import DailyItem from "./DailyItem"
import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
export default function Index(props: any){
if(props.dailys == undefined) {
return <div id="classDisplay">No Dailies Present</div>
}
else {
const notDueDailies = props.dailys.map((daily: any) => {
if (!daily.isDue) {
let daily_notes = '';
let daily_subtasks = '';
if (props.settings.showTaskDescription) {
daily_notes = daily.notes;
}
if (props.settings.showSubTasks) {
daily_subtasks = daily.checklist;
}
return <DailyItem key={daily.id} id={daily.id} daily_text={daily.text}
daily_notes={daily_notes} daily_subtasks={daily_subtasks}
onChange={props.onChange} completed={daily.completed} onChangeChecklistItem={props.onChangeChecklistItem}/>
}
})
const incompleteDailies = props.dailys.map((daily: any) => {
if (!daily.completed&&daily.isDue) {
let daily_notes = '';
let daily_subtasks = '';
if (props.settings.showTaskDescription) {
daily_notes = daily.notes;
}
if (props.settings.showSubTasks) {
daily_subtasks = daily.checklist;
}
return <DailyItem key={daily.id} id={daily.id} daily_text={daily.text}
daily_notes={daily_notes} daily_subtasks={daily_subtasks}
onChange={props.onChange} completed={daily.completed} onChangeChecklistItem={props.onChangeChecklistItem}/>
}
})
const completedDailies = props.dailys.map((daily: any) => {
// if(daily.completed)
// return <DailyItem key={daily.id} id={daily.id} daily_text={daily.text} daily_notes={daily.notes} onChange={props.onChange} completed={daily.completed}/>
if (daily.completed) {
let daily_notes = '';
let daily_subtasks = '';
if (props.settings.showTaskDescription) {
daily_notes = daily.notes;
}
if (props.settings.showSubTasks) {
daily_subtasks = daily.checklist;
}
return <DailyItem key={daily.id} id={daily.id} daily_text={daily.text}
daily_notes={daily_notes} daily_subtasks={daily_subtasks}
onChange={props.onChange} completed={daily.completed} onChangeChecklistItem={props.onChangeChecklistItem}/>
}
})
const allDailies = props.dailys.map((daily: any) => {
// if(daily.completed)
// return <DailyItem key={daily.id} id={daily.id} daily_text={daily.text} daily_notes={daily.notes} onChange={props.onChange} completed={daily.completed}/>
let daily_notes = '';
let daily_subtasks = '';
if (props.settings.showTaskDescription) {
daily_notes = daily.notes;
}
if (props.settings.showSubTasks) {
daily_subtasks = daily.checklist;
}
return <DailyItem key={daily.id} id={daily.id} daily_text={daily.text}
daily_notes={daily_notes} daily_subtasks={daily_subtasks}
onChange={props.onChange} completed={daily.completed} onChangeChecklistItem={props.onChangeChecklistItem}/>
})
const display = <div id="classDisplay">
<Tabs>
<TabList>
<Tab>Active</Tab>
<Tab>Completed</Tab>
<Tab>Not Due</Tab>
<Tab>All</Tab>
</TabList>
<TabPanel>
<ul>{incompleteDailies}</ul>
</TabPanel>
<TabPanel>
<ul>{completedDailies}</ul>
</TabPanel>
<TabPanel>
<ul>{notDueDailies}</ul>
</TabPanel>
<TabPanel>
<ul>{allDailies}</ul>
</TabPanel>
</Tabs>
</div>
return(display);
}
}

View file

@ -0,0 +1,25 @@
import * as React from "react";
import renderMarkdown from "../markdownRender";
function HabitItem(props: any) {
let habit_text = renderMarkdown(props.habit_text);
let habit_notes = renderMarkdown(props.habit_notes);
return (
<div className="habit-item" id={props.id}>
<div className="habit-button-grp">
<button className="habit-button" id={"plus" + props.id} onClick={props.onChange}>
+{props.upCount}
</button>
<button className="habit-button" id={"mins" + props.id} onClick={props.onChange}>
-{props.downCount}
</button>
</div>
<div>
<p className="habit-text"><span dangerouslySetInnerHTML={{__html: habit_text}}></span></p>
<div className="description" dangerouslySetInnerHTML={{__html: habit_notes}}></div>
</div>
</div>
)
}
export default HabitItem

View file

@ -0,0 +1,25 @@
import * as React from "react";
import HabitItem from "./HabitItem"
export default function Index(props: any){
if(props.habits == undefined) {
return (<div id="classDisplay">
No habits present.
</div>)
}
else {
const allHabits = props.habits.map((habit: any) => {
if (props.settings.showTaskDescription) {
return <HabitItem key={habit.id} id={habit.id} habit_text={habit.text} habit_notes={habit.notes} upCount={habit.counterUp} downCount={habit.counterDown} onChange={props.onChange}/>
} else {
return <HabitItem key={habit.id} id={habit.id} habit_text={habit.text} upCount={habit.counterUp} downCount={habit.counterDown} onChange={props.onChange}/>
}
})
const display = <div id="classDisplay">
<ul>{allHabits}</ul>
</div>
return(display);
}
}

View file

@ -0,0 +1,21 @@
import * as React from "react";
import renderMarkdown from "../markdownRender";
function RewardItem(props: any) {
let reward_text = renderMarkdown(props.reward_text);
let reward_notes = renderMarkdown(props.reward_notes);
return (
<div className="habit-item" id={props.id}>
<div className="habit-button-grp">
<button className="habit-button" id={props.id} onClick={props.onChange}>-{props.reward_value}</button>
</div>
<div>
<p className="habit-text"><span dangerouslySetInnerHTML={{__html: reward_text}}></span></p>
<div className="description" dangerouslySetInnerHTML={{__html: reward_notes}}></div>
</div>
</div>
)
}
export default RewardItem

View file

@ -0,0 +1,25 @@
import * as React from "react";
import RewardItem from "./RewardItem"
export default function Index(props: any){
if(props.rewards == undefined) {
return (<div id="classDisplay">
No Rewards present.
</div>)
}
else {
const allRewards = props.rewards.map((reward: any) => {
if (props.settings.showTaskDescription) {
return <RewardItem key={reward.id} id={reward.id} reward_text={reward.text} reward_notes={reward.notes} reward_value={reward.value} onChange={props.onChange}/>
} else {
return <RewardItem key={reward.id} id={reward.id} reward_text={reward.text} reward_value={reward.value} onChange={props.onChange}/>
}
})
const display = <div id="classDisplay">
<ul>{allRewards}</ul>
</div>
return(display);
}
}

View file

@ -0,0 +1,23 @@
import * as React from "react";
import TodoSubTasks from "./TodoSubTasks";
import renderMarkdown from "../markdownRender"
import moment from "moment";
function TodoItem(props: any) {
var dueDate = (props.dueDate==null)?"":("Due Date:"+(moment(props.dueDate).format(props.dueDateFormat)));
var text_html = renderMarkdown(props.todo_text);
var note_html = renderMarkdown(props.todo_notes);
return (
<div className="todo-item" id={props.id}>
<input type="checkbox" className="checkbox" id={props.id} onChange={props.onChange} checked={props.completed}/>
<div>
<p><span dangerouslySetInnerHTML={{__html: text_html}}></span></p>
<div className="description" dangerouslySetInnerHTML={{__html: note_html}}></div>
<TodoSubTasks todoID={props.id} subtasks={props.todo_subtasks} onChange={props.onChangeChecklistItem}></TodoSubTasks>
<div className="due-date">{dueDate}</div>
</div>
</div>
)
}
export default TodoItem

View file

@ -0,0 +1,21 @@
import * as React from "react";
import renderMarkdown from "../markdownRender";
function TodoSubTasks(props: any) {
if (props.subtasks) {
const subtasks = props.subtasks.map((subtask: any) => {
let subtask_text = renderMarkdown(subtask.text);
return (
<div className="subtask" id={subtask.id} key={subtask.id}>
<input type="checkbox" className="checkbox" onChange={props.onChange} checked={subtask.completed} id={subtask.id}/>
<p id={subtask.id}><span dangerouslySetInnerHTML={{__html: subtask_text}}></span></p>
</div>
)
});
return subtasks
}
else {
return <div></div>
}
}
export default TodoSubTasks

View file

@ -0,0 +1,50 @@
import * as React from "react";
import TodoItem from "./TodoItem"
import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
export default function Index(props: any){
if(props.todos == undefined) {
return <div id="classDisplay">No Todos present.</div>
}
else {
const incompleteTodos = props.todos.map((todo: any) => {
if(!todo.completed) {
let todo_notes = '';
let todo_subtasks = '';
if (props.settings.showTaskDescription) {
todo_notes = todo.notes;
}
if (props.settings.showSubTasks) {
todo_subtasks = todo.checklist;
}
return <TodoItem key={todo.id} id={todo.id} todo_text={todo.text}
todo_notes={todo_notes} todo_subtasks={todo_subtasks}
onChange={props.onChange} onChangeChecklistItem={props.onChangeChecklistItem} completed={todo.completed} dueDate={todo.date} dueDateFormat={props.settings.dueDateFormat}/>
}
})
const completedTodos = props.todos.map((todo: any) => {
if(todo.completed)
return <TodoItem key={todo.id} id={todo.id} todo_text={todo.text} todo_notes={todo.notes} onChange={props.onChange} completed={todo.completed}/>
})
const display = <div id="classDisplay">
<Tabs>
<TabList>
<Tab>Active</Tab>
<Tab>Completed</Tab>
</TabList>
<TabPanel>
<ul className="todolist-indent">{incompleteTodos}</ul>
</TabPanel>
<TabPanel>
<ul className="todolist-indent">{completedTodos}</ul>
</TabPanel>
</Tabs>
</div>
return(display);
}
}

View file

@ -0,0 +1,42 @@
import * as React from "react";
import Dailiesview from "./Dailiesview"
import Habitsview from "./Habitsview"
import Todoview from "./Todoview"
import Rewardview from "./Rewardview"
import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
export default function Index(props: any){
const display = <div className="task-view">
<Tabs>
<TabList>
<Tab>
<span className="material-icons md-24">today</span>
</Tab>
<Tab >
<span className="material-icons md-24">add_chart</span>
</Tab>
<Tab>
<span className="material-icons md-24">assignment_turned_in</span>
</Tab>
<Tab>
<span className="material-icons md-24">account_balance</span>
</Tab>
</TabList>
<TabPanel>
<Dailiesview dailys={props.data.dailys} settings = {props.settings} onChange={props.handleChangeDailys} onChangeChecklistItem={props.handleChangeChecklistItem}/>
</TabPanel>
<TabPanel>
<Habitsview habits={props.data.habits} settings = {props.settings} onChange={props.handleChangeHabits}/>
</TabPanel>
<TabPanel>
<Todoview todos={props.data.todos} settings = {props.settings} onChange={props.handleChangeTodos} onChangeChecklistItem={props.handleChangeChecklistItem}/>
</TabPanel>
<TabPanel>
<Rewardview rewards={props.data.rewards} settings = {props.settings} onChange={props.handleChangeRewards} />
</TabPanel>
</Tabs>
</div>
return(display);
}

View file

@ -0,0 +1,21 @@
import MarkdownIt from "markdown-it";
import markdownitEmoji from "markdown-it-emoji"
import twemoji from "twemoji";
export default function renderMarkdown(markdown: string) {
//check if markdown is empty or not a string
if (markdown === "" || markdown === undefined) {
return "";
}
const md = new MarkdownIt({
html: true,
breaks: true,
linkify: true,
typographer: true
});
md.use(markdownitEmoji);
md.renderer.rules.emoji = function(token, idx) {
return twemoji.parse(token[idx].content);
};
return md.render(markdown);
}

70
src/view/habiticaAPI.ts Normal file
View file

@ -0,0 +1,70 @@
// import fetch from "node-fetch";
export async function getStats(username: string, credentials: string){
const url = "https://habitica.com/export/userdata.json"
const response = await fetch(url, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"x-client": "278e719e-5f9c-43b1-9dba-8b73343dc062-HabiticaSync",
"x-api-user": username,
"x-api-key": credentials,
},
})
return (await response)
}
export async function scoreTask(username: string, credentials: string, taskID: string, direction: string) {
const url = "https://habitica.com/api/v3/tasks/".concat(taskID).concat("/score/").concat(direction)
const response = fetch(url, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"x-client": "278e719e-5f9c-43b1-9dba-8b73343dc062-HabiticaSync",
"x-api-user": username,
"x-api-key": credentials,
}
})
return(response)
}
export async function makeCronReq(username: string, credentials: string){
const url = "https://habitica.com/api/v3/cron";
const response = fetch(url, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"x-client": "278e719e-5f9c-43b1-9dba-8b73343dc062-HabiticaSync",
"x-api-user": username,
"x-api-key": credentials,
}
})
return(response)
}
export async function costReward(username: string, credentials: string, taskID: string, direction: string) {
const url = "https://habitica.com/api/v4/tasks/".concat(taskID).concat("/score/").concat(direction)
const response = fetch(url, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"x-client": "278e719e-5f9c-43b1-9dba-8b73343dc062-HabiticaSync",
"x-api-user": username,
"x-api-key": credentials,
}
})
return(response)
}
export async function scoreChecklistItem(username: string, credentials: string, checklistItemID: string, taskID: string) {
const url = "https://habitica.com/api/v3/tasks/".concat(taskID).concat("/checklist/").concat(checklistItemID).concat("/score")
const response = fetch(url, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"x-client": "278e719e-5f9c-43b1-9dba-8b73343dc062-HabiticaSync",
"x-api-user": username,
"x-api-key": credentials,
}
})
return(response)
}

View file

@ -1,35 +1,323 @@
/* Empty. change later */ @import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@1,300&display=swap');
@import url('https://fonts.googleapis.com/css2?family=Open Sans:ital,wght@0,400;1,100&family=Roboto&display=swap');
.book { .add-task-input {
border: 1px solid var(--background-modifier-border); display: flex;
padding: 10px; }
}
.book__title { #profile-name {
font-weight: 600; font-size: x-large;
} font-weight: bold;
padding-bottom: 3%;
}
.stats {
width: 95%;
display: flex;
justify-content: space-between;
font-weight: bold;
padding-bottom: 5px;
}
.book__author { .stats-view {
color: var(--text-muted); border-bottom: 1px;
} }
.modify-todo {
align-self: center;
}
.delete-todo {
align-self: center;
}
.todo-item { .todo-item {
display: flex; display: grid;
justify-content: flex-start; grid-template-columns: 1fr 30fr 1fr 1fr;
align-items: center; justify-content: left;
padding: 0px 0px 0; align-items: flex-start;
padding-top: 5px;
padding-bottom: 5px;
width: 100%;
border-bottom: 1px solid #cecece;
font-family: Roboto, sans-serif;
font-weight: bold;
font-size: 16px;
padding-left: 0;
}
.description {
font-family: Open Sans, sans-serif;
font-weight: 100;
}
.description > ul {
list-style-type: none;
}
p {
margin: 0;
}
.habit-text {
text-align: left !important;
font-weight: bold;
padding-top: 5px;
width: 80%; width: 80%;
border-bottom: 1px solid #cecece; margin-right: 20px;
font-family: Roboto, sans-serif; }
font-weight: normal;
.habit-button {
background-color: var(--interactive-accent);
border: none;
/* color: black; */
text-align: center;
text-decoration: none;
font-size: 16px; font-size: 16px;
color: #ffffff; display: block;
width: 100%;
color: var(--text-on-accent);
}
/* habit-button on hover css selector */
.habit-button:hover {
color: var(--text-on-accent);
background-color: var(--interactive-accent-hover);
}
.habit-item {
display: flex;
grid-template-columns: 60px 1fr;
width: 100%;
gap: 5px;
border-bottom: 1px solid #cecece;
font-family: Open Sans, sans-serif;
font-weight: 100%;
font-size: 16px;
padding-top: 5px;
padding-bottom: 5px;
}
.habit-button-grp {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-content: stretch;
height: 100%;
} }
input[type=checkbox] { input[type=checkbox] {
margin-right: 10px; margin-right: 10px;
margin-top: 5px;
align-self: start;
}
.todo-content {
align-self: center;
}
.submit-button {
/* padding: 5px 5px; */
font-size: 15px;
border: 1px solid #aaa;
/* white-space: nowrap; */
/* margin: 10px; */
margin: 0;
} }
input[type=checkbox]:focus { input[type=checkbox]:focus {
outline: 0; outline: 0;
} }
::-webkit-scrollbar {
display: none; /* Chrome Safari */
}
.plugin-root {
min-width: 260px;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
}
#classDisplay {
height: 100%;
width: 100%;
}
.view-content {
margin-bottom: 0;
}
.substats {
font-size: medium;
}
ul {
margin: 0;
}
/* react-tabs internal file :wink: */
.material-icons {
font-size: 12px !important;
padding-top: 1px;
padding-right: 2px;
}
.material-icons.md-18 { font-size: 18px !important; }
.material-icons.md-24 { font-size: 24px !important; }
.material-icons.md-32 { font-size: 32px !important; }
.material-icons.md-48 { font-size: 48px !important; }
.react-tabs {
-webkit-tap-highlight-color: transparent;
height: 100%;
}
.react-tabs__tab-list {
border-bottom: 1px solid #aaa;
margin: 0 0 5px;
padding: 0;
}
.react-tabs__tab {
display: inline-block;
border: 1px solid transparent;
border-bottom: none;
bottom: -1px;
position: relative;
list-style: none;
padding: 1% 2%;
cursor: pointer;
font-size: medium;
}
.react-tabs__tab--selected {
background: var(--interactive-accent);
color: white;
border-radius: 5px 5px 0 0;
}
.react-tabs__tab--disabled {
color: GrayText;
cursor: default;
}
.react-tabs__tab:focus {
box-shadow: 0 0 5px hsl(208, 99%, 50%);
border-color: hsl(208, 99%, 50%);
outline: none;
}
.react-tabs__tab:focus:after {
content: "";
position: absolute;
height: 5px;
left: -4px;
right: -4px;
bottom: -5px;
background: #fff;
}
.react-tabs__tab-panel {
display: none;
left: 0px;
height: 88%;
/* overflow: scroll; */
}
.task-panel {
overflow: scroll;
height: 100%;
}
.react-tabs__tab-panel--selected {
display: block;
}
ul li:not(.task-list-item)::before {
content: "•";
color: transparent;
display: inline-block;
width: 1em;
margin-left: -1em;
padding: 0;
font-weight: bold;
text-shadow: 0 0 0.5em transparent;
}
.task-operation {
align-self: center;
margin: 0;
padding: 0;
}
.edit-item {
display: flex;
flex-direction: column;
width: 100%;
}
.edit-button {
display: flex;
flex-direction: row;
justify-content: flex-end;
}
.task-submit {
display: grid;
grid-template-columns: 10fr 5fr;
}
.add-task-input {
display: block;
width: 100%;
}
.task-input-box {
margin-right: 10px;
}
button {
margin: auto;
margin-bottom: 5px;
white-space: nowrap;
padding: 5px 5px;
margin-right: 0;
}
.cron {
display: inline-grid;
justify-content: center;
text-align: center;;
margin-top: 5px;
margin-left: 10%;
margin-right: 10%;
margin-bottom: 10px;
border-radius: 10px;
}
#cronMessage {
margin: 20px;
margin-bottom: 10px;
color: var(--text-normal)
}
#cronButton {
margin: auto;
margin-bottom: 5px;
white-space: nowrap;
padding: 5px 5px;
background-color: var(--interactive-accent);
margin-right: auto;
}
.subtask {
display: flex;
flex-direction: row;
font-weight: normal;
font-family: Roboto, sans-serif;
justify-content: flex-start;
}
.emoji {
height: 1em;
}
.description>ul {
list-style-type: disc;
margin-left: 10% !important;
}

View file

@ -3,13 +3,14 @@
"baseUrl": ".", "baseUrl": ".",
"inlineSourceMap": true, "inlineSourceMap": true,
"inlineSources": true, "inlineSources": true,
"module": "esnext", "module": "ESNext",
"target": "es6",
"jsx": "react", "jsx": "react",
"target": "es2017",
"allowJs": true,
"noImplicitAny": true, "noImplicitAny": true,
"moduleResolution": "node", "moduleResolution": "node",
"importHelpers": true, "importHelpers": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"lib": [ "lib": [
"dom", "dom",
"es5", "es5",
@ -19,5 +20,5 @@
}, },
"include": [ "include": [
"**/*.ts" "**/*.ts"
, "view/TodoItem.tsx" ] ]
} }

28
view.js
View file

@ -1,28 +0,0 @@
import { __awaiter } from "tslib";
import { ItemView } from "obsidian";
import * as React from "react";
import * as ReactDOM from "react-dom";
import { ReactView } from "./ReactView";
export const VIEW_TYPE_EXAMPLE = "example-view";
export class ExampleView extends ItemView {
constructor(leaf) {
super(leaf);
}
getViewType() {
return VIEW_TYPE_EXAMPLE;
}
getDisplayText() {
return "Example View";
}
onOpen() {
return __awaiter(this, void 0, void 0, function* () {
ReactDOM.render(React.createElement(ReactView), this.containerEl.children[1]);
});
}
onClose() {
return __awaiter(this, void 0, void 0, function* () {
ReactDOM.unmountComponentAtNode(this.containerEl.children[1]);
});
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmlldy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbInZpZXcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sRUFBRSxRQUFRLEVBQWdCLE1BQU0sVUFBVSxDQUFDO0FBQ2xELE9BQU8sS0FBSyxLQUFLLE1BQU0sT0FBTyxDQUFDO0FBQy9CLE9BQU8sS0FBSyxRQUFRLE1BQU0sV0FBVyxDQUFDO0FBQ3RDLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFHeEMsTUFBTSxDQUFDLE1BQU0saUJBQWlCLEdBQUcsY0FBYyxDQUFBO0FBRS9DLE1BQU0sT0FBTyxXQUFZLFNBQVEsUUFBUTtJQUNyQyxZQUFZLElBQW1CO1FBQzNCLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQTtJQUNmLENBQUM7SUFFRCxXQUFXO1FBQ1AsT0FBTyxpQkFBaUIsQ0FBQTtJQUM1QixDQUFDO0lBRUQsY0FBYztRQUNWLE9BQU8sY0FBYyxDQUFBO0lBQ3pCLENBQUM7SUFFSyxNQUFNOztZQUNSLFFBQVEsQ0FBQyxNQUFNLENBQ1gsS0FBSyxDQUFDLGFBQWEsQ0FBQyxTQUFTLENBQUMsRUFDOUIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQy9CLENBQUE7UUFDTCxDQUFDO0tBQUE7SUFFSyxPQUFPOztZQUNULFFBQVEsQ0FBQyxzQkFBc0IsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2xFLENBQUM7S0FBQTtDQUNKIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSXRlbVZpZXcsV29ya3NwYWNlTGVhZiB9IGZyb20gXCJvYnNpZGlhblwiO1xyXG5pbXBvcnQgKiBhcyBSZWFjdCBmcm9tIFwicmVhY3RcIjtcclxuaW1wb3J0ICogYXMgUmVhY3RET00gZnJvbSBcInJlYWN0LWRvbVwiO1xyXG5pbXBvcnQgeyBSZWFjdFZpZXcgfSBmcm9tIFwiLi9SZWFjdFZpZXdcIjtcclxuXHJcblxyXG5leHBvcnQgY29uc3QgVklFV19UWVBFX0VYQU1QTEUgPSBcImV4YW1wbGUtdmlld1wiXHJcblxyXG5leHBvcnQgY2xhc3MgRXhhbXBsZVZpZXcgZXh0ZW5kcyBJdGVtVmlldyB7XHJcbiAgICBjb25zdHJ1Y3RvcihsZWFmOiBXb3Jrc3BhY2VMZWFmKSB7XHJcbiAgICAgICAgc3VwZXIobGVhZilcclxuICAgIH1cclxuXHJcbiAgICBnZXRWaWV3VHlwZSgpIHtcclxuICAgICAgICByZXR1cm4gVklFV19UWVBFX0VYQU1QTEVcclxuICAgIH1cclxuXHJcbiAgICBnZXREaXNwbGF5VGV4dCgpIHtcclxuICAgICAgICByZXR1cm4gXCJFeGFtcGxlIFZpZXdcIlxyXG4gICAgfVxyXG5cclxuICAgIGFzeW5jIG9uT3BlbigpIHtcclxuICAgICAgICBSZWFjdERPTS5yZW5kZXIoXHJcbiAgICAgICAgICAgIFJlYWN0LmNyZWF0ZUVsZW1lbnQoUmVhY3RWaWV3KSxcclxuICAgICAgICAgICAgdGhpcy5jb250YWluZXJFbC5jaGlsZHJlblsxXVxyXG4gICAgICAgIClcclxuICAgIH1cclxuXHJcbiAgICBhc3luYyBvbkNsb3NlKCl7XHJcbiAgICAgICAgUmVhY3RET00udW5tb3VudENvbXBvbmVudEF0Tm9kZSh0aGlzLmNvbnRhaW5lckVsLmNoaWxkcmVuWzFdKTtcclxuICAgIH1cclxufSJdfQ==

View file

@ -1,35 +0,0 @@
import { ItemView,WorkspaceLeaf } from "obsidian";
import * as React from "react";
import * as ReactDOM from "react-dom";
import ReactView from "./ReactView";
import ExamplePlugin from "main";
export const VIEW_TYPE_EXAMPLE = "example-view"
export class ExampleView extends ItemView {
plugin: ExamplePlugin;
constructor(leaf: WorkspaceLeaf, plugin: ExamplePlugin) {
super(leaf)
this.plugin = plugin
}
getViewType() {
return VIEW_TYPE_EXAMPLE
}
getDisplayText() {
return "Example View"
}
async onOpen() {
ReactDOM.render(
<ReactView userID = {this.plugin.settings.userID} tokenAPI = {this.plugin.settings.apiToken} plugin={this.plugin}/>,
this.containerEl.children[1]
)
}
async onClose(){
ReactDOM.unmountComponentAtNode(this.containerEl.children[1]);
}
}

View file

@ -1,16 +0,0 @@
import * as React from "react";
import { tasks } from "./habiticaAPI";
const username = "ebbdcbab-e0dc-404b-aa50-9824f0678adf";
const credentials = "bed67d72-63cc-479c-88d3-8845569b04f8";
class App extends React.Component {
constructor(props) {
super(props);
const data = tasks(username, credentials);
console.log(data);
}
render() {
return (React.createElement("h2", null, "Hello"));
}
}
export default App;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQXBwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiQXBwLnRzeCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssS0FBSyxNQUFNLE9BQU8sQ0FBQztBQUMvQixPQUFPLEVBQUUsS0FBSyxFQUFFLE1BQU0sZUFBZSxDQUFBO0FBRXJDLE1BQU0sUUFBUSxHQUFHLHNDQUFzQyxDQUFBO0FBQ3ZELE1BQU0sV0FBVyxHQUFHLHNDQUFzQyxDQUFBO0FBRTFELE1BQU0sR0FBSSxTQUFRLEtBQUssQ0FBQyxTQUFTO0lBQzdCLFlBQVksS0FBVTtRQUNsQixLQUFLLENBQUMsS0FBSyxDQUFDLENBQUE7UUFDWixNQUFNLElBQUksR0FBRyxLQUFLLENBQUMsUUFBUSxFQUFFLFdBQVcsQ0FBQyxDQUFBO1FBQ3pDLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDdEIsQ0FBQztJQUNELE1BQU07UUFBRyxPQUFNLENBQ1gsd0NBQWMsQ0FDakIsQ0FBQTtJQUNELENBQUM7Q0FDSjtBQUNELGVBQWUsR0FBRyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgUmVhY3QgZnJvbSBcInJlYWN0XCI7XHJcbmltcG9ydCB7IHRhc2tzIH0gZnJvbSBcIi4vaGFiaXRpY2FBUElcIlxyXG5cclxuY29uc3QgdXNlcm5hbWUgPSBcImViYmRjYmFiLWUwZGMtNDA0Yi1hYTUwLTk4MjRmMDY3OGFkZlwiXHJcbmNvbnN0IGNyZWRlbnRpYWxzID0gXCJiZWQ2N2Q3Mi02M2NjLTQ3OWMtODhkMy04ODQ1NTY5YjA0ZjhcIlxyXG5cclxuY2xhc3MgQXBwIGV4dGVuZHMgUmVhY3QuQ29tcG9uZW50IHtcclxuICAgIGNvbnN0cnVjdG9yKHByb3BzOiBhbnkpIHtcclxuICAgICAgICBzdXBlcihwcm9wcylcclxuICAgICAgICBjb25zdCBkYXRhID0gdGFza3ModXNlcm5hbWUsIGNyZWRlbnRpYWxzKVxyXG4gICAgICAgIGNvbnNvbGUubG9nKGRhdGEpO1xyXG4gICAgfVxyXG4gICAgcmVuZGVyKCl7cmV0dXJuKFxyXG4gICAgICAgIDxoMj5IZWxsbzwvaDI+XHJcbiAgICApXHJcbiAgICB9XHJcbn1cclxuZXhwb3J0IGRlZmF1bHQgQXBwIl19

View file

@ -1,116 +0,0 @@
import * as React from "react";
import { getStats, scoreTask } from "./habiticaAPI"
import Statsview from "./Components/Statsview"
import Taskview from "./Components/Taskview"
let username = ""
let credentials = ""
class App extends React.Component<any,any> {
constructor(props: any) {
super(props)
username = this.props.username
credentials = this.props.apiToken
this.state = {
isLoaded: false,
user_data: {
profile: {
name: "",
},
stats: {
hp: 0,
lvl: 0,
}
},
todos: []
}
this.handleChange = this.handleChange.bind(this)
}
sendNotice(message: string){
this.props.plugin.displayNotice(message)
}
reloadData() {
getStats(username, credentials)
.then(res => res.json())
.then(
result => {
console.log(result)
console.log("data reloaded")
this.setState({
isLoaded: true,
user_data: result,
todos: result.tasks.todos
})
},
(error) => {
this.setState({
isLoaded: true,
error
})
}
)
}
componentDidMount() {
this.reloadData()
}
handleChange(event: any){
this.state.todos.forEach((element: any) => {
if(element.id == event.target.id){
if(!element.completed){
scoreTask(username, credentials, event.target.id, "up")
.then(res => res.json())
.then(
result => {
if(result.success) {
this.sendNotice("Checked!")
console.log(result)
this.reloadData()
} else {
this.sendNotice("Resyncing, please try again")
this.reloadData()
}
},
(error) => {
this.sendNotice("API Error: Please Check crendentials and try again")
console.log(error)
}
)
} else {
scoreTask(username, credentials, event.target.id, "down")
.then(res => res.json())
.then(
result => {
if(result.success){
this.sendNotice("Un-checked!")
console.log(result)
this.reloadData()
} else {
this.sendNotice("Resyncing, please try again")
this.reloadData()
}
},
(error) => {
this.sendNotice("API Error: Please Check crendentials and try again")
console.log(error)
}
)
}
}
})
}
render(){
if(this.state.error)
return(<div className="loading">Loading....</div>)
else if(!this.state.isLoaded)
return <div className="loading">Loading....</div>
else {
return (<div>
<Statsview user_data={this.state.user_data} />
<Taskview todos={this.state.todos} onChange={this.handleChange} />
</div>
);
}
}
}
export default App

View file

@ -1,11 +0,0 @@
import * as React from 'react';
export default function Index(props: any) {
return(
<div id="stats">
<div id="profile-name">{props.user_data.profile.name}</div>
<div id="hp">HP: {props.user_data.stats.hp}</div>
<div id="lvl">LVL: {props.user_data.stats.lvl}</div>
</div>
);
}

View file

@ -1,12 +0,0 @@
import * as React from "react";
function TodoItem(props: any) {
return (
<div className="todo-item" id={props.id}>
<input type="checkbox" className="checkbox" id={props.id} onChange={props.onChange} checked={props.completed}/>
<p>{props.todo_text}</p>
</div>
)
}
export default TodoItem

View file

@ -1,9 +0,0 @@
import * as React from "react";
import TodoItem from "./TodoItem"
export default function Index(props: any){
const listItems = props.todos.map((todo: any) => {
return <TodoItem key={todo.id} id={todo.id} todo_text={todo.text} onChange={props.onChange} completed={todo.completed}/>
})
return(<ul>{listItems}</ul>);
}

View file

@ -1,29 +0,0 @@
// import fetch from "node-fetch";
export async function getStats(username: string, credentials: string){
const url = "https://habitica.com/export/userdata.json"
const response = fetch(url, {
method: 'GET',
headers: {
"Content-Type": "application/json",
"x-client": username.concat("-testAPI"),
"x-api-user": username,
"x-api-key": credentials,
},
})
return (response)
}
export async function scoreTask(username: string, credentials: string, taskID: string, direction: string) {
const url = "https://habitica.com/api/v3/tasks/".concat(taskID).concat("/score/").concat(direction)
const response = fetch(url, {
method: 'POST',
headers: {
"Content-Type": "application/json",
"x-client": username.concat("-testAPI"),
"x-api-user": username,
"x-api-key": credentials,
}
})
return(response)
}