Added source code files which were left out
This commit is contained in:
parent
32f0e6bc98
commit
470157b672
12 changed files with 414 additions and 0 deletions
8
src/ReactView.tsx
Normal file
8
src/ReactView.tsx
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
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}/>
|
||||||
|
)
|
||||||
|
}
|
||||||
65
src/main.ts
Normal file
65
src/main.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
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]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
42
src/settings.ts
Normal file
42
src/settings.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
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();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/view.tsx
Normal file
38
src/view.tsx
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
getIcon(): string {
|
||||||
|
return "popup-open"
|
||||||
|
}
|
||||||
|
|
||||||
|
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
src/view/App.tsx
Normal file
122
src/view/App.tsx
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
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
|
||||||
0
src/view/Components/Statsview/index.css
Normal file
0
src/view/Components/Statsview/index.css
Normal file
11
src/view/Components/Statsview/index.tsx
Normal file
11
src/view/Components/Statsview/index.tsx
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
12
src/view/Components/Taskview/TodoItem.tsx
Normal file
12
src/view/Components/Taskview/TodoItem.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
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
|
||||||
30
src/view/Components/Taskview/index.tsx
Normal file
30
src/view/Components/Taskview/index.tsx
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
56
src/view/Components/Taskview/react-tabs.css
Normal file
56
src/view/Components/Taskview/react-tabs.css
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
.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
src/view/Components/Taskview/tabs.ts
Normal file
1
src/view/Components/Taskview/tabs.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
import * as React from "react"
|
||||||
29
src/view/habiticaAPI.ts
Normal file
29
src/view/habiticaAPI.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
// 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