Merge pull request #2 from dhruvik7/update/tracker

Update/tracker
This commit is contained in:
dhruvik7
2021-02-25 08:44:38 -05:00
committed by GitHub
11 changed files with 1716 additions and 21 deletions

View File

@@ -1,7 +1,8 @@
## Obsidian Daily Stats
This is a daily word count plugin for Obsidian (https://obsidian.md). You can see today's word count in the bottom right corner of your screen, and also see the historical logs. Currently the historical logs are available in the data.json, they will be integrated visually into the application in the next update.
This plugin was inspired by liamcain's [Calender](https://github.com/liamcain/obsidian-calendar-plugin) and lukeleppan's [Better Word Count](https://github.com/lukeleppan/better-word-count).
![Example](./images/example.png)
## Obsidian Daily Stats
This is a daily word count plugin for Obsidian (https://obsidian.md). You can see today's word count in the bottom right corner of your screen, and also see the historical logs in a right panel view akin to Github's contribution graph.
This plugin was inspired by liamcain's [Calender](https://github.com/liamcain/obsidian-calendar-plugin) and lukeleppan's [Better Word Count](https://github.com/lukeleppan/better-word-count).
![Example](./images/example-graph.png)

BIN
images/example-graph.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

View File

@@ -15,9 +15,17 @@
"@rollup/plugin-node-resolve": "^9.0.0",
"@rollup/plugin-typescript": "^6.0.0",
"@types/node": "^14.14.2",
"@types/react-calendar-heatmap": "^1.6.2",
"@types/react-dom": "^17.0.1",
"obsidian": "https://github.com/obsidianmd/obsidian-api/tarball/master",
"postcss": "^8.2.6",
"react": "^17.0.1",
"react-calendar-heatmap": "^1.8.1",
"react-dom": "^17.0.1",
"rollup": "^2.32.1",
"rollup-plugin-postcss": "^4.0.0",
"tslib": "^2.0.3",
"typescript": "^4.0.3"
}
}
},
"dependencies": {}
}

View File

@@ -1,19 +1,23 @@
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 postcss from 'rollup-plugin-postcss'
export default {
input: 'main.ts',
input: 'src/main.ts',
output: {
dir: '.',
sourcemap: 'inline',
format: 'cjs',
exports: 'default'
},
external: ['obsidian'],
external: ['obsidian', 'crypto'],
plugins: [
typescript(),
nodeResolve({browser: true}),
nodeResolve({ browser: true, preferBuiltins: true }),
commonjs(),
postcss({
plugins: []
})
]
};

54
src/calendar.tsx Normal file
View File

@@ -0,0 +1,54 @@
import * as React from "react";
import ReactCalendarHeatmap from "react-calendar-heatmap";
import { MAX_COLORS, COLOR_FREQ } from "./constants";
interface HeatmapProps {
data: any[];
}
class Heatmap extends React.Component<HeatmapProps> {
render() {
const element = document.getElementById("color-elem");
if (element) {
const base = getComputedStyle(element).getPropertyValue("color");
for (let elem of Array.from(document.getElementsByClassName("color1") as HTMLCollectionOf<HTMLElement>)) {
elem.style.fill = base;
elem.style.opacity = "0.44";
}
for (let elem of Array.from(document.getElementsByClassName("color2") as HTMLCollectionOf<HTMLElement>)) {
elem.style.fill = base;
elem.style.opacity = "0.6";
}
for (let elem of Array.from(document.getElementsByClassName("color3") as HTMLCollectionOf<HTMLElement>)) {
elem.style.fill = base;
elem.style.opacity = "0.76";
}
for (let elem of Array.from(document.getElementsByClassName("color4") as HTMLCollectionOf<HTMLElement>)) {
elem.style.fill = base;
elem.style.opacity = "0.92";
}
}
return <div style={{ padding: "10px 0px 0px 10px", maxWidth: "300px", marginLeft: "auto", marginRight: "auto", fontSize: "4px !important" }} id="calendar-container">
<ReactCalendarHeatmap
startDate={new Date(new Date().setFullYear(new Date().getFullYear() - 1))}
endDate={new Date()}
values={this.props.data}
horizontal={false}
showMonthLabels={true}
showWeekdayLabels={true}
weekdayLabels={["S", "M", "T", "W", "T", "F", "S"]}
classForValue={(value) => {
if (!value || value.count == 0) {
return 'color-empty';
}
return `color${Math.min(MAX_COLORS, Math.floor(Math.log(value.count) / Math.log(COLOR_FREQ)))}`;
}}
titleForValue={(value) => !value || value.date === null ? '' : value.count + ' words on ' + new Date(value.date).toLocaleDateString()}
/>
<div id="color-elem" />
</div>
}
}
export default Heatmap;

3
src/constants.ts Normal file
View File

@@ -0,0 +1,3 @@
export const VIEW_TYPE_STATS_TRACKER = "stats-tracker";
export const MAX_COLORS = 5;
export const COLOR_FREQ = 6;

View File

