Skip to content

Commit

Permalink
First pass at PoC
Browse files Browse the repository at this point in the history
  • Loading branch information
lynchjames committed Nov 3, 2020
1 parent d83153a commit 4d16c50
Show file tree
Hide file tree
Showing 17 changed files with 747 additions and 0 deletions.
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
*.DS_Store
# npm
node_modules
package-lock.json

# build
main.js
*.js.map
*.zip
dist/
coverage/
.nyc_output/
test-vault/
55 changes: 55 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Contributing

When contributing to this repository, please first discuss the change you wish to make via an [issue](https://github.com/lynchjames/obsidian-day-planner/issues), or with the maintainer on the [Obsidian Forum](https://forum.obsidian.md/u/j_l/summary) before making a change.

I have included a code of conduct, please follow it in all your interactions with the development of this plugin.

## Pull Request Process

1. Ensure any install or build dependencies are removed before the end of the layer when doing a
build.
2. Where appropriate, update the README.md with details of changes to the plugin, this includes additions and changes to configuration
settings, plugin commands, useful file locations and additional installation instructions.
3. If you can, please include tests in your Pull Request, particularly if you are making significant changes or additions to the behaviour of the plugin.
4. A CI Test Github Action workflow will run when a new Pull Request is made. The Pull Request cannot be completed until that workflow is passing with the plugin successfully building and all tests passing.
5. The repository maintainer will be responsible for increasing the version numbers in files and the README.md to the new version that this Pull Request would represent once it has been completed and merged. The versioning scheme used is [SemVer](http://semver.org/).

## Code of Conduct

### Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

### Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

### Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

### Attribution

This Code of Conduct is based on and adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Day Planner

<!-- ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/lynchjames/obsidian-day-planner/Release%20Build?logo=github&style=for-the-badge) ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/lynchjames/obsidian-day-planner?style=for-the-badge&sort=semver) -->


