Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SRS SM2 #25

Merged
merged 15 commits into from
Mar 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ module.exports = {
// setupFiles: [],

// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
setupFilesAfterEnv: ["jest-extended"],

// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
Expand Down
149 changes: 149 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"cross-env": "^6.0.3",
"html-webpack-plugin": "^3.2.0",
"jest": "^25.1.0",
"jest-extended": "^0.11.5",
"react-test-renderer": "^16.12.0",
"ts-jest": "^25.0.0",
"tslint": "^6.0.0",
Expand Down
54 changes: 54 additions & 0 deletions src/ts/contentScripts/estimates/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import {Roam, RoamNode} from '../../utils/roam';
import {Feature, Settings, Shortcut, String} from '../../utils/settings';
import {getActiveEditElement} from '../../utils/dom';

const estimateProperty: String = {type: 'string', id: 'estimate_property', label: 'Property to base estimates on'};

export const config: Feature = {
id: 'calculate-estimate',
name: 'Calculate estimate',
settings: [
{
type: 'shortcut',
id: 'calculate-estimate',
label: 'Calculate estimate shortcut',
initValue: 'ctrl+m',
placeholder: '',
onPress: calculateFirstSiblingTotal
} as Shortcut,
estimateProperty,
],
};

function getParentElement() {
return getActiveEditElement()?.closest('.roam-block-container')?.parentElement;
}

/** I'm still figuring out UX on this one.
* The current expectation is that you have to create a parent node, put a query as a child of it,
* then run this with cursor ina query sibling.
* Maybe I should create sibling? then you can do it from query node, which seems somewhat more intuitive
* but when your cursor is in the query node it's not rendered, which may be confusing
*
* or maybe flow - you select query node, then press shortcut, get estimate for it in new node below
*
*/
export async function calculateFirstSiblingTotal() {
const attributeName = await Settings.get(config.id, estimateProperty.id, 'pomodoro_estimate');
const estimateRegex = new RegExp(`${attributeName}:\\s*(\\d+\\.?\\d*)`, 'g');

const queryNode = getParentElement()?.querySelector('.rm-reference-main') as HTMLElement;
const queryText = queryNode?.innerText;
console.log('Extracting estimate from ' + queryText);

let total = 0;

const nextMatch = () => estimateRegex.exec(queryText);
let match = nextMatch();
while (match) {
total += parseFloat(match[1]);
match = nextMatch();
}

Roam.applyToCurrent(node => new RoamNode(`total_${attributeName}::${total}` + node.text, node.selection))
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {config as incDec} from './inc-dec-value/index'
import {config as customCss} from './custom-css/index'
import {config as srs} from './srs/index'
import {config as blockManipulation} from './block-manipulation'
import {config as estimate} from './estimates/index'
import {filterAsync, mapAsync} from '../utils/async';

export const Features = {
Expand All @@ -12,6 +13,7 @@ export const Features = {
customCss,
srs,
blockManipulation,
estimate,
]),

isActive: Settings.isActive,
Expand Down
12 changes: 1 addition & 11 deletions src/ts/contentScripts/inc-dec-value/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {getActiveEditElement, getInputEvent} from '../../utils/dom';
import {Feature, Shortcut} from '../../utils/settings'
import {RoamDate} from '../../date/common';
import {dateFromPageName, RoamDate} from '../../date/common';

export const config: Feature = {
id: 'incDec',
Expand All @@ -23,16 +23,6 @@ export const config: Feature = {
]
}


const dateFromPageName = (text: string): Date => {
return new Date(
text
.slice(2)
.slice(0, -2)
.replace(/(th,|nd,|rd,|st,)/, ',')
);
};

const saveChanges = (el: HTMLTextAreaElement, cursor: number, value: string): void => {
el.value = value;
el.selectionStart = cursor;
Expand Down
29 changes: 21 additions & 8 deletions src/ts/contentScripts/srs/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import {Roam, RoamNode} from '../../utils/roam';
import {Feature, Shortcut} from '../../utils/settings'
import {SRSSignal, SRSSignals} from '../../srs/scheduler';
import {SM2Node} from '../../srs/SM2Node';
import {AnkiScheduler} from '../../srs/AnkiScheduler';

export const config: Feature = {
id: 'srs',
Expand All @@ -12,16 +15,26 @@ export const config: Feature = {
initValue: 'Ctrl+q',
onPress: triggerNextBucket
} as Shortcut
]
].concat(SRSSignals.map(it => ({
type: 'shortcut', id: `srs_${SRSSignal[it]}`, label: `SRS: ${SRSSignal[it]}`, initValue: `ctrl+shift+${it}`,
onPress: () => rescheduleCurrentNote(it)
}
)))
}

const bucketExpr = /\[\[Bucket (\d+)]]/;
const nextBucket = (nodeStr: string) => `[[Bucket ${parseInt(nodeStr) + 1}]]`;
export function rescheduleCurrentNote(signal: SRSSignal) {
const scheduler = new AnkiScheduler()
Roam.applyToCurrent(node => scheduler.schedule(new SM2Node(node.text, node.selection), signal))
}

const bucketExpr = /(?:\[\[\[\[interval]]::(\d+)]])|(?:#Box(\d+))/gi;
const nextBucket = (nodeStr: string) => `[[[[interval]]::${parseInt(nodeStr) + 1}]]`;

export function triggerNextBucket() {
Roam.applyToCurrent(
(element => {
return new RoamNode(element.text.replace(bucketExpr, (_, numStr: string) => nextBucket(numStr)),
element.selection);
}));
}
(element =>
new RoamNode(
element.text.replace(bucketExpr,
(_, ...numbers) => nextBucket(numbers.filter(it => it)[0])),
element.selection)));
}
17 changes: 16 additions & 1 deletion src/ts/date/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,24 @@ import dateFormat from 'dateformat';

export const RoamDate = {
formatString: `'[['mmmm dS, yyyy']]'`,
regex: /\[\[(January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}(st|nd|th|rd), \d{4}\]\]/gm,
regex: /\[\[(January|February|March|April|May|June|July|August|September|October|November|December) \d{1,2}(st|nd|th|rd), \d{4}]]/gm,

format(date: Date) {
return dateFormat(date, this.formatString)
}
}

export const dateFromPageName = (text: string): Date => {
return new Date(
text
.slice(2)
.slice(0, -2)
.replace(/(th,|nd,|rd,|st,)/, ',')
);
};

export function addDays(date: Date, days: number) {
const result = new Date(date)
result.setDate(result.getDate() + days)
return result
}
27 changes: 27 additions & 0 deletions src/ts/date/withDate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {RoamNode} from '../utils/roam';
import {dateFromPageName, RoamDate} from './common';
import {Constructor} from '../mixins/common';

export function withDate<T extends Constructor<RoamNode>>(SuperClass: T) {
return class NodeWithDate extends SuperClass {
listDatePages() {
return this.text.match(RoamDate.regex) || []
}

listDates() {
return this.listDatePages().map(dateFromPageName)
}

/** If has 1 date - replace it, if more then 1 date - append it */
withDate(date: Date) {
const currentDates = this.listDatePages()
const newDate = RoamDate.format(date);
const newText = currentDates.length === 1 ?
this.text.replace(currentDates[0], newDate) :
this.text + ' ' + newDate;

// @ts-ignore
return new this.constructor(newText, this.selection)
}
};
}
1 change: 1 addition & 0 deletions src/ts/mixins/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type Constructor<T> = new (...args: any[]) => T;
Loading