@@ -1,4 +1,6 @@
import { TFile, Plugin, MarkdownView, debounce, Debouncer } from 'obsidian';
import { TFile, Plugin, MarkdownView, debounce, Debouncer, WorkspaceLeaf, addIcon } from 'obsidian';
import { VIEW_TYPE_STATS_TRACKER } from './constants';
import StatsTrackerView from './view';
interface WordCount {
initial: number;
@@ -22,6 +24,8 @@ export default class DailyStats extends Plugin {
today: string;
debouncedUpdate: Debouncer<[contents: string, filepath: string]>;
private view: StatsTrackerView;
async onload() {
await this.loadSettings();
@@ -37,6 +41,24 @@ export default class DailyStats extends Plugin {
this.updateWordCount(contents, filepath);
}, 400, false);
this.registerView(
VIEW_TYPE_STATS_TRACKER,
(leaf: WorkspaceLeaf) => (this.view = new StatsTrackerView(leaf, this.settings.dayCounts))
);
this.addCommand({
id: "show-daily-stats-tracker-view",
name: "Open tracker view",
checkCallback: (checking: boolean) => {
if (checking) {
return (
this.app.workspace.getLeavesOfType(VIEW_TYPE_STATS_TRACKER).length === 0
);
}
this.initLeaf();
},
});
this.registerEvent(
this.app.workspace.on("quit", this.onunload.bind(this))
);
@@ -51,10 +73,28 @@ export default class DailyStats extends Plugin {
}, 200)
);
addIcon("bar-graph", `<path fill="currentColor" stroke="currentColor" d="M122.88,105.98H9.59v-0.02c-2.65,0-5.05-1.08-6.78-2.81c-1.72-1.72-2.79-4.11-2.79-6.75H0V0h12.26v93.73h110.62V105.98 L122.88,105.98z M83.37,45.6h19.55c1.04,0,1.89,0.85,1.89,1.89v38.46c0,1.04-0.85,1.89-1.89,1.89H83.37 c-1.04,0-1.89-0.85-1.89-1.89V47.5C81.48,46.46,82.33,45.6,83.37,45.6L83.37,45.6z M25.36,22.07h19.55c1.04,0,1.89,0.85,1.89,1.89 v62c0,1.04-0.85,1.89-1.89,1.89H25.36c-1.04,0-1.89-0.85-1.89-1.89v-62C23.47,22.92,24.32,22.07,25.36,22.07L25.36,22.07 L25.36,22.07z M54.37,8.83h19.54c1.04,0,1.89,0.85,1.89,1.89v75.24c0,1.04-0.85,1.89-1.89,1.89H54.37c-1.04,0-1.89-0.85-1.89-1.89 V10.72C52.48,9.68,53.33,8.83,54.37,8.83L54.37,8.83z"/>`);
this.registerInterval(window.setInterval(() => {
this.updateDate();
this.saveSettings();
}, 1000));
if (this.app.workspace.layoutReady) {
this.initLeaf();
} else {
this.registerEvent(
this.app.workspace.on("layout-ready", this.initLeaf.bind(this))
);
}
}
initLeaf(): void {
if (this.app.workspace.getLeavesOfType(VIEW_TYPE_STATS_TRACKER).length) {
return;
}
this.app.workspace.getRightLeaf(false).setViewState({
type: VIEW_TYPE_STATS_TRACKER,
});
}
async onunload() {
@@ -118,6 +158,8 @@ export default class DailyStats extends Plugin {
}
async saveSettings() {
await this.saveData(this.settings);
if (Object.keys(this.settings.dayCounts).length > 0) { //ensuring we never reset the data by accident
await this.saveData(this.settings);
}
}
}

45
src/view.ts Normal file
View File

@@ -0,0 +1,45 @@
import { ItemView, WorkspaceLeaf } from "obsidian";
import { VIEW_TYPE_STATS_TRACKER } from "./constants";
import * as ReactDOM from "react-dom";
import * as React from "react";
import Calendar from "./calendar";
import '../styles.css';
export default class StatsTrackerView extends ItemView {
private dayCounts: Record<string, number>;
constructor(leaf: WorkspaceLeaf, dayCounts: Record<string, number>) {
super(leaf);
this.dayCounts = dayCounts;
this.registerInterval(
window.setInterval(() => {
ReactDOM.render(React.createElement(Calendar, {
data: Object.keys(this.dayCounts).map(day => {
return { "date": new Date(new Date(day).setMonth(new Date(day).getMonth() + 1)), "count": this.dayCounts[day] }
}),
}), (this as any).contentEl);
}, 1000)
);
}
getDisplayText() {
return "Daily Stats";
}
getIcon() {
return "bar-graph";
}
getViewType() {
return VIEW_TYPE_STATS_TRACKER;
}
async onOpen() {
ReactDOM.render(React.createElement(Calendar, {
data: Object.keys(this.dayCounts).map(day => {
return { "date": new Date(new Date(day).setMonth(new Date(day).getMonth() + 1)), "count": this.dayCounts[day] }
}),
}), (this as any).contentEl);
}
}

View File

@@ -0,0 +1,24 @@
text {
fill: #aaa;
}
rect:hover {
stroke: #555;
stroke-width: 0.5px;
}
/*
* Default color scale
*/
.color-empty {
fill: var(--background-modifier-border);
}
.color-filled {
fill: var(--text-accent);
}
#color-elem {
color: var(--text-accent);
}

View File

@@ -13,10 +13,13 @@
"dom",
"es5",
"scripthost",
"es2015"
]
"es2015",
"DOM.Iterable"
],
"jsx": "react",
},
"include": [
"**/*.ts"
"**/*.ts",
"**/*.tsx",
]
}
}

1515
yarn.lock

File diff suppressed because it is too large Load Diff