This repository contains a plugin for [Obsidian](https://obsidian.md/) for day planning and managing pomodoro timers in Markdown.


## Usage

## Commands


## Configuration

### Timezone
<<<Details of timezone plugin TBC>>>

## Compatibility

Custom plugins are only available for Obsidian v0.9.7+.

The current API of this repo targets Obsidian **v0.9.10**.

## Installing

As of version [0.9.7 of Obsidian](https://forum.obsidian.md/t/obsidian-release-v0-9-7-insider-build/7628), this plugin is available to be installed directly from within the app. The plugin can be found in the Community Plugins directory which can be accessed from the Settings pane under Third Party Plugins.

## Manual installation

1. Download the [latest release](https://github.com/lynchjames/obsidian-day-planner/releases/latest)
1. Extract the obsidian-day-planner folder from the zip to your vault's plugins folder: `<vault>/.obsidian/plugins/`
Note: On some machines the `.obsidian` folder may be hidden. On MacOS you should be able to press `Command+Shift+Dot` to show the folder in Finder.
1. Reload Obsidian
1. If prompted about Safe Mode, you can disable safe mode and enable the plugin.

## Credits

TBC

## For developers
Pull requests are both weclcome and appreciated. 😀

If you would like to contribute to the development of this plugin, please follow the guidelines provided in [CONTRIBUTING.md](CONTRIBUTING.md).

## Donating

This plugin is provided free of charge. If you would like to donate something to me, you can via [PayPal](https://paypal.me/lynchjames2020). Thank you!
9 changes: 9 additions & 0 deletions manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"id": "obsidian-day-planner",
"name": "Day Planner",
"version": "0.0.1",
"description": "A plugin to help you plan your day and setup pomodoro timers",
"isDesktopOnly": false,
"js": "main.js",
"css": "style.css"
}
39 changes: 39 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "obsidian-day-planner",
"version": "0.1.0",
"description": "A plugin to help you plan your day and setup pomodoro timers",
"main": "main.js",
"scripts": {
"dev": "rollup --config rollup.config.js -w",
"build": "rollup --config rollup.config.js",
"test": "cross-env TS_NODE_COMPILER_OPTIONS='{ \"module\": \"commonjs\" }' mocha -r ts-node/register -r ignore-styles -r jsdom-global/register tests/**/*.test.ts",
"coverage": "nyc -r lcov -e .ts -x \"*.test.ts\" npm run test"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"@rollup/plugin-commonjs": "^15.1.0",
"@rollup/plugin-node-resolve": "^9.0.0",
"@rollup/plugin-typescript": "^6.0.0",
"@types/chai": "^4.2.14",
"@types/mocha": "^8.0.3",
"@types/node": "^14.14.2",
"chai": "^4.2.0",
"cross-env": "^7.0.2",
"ignore-styles": "^5.0.1",
"jsdom": "^16.4.0",
"jsdom-global": "^3.0.2",
"mocha": "^8.2.0",
"nyc": "^15.1.0",
"obsidian": "https://github.com/obsidianmd/obsidian-api/tarball/master",
"rollup": "^2.32.1",
"rollup-plugin-copy": "^3.3.0",
"ts-node": "^9.0.0",
"tslib": "^2.0.3",
"typescript": "^4.0.5"
},
"dependencies": {
"moment": "^2.29.1"
}
}
27 changes: 27 additions & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import typescript from '@rollup/plugin-typescript';
import {nodeResolve} from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import copy from 'rollup-plugin-copy';
const TEST_VAULT = 'test-vault/.obsidian/plugins/day-planner';

export default {
input: 'src/main.ts',
output: {
dir: 'dist/',
sourcemap: 'inline',
format: 'cjs',
exports: 'default'
},
external: ['obsidian'],
plugins: [
typescript(),
nodeResolve({browser: true}),
commonjs(),
copy({
targets: [
{ src: 'dist/main.js', dest: TEST_VAULT },
{ src: ['manifest.json', 'styles.css'], dest: TEST_VAULT }
], flatten: true
})
]
};
9 changes: 9 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const HEADING_REGEX = /^[#\s-]*/;
export const HEADING_FORMAT = '#';

export const DEFAULT_DATE_FORMAT = 'YYYYMMDDHHmm';
export const DATE_REGEX = /(?<target>{{date:?(?<date>[^}]*)}})/g;

export const DAY_PLANNER_FILENAME = 'Day Planner.md';
export const PLAN_PARSER_REGEX =
/^-?[\s]*\[?(?<completion>[x ]*)\]?(\d.)?\s*?(?<hours>\d{2}):(?<minutes>\d{2})(?<text>.*)$/gmi;
106 changes: 106 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {
MarkdownView,
Plugin,
Vault,
DataAdapter,
TFile,
EventRef
} from 'obsidian';
import MomentDateRegex from './moment-date-regex';
import moment from 'moment';
import { DayPlannerSettingsTab } from './settings-tab';
import { DayPlannerSettings } from './settings';
import { DAY_PLANNER_FILENAME } from './constants';
import Parser, { PlanItem, PlanSummaryData } from './parser';

export default class DayPlanner extends Plugin {
settings: DayPlannerSettings;
momentDateRegex: MomentDateRegex;
parser: Parser;
vault: Vault;
statusBar: HTMLElement
statusBarAdded: boolean;
statusBarText: HTMLSpanElement;
statusBarProgress: HTMLDivElement;
statusBarCurrentProgress: HTMLDivElement;



onInit() {}

async onload() {
console.log("Loading Day Planner plugin");
this.settings = (await this.loadData()) || new DayPlannerSettings();
this.vault = this.app.vault;
this.statusBar = this.addStatusBarItem()
this.momentDateRegex = new MomentDateRegex();
this.parser = new Parser(this.app.vault);

this.linkToDayPlanBlock();
// this.registerEvent(this.app.workspace.on('file-open', this.parseDayPlanner));
this.registerEvent(this.app.on("codemirror", this.codeMirror));

this.addSettingTab(new DayPlannerSettingsTab(this.app, this));
this.parseDayPlanner();

this.registerInterval(
window.setInterval(() => this.refreshStatusBar(), 2500)
);
}

async refreshStatusBar() {
const planSummary = await this.parseDayPlanner();
const current = planSummary.current();
this.updateProgress(current.current, current.next);
}

updateProgress(current: PlanItem, next: PlanItem) {
if(!current || !next){
return;
}
const nowMoment = moment(new Date());
const currentMoment = moment(current.time);
const nextMoment = moment(next.time);
const diff = moment.duration(nextMoment.diff(currentMoment));
const fromStart = moment.duration(nowMoment.diff(currentMoment));
const fromNext = moment.duration(nextMoment.diff(nowMoment));
let percentageComplete = (fromStart.asMinutes()/diff.asMinutes())*100;
console.log(fromStart.asMinutes(), fromNext.asMinutes(), diff.asMinutes(), percentageComplete);
this.statusBarCurrentProgress.style.width = `${percentageComplete.toFixed(0)}%`;
this.statusBarText.innerText = `${fromNext.asMinutes().toFixed(0)} mins left`;
}

async parseDayPlanner():Promise<PlanSummaryData> {
const fileContent = await this.vault.adapter.read(DAY_PLANNER_FILENAME);
const planData = await this.parser.parseMarkdown(fileContent);
console.log('Current Task', planData.current());
return planData;
}

linkToDayPlanBlock() {
if(this.statusBarAdded) {
return;
}
let minutes = new Date().getMinutes();
let left = 60 - minutes;
// let statusBarContent = new Array(count + 1).join('=');
let status = this.statusBar.createEl('div', { cls: 'day-planner', 'title': 'View the Day Planner', prepend: true});
this.statusBarText = status.createEl('span', { cls: ['status-bar-item-segment', 'day-planner-status-bar-text']});
this.statusBarProgress = status.createEl('div', { cls: ['status-bar-item-segment', 'day-planner-progress-bar']});
this.statusBarCurrentProgress = this.statusBarProgress.createEl('div', { cls: 'day-planner-progress-value'});
status.onClickEvent((ev: any) => {
this.app.workspace.openLinkText('Day Planner', '', false);
});

this.statusBarAdded = true;
}

codeMirror = (cm: any) => {
console.log(cm);
}

onunload() {
console.log("Unloading Day Planner plugin");
}

}
45 changes: 45 additions & 0 deletions src/moment-date-regex.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { DEFAULT_DATE_FORMAT, DATE_REGEX } from './constants';
import * as moment from 'moment';

export default class MomentDateRegex {
replace(input: string): string {
//A regex to capture multiple matches, each with a target group ({date:YYMMDD}) and date group (YYMMDD)
const dateRegex = DATE_REGEX;
const customFolderString = input;
//Iterate through the matches to collect them in a single array
const matches = [];
let match;
while(match = dateRegex.exec(customFolderString)){
matches.push(match)
}
//Return the custom folder setting value if no dates are found
if(!matches || matches.length === 0){
return input;
}
const now = new Date();
//Transform date matches into moment formatted dates
const formattedDates = matches.map(m => {
//Default to YYYYMMDDHHmm if {{date}} is used
const dateFormat = m.groups.date === '' ? DEFAULT_DATE_FORMAT : m.groups.date;
return [m.groups.target,
this.getMoment(now, dateFormat)];
});

//Check to see if any date formatting is needed. If not return the unformatted setting text.
let output = customFolderString;
formattedDates.forEach(fd => {
output = output.replace(fd[0], fd[1]);
})
return output;
}

getMoment(now: Date, dateFormat: string) {
if((window as any).moment) {
return (window as any)
.moment(now)
.format(dateFormat)
} else {
return moment(now).format(dateFormat);
}
}
}
Loading

0 comments on commit 4d16c50

Please sign in to comment.