Organized all files. Code goes in src/ Added icon
This commit is contained in:
parent
6b715682f9
commit
32f0e6bc98
16 changed files with 117 additions and 482 deletions
|
|
@ -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}/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
174
main.js
174
main.js
File diff suppressed because one or more lines are too long
65
main.ts
65
main.ts
|
|
@ -1,65 +0,0 @@
|
||||||
import { Notice, Plugin } from "obsidian";
|
|
||||||
import { HabiticaSyncSettingsTab } from "./settings";
|
|
||||||
import { HabiticaSyncView, VIEW_TYPE} from "./view"
|
|
||||||
|
|
||||||
interface HabiticaSyncSettings {
|
|
||||||
userID: string
|
|
||||||
apiToken: string
|
|
||||||
}
|
|
||||||
const DEFAULT_SETTINGS: Partial<HabiticaSyncSettings> = {
|
|
||||||
userID: "",
|
|
||||||
apiToken: ""
|
|
||||||
}
|
|
||||||
export default class HabiticaSync extends Plugin {
|
|
||||||
settings: HabiticaSyncSettings;
|
|
||||||
view: HabiticaSyncView;
|
|
||||||
|
|
||||||
displayNotice(message: string){
|
|
||||||
new Notice(message)
|
|
||||||
}
|
|
||||||
async onload() {
|
|
||||||
await this.loadSettings();
|
|
||||||
this.addSettingTab(new HabiticaSyncSettingsTab(this.app, this));
|
|
||||||
this.registerView(
|
|
||||||
VIEW_TYPE,
|
|
||||||
(leaf) => (this.view = new HabiticaSyncView(leaf, this))
|
|
||||||
);
|
|
||||||
this.addRibbonIcon("popup-open", "Open Habitica Pane", () => { //activate view
|
|
||||||
this.activateView();
|
|
||||||
});
|
|
||||||
this.addCommand({
|
|
||||||
id: "habitica-view-open",
|
|
||||||
name: "Habitica: 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]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -6,5 +6,6 @@
|
||||||
"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"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,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',
|
||||||
|
|
|
||||||
42
settings.ts
42
settings.ts
|
|
@ -1,42 +0,0 @@
|
||||||
import HabiticaSync from "main";
|
|
||||||
import { App, PluginSettingTab, Setting } from "obsidian";
|
|
||||||
|
|
||||||
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();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,10 +3,9 @@
|
||||||
"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,
|
||||||
|
|
@ -19,5 +18,5 @@
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"**/*.ts"
|
"**/*.ts"
|
||||||
, "view/TodoItem.tsx" ]
|
]
|
||||||
}
|
}
|
||||||
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 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"
|
|
||||||
}
|
|
||||||
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
122
view/App.tsx
122
view/App.tsx
|
|
@ -1,122 +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 => {
|
|
||||||
if(result.success === false){
|
|
||||||
this.sendNotice("Login Failed, Please check credentials and try again!")
|
|
||||||
console.log(result)
|
|
||||||
} else {
|
|
||||||
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 className="plugin-root">
|
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
|
|
||||||
<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 className="stats">
|
|
||||||
<div id="profile-name">{props.user_data.profile.name}</div>
|
|
||||||
<div className = "substats" id="hp"><i className="material-icons">favorite</i>HP: {(props.user_data.stats.hp).toPrecision(3)}</div>
|
|
||||||
<div className = "substats" id="lvl"><i className="material-icons">star</i>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,30 +0,0 @@
|
||||||
import * as React from "react";
|
|
||||||
import TodoItem from "./TodoItem"
|
|
||||||
import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
|
|
||||||
|
|
||||||
export default function Index(props: any){
|
|
||||||
const incompleteTodos = props.todos.map((todo: any) => {
|
|
||||||
if(!todo.completed)
|
|
||||||
return <TodoItem key={todo.id} id={todo.id} todo_text={todo.text} onChange={props.onChange} completed={todo.completed}/>
|
|
||||||
})
|
|
||||||
const completedTodos = props.todos.map((todo: any) => {
|
|
||||||
if(todo.completed)
|
|
||||||
return <TodoItem key={todo.id} id={todo.id} todo_text={todo.text} onChange={props.onChange} completed={todo.completed}/>
|
|
||||||
})
|
|
||||||
const display = <div id="classDisplay">
|
|
||||||
<Tabs>
|
|
||||||
<TabList>
|
|
||||||
<Tab>Active Todos</Tab>
|
|
||||||
<Tab>Completed Todos</Tab>
|
|
||||||
</TabList>
|
|
||||||
<TabPanel>
|
|
||||||
<ul>{incompleteTodos}</ul>
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel>
|
|
||||||
<ul>{completedTodos}</ul>
|
|
||||||
</TabPanel>
|
|
||||||
</Tabs>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
return(display);
|
|
||||||
}
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
.react-tabs {
|
|
||||||
-webkit-tap-highlight-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-tabs__tab-list {
|
|
||||||
border-bottom: 1px solid #aaa;
|
|
||||||
margin: 0 0 10px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-tabs__tab {
|
|
||||||
display: inline-block;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-bottom: none;
|
|
||||||
bottom: -1px;
|
|
||||||
position: relative;
|
|
||||||
list-style: none;
|
|
||||||
padding: 6px 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-tabs__tab--selected {
|
|
||||||
background: #fff;
|
|
||||||
border-color: #aaa;
|
|
||||||
color: black;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-tabs__tab-panel--selected {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
import * as React from "react"
|
|
||||||
|
|
@ -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