Compare commits
90 commits
checkboxes
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3e378f219 | ||
|
|
c63f039b11 | ||
|
|
3c7d2099bd | ||
|
|
44893508dc | ||
|
|
64d045bbc5 | ||
|
|
fc6506525c | ||
|
|
b80d2f8dc1 | ||
|
|
67463a648d | ||
|
|
b2c93a4747 | ||
|
|
3d09f20d84 | ||
|
|
eadd8d9f8c | ||
|
|
e7b4572d2e | ||
|
|
350ab38135 | ||
|
|
615f3e8a78 | ||
|
|
c1851c2fc6 | ||
|
|
82780ba589 | ||
|
|
e4d3c78a9f | ||
|
|
932562ab2e | ||
|
|
22616cbed9 | ||
|
|
88fdde519d | ||
|
|
916236db5d | ||
|
|
e31d103dd7 | ||
|
|
0ac05ef6d3 | ||
|
|
6865d4f080 | ||
|
|
4878ed8aa8 | ||
|
|
dfc4e55cb5 | ||
|
|
6384c98181 | ||
|
|
54e6000eb8 | ||
|
|
d10a2ca747 | ||
|
|
8deee0ecc6 | ||
|
|
e45182547f | ||
|
|
9b15bb1236 | ||
|
|
bc97bcfc3f | ||
|
|
ffa79454a0 | ||
|
|
d29bfd9995 | ||
|
|
86dc33e57c | ||
|
|
288f0a0b09 | ||
|
|
4361341f56 | ||
|
|
9d6c50ef88 | ||
|
|
3d61b94932 | ||
|
|
f1915876ba | ||
|
|
25a1505fd0 | ||
|
|
412f9ed431 | ||
|
|
e0f400b761 | ||
|
|
b3282a360c | ||
|
|
af3b2fe53b | ||
|
|
04f207db5d | ||
|
|
b7b1c3848f | ||
|
|
af6f93d997 | ||
|
|
e8b010a3c5 | ||
|
|
faa33a5655 | ||
|
|
6a7651c04a | ||
|
|
f996379243 | ||
|
|
86f4fd0d74 | ||
|
|
f1a130077a | ||
|
|
2aa6c949fb | ||
|
|
68eff40b6b | ||
|
|
a8a4e32f7b | ||
|
|
977cacb8f6 | ||
|
|
2e0ec9d021 | ||
|
|
bc77246e44 | ||
|
|
4a9f52df3a | ||
|
|
dc05ce086e | ||
|
|
f3e8047322 | ||
|
|
24f22d28ca | ||
|
|
103620f3ef | ||
|
|
a3cc611dbc | ||
|
|
75b18f2f2a | ||
|
|
25312a6d93 | ||
|
|
0280d5bd3a | ||
|
|
0cd5fde19c | ||
|
|
30dd6590ec | ||
|
|
a4ebd7aab3 | ||
|
|
9fe8405bab | ||
|
|
048a6725ce | ||
|
|
a8e28b2ab7 | ||
|
|
bb4e7cdb92 | ||
|
|
470157b672 | ||
|
|
32f0e6bc98 | ||
|
|
6b715682f9 | ||
|
|
cd3c81cf5c | ||
|
|
08664f2aaf | ||
|
|
a2d3067b75 | ||
|
|
f195546524 | ||
|
|
e3f1d13ee0 | ||
|
|
9c58769a99 | ||
|
|
964829cbb7 | ||
|
|
a84b981019 | ||
|
|
30dfdeaee7 | ||
|
|
45a760e172 |
42 changed files with 1371 additions and 568 deletions
9
.gitignore
vendored
9
.gitignore
vendored
|
|
@ -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
21
LICENSE
Normal 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.
|
||||||
83
README.md
83
README.md
|
|
@ -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.
|
- [](https://gyazo.com/1966b17f954dcffa954922570e860a06)
|
||||||
- Registers a global interval which logs 'setInterval' to the console.
|
- Habits
|
||||||
|
- [](https://gyazo.com/280494e620fc91548838d5b29a62652b)
|
||||||
|
- Rewards
|
||||||
|
#### Interactivity
|
||||||
|
- Check off tasks/dailies in the view
|
||||||
|
- Can uncheck completed habits/todos
|
||||||
|
- [](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
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import App from "./view/App";
|
|
||||||
export const ReactView = () => {
|
|
||||||
return React.createElement(App, null);
|
|
||||||
};
|
|
||||||
|
|
@ -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
57
main.ts
|
|
@ -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]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
34
old_view.ts
34
old_view.ts
|
|
@ -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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
59
package.json
59
package.json
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
23
settings.js
23
settings.js
|
|
@ -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=
|
|
||||||
42
settings.ts
42
settings.ts
|
|
@ -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
71
src/main.ts
Normal 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
80
src/settings.ts
Normal 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
37
src/view.tsx
Normal 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
227
src/view/App.tsx
Normal 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
|
||||||
0
src/view/Components/Statsview/index.css
Normal file
0
src/view/Components/Statsview/index.css
Normal file
15
src/view/Components/Statsview/index.tsx
Normal file
15
src/view/Components/Statsview/index.tsx
Normal 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, ",");
|
||||||
|
}
|
||||||
22
src/view/Components/Taskview/Dailiesview/DailyItem.tsx
Normal file
22
src/view/Components/Taskview/Dailiesview/DailyItem.tsx
Normal 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
|
||||||
22
src/view/Components/Taskview/Dailiesview/DailySubTasks.tsx
Normal file
22
src/view/Components/Taskview/Dailiesview/DailySubTasks.tsx
Normal 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
|
||||||
107
src/view/Components/Taskview/Dailiesview/index.tsx
Normal file
107
src/view/Components/Taskview/Dailiesview/index.tsx
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
25
src/view/Components/Taskview/Habitsview/HabitItem.tsx
Normal file
25
src/view/Components/Taskview/Habitsview/HabitItem.tsx
Normal 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
|
||||||
25
src/view/Components/Taskview/Habitsview/index.tsx
Normal file
25
src/view/Components/Taskview/Habitsview/index.tsx
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
21
src/view/Components/Taskview/Rewardview/RewardItem.tsx
Normal file
21
src/view/Components/Taskview/Rewardview/RewardItem.tsx
Normal 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
|
||||||
25
src/view/Components/Taskview/Rewardview/index.tsx
Normal file
25
src/view/Components/Taskview/Rewardview/index.tsx
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
23
src/view/Components/Taskview/Todoview/TodoItem.tsx
Normal file
23
src/view/Components/Taskview/Todoview/TodoItem.tsx
Normal 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
|
||||||
21
src/view/Components/Taskview/Todoview/TodoSubTasks.tsx
Normal file
21
src/view/Components/Taskview/Todoview/TodoSubTasks.tsx
Normal 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
|
||||||
50
src/view/Components/Taskview/Todoview/index.tsx
Normal file
50
src/view/Components/Taskview/Todoview/index.tsx
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/view/Components/Taskview/index.tsx
Normal file
42
src/view/Components/Taskview/index.tsx
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
21
src/view/Components/Taskview/markdownRender.ts
Normal file
21
src/view/Components/Taskview/markdownRender.ts
Normal 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
70
src/view/habiticaAPI.ts
Normal 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)
|
||||||
|
}
|
||||||
318
styles.css
318
styles.css
|
|
@ -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;
|
||||||
width: 80%;
|
padding-top: 5px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
width: 100%;
|
||||||
border-bottom: 1px solid #cecece;
|
border-bottom: 1px solid #cecece;
|
||||||
font-family: Roboto, sans-serif;
|
font-family: Roboto, sans-serif;
|
||||||
font-weight: normal;
|
font-weight: bold;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #ffffff;
|
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%;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.habit-button {
|
||||||
|
background-color: var(--interactive-accent);
|
||||||
|
border: none;
|
||||||
|
/* color: black; */
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 16px;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
28
view.js
|
|
@ -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==
|
|
||||||
35
view.tsx
35
view.tsx
|
|
@ -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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
16
view/App.js
16
view/App.js
|
|
@ -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
|
|
||||||
116
view/App.tsx
116
view/App.tsx
|
|
@ -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
|
|
||||||
|
|
@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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>);
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue