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

Add time in note title #317

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
81 changes: 70 additions & 11 deletions src/calendars/FullNoteCalendar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ const makeApp = (app: MockApp): ObsidianInterface => ({

const dirName = "events";
const color = "#BADA55";
const timeNotInNoteTitle = false;
const timeInNoteTitle = true;

describe("Note Calendar Tests", () => {
it.each([
Expand Down Expand Up @@ -120,13 +122,19 @@ describe("Note Calendar Tests", () => {
new MockAppBuilder(dirName)
)
)
.done()
);
const calendar = new FullNoteCalendar(obsidian, color, dirName);
const res = await calendar.getEvents();
expect(res.length).toBe(inputs.length);
const events = res.map((e) => e[0]);
const paths = res.map((e) => e[1].file.path);
)
.done()
);
const calendar = new FullNoteCalendar(
obsidian,
color,
dirName,
timeNotInNoteTitle
);
const res = await calendar.getEvents();
expect(res.length).toBe(inputs.length);
const events = res.map((e) => e[0]);
const paths = res.map((e) => e[1].file.path);

expect(
res.every((elt) => elt[1].lineNumber === undefined)
Expand Down Expand Up @@ -162,8 +170,13 @@ describe("Note Calendar Tests", () => {

it("creates an event", async () => {
const obsidian = makeApp(MockAppBuilder.make().done());
const calendar = new FullNoteCalendar(obsidian, color, dirName);
const event = {
const calendar = new FullNoteCalendar(
obsidian,
color,
dirName,
timeNotInNoteTitle
);
const event: OFCEvent = {
title: "Test Event",
date: "2022-01-01",
endDate: null,
Expand Down Expand Up @@ -196,6 +209,42 @@ describe("Note Calendar Tests", () => {
`);
});

it("creates an event with time in note title", async () => {
const obsidian = makeApp(MockAppBuilder.make().done());
const calendar = new FullNoteCalendar(
obsidian,
color,
dirName,
timeInNoteTitle
);
const event: OFCEvent = {
title: "Test Event",
date: "2022-01-01",
startTime: "11:00",
endTime: "12:30",
};

(obsidian.create as jest.Mock).mockReturnValue({
path: join(dirName, "2022-01-01 1100 Test Event.md"),
});
const { lineNumber } = await calendar.createEvent(event);
expect(lineNumber).toBeUndefined();
expect(obsidian.create).toHaveBeenCalledTimes(1);
const returns = (obsidian.create as jest.Mock).mock.calls[0];
expect(returns).toMatchInlineSnapshot(`
[
"events/2022-01-01 1100 Test Event.md",
"---
title: Test Event
date: 2022-01-01
startTime: 11:00
endTime: 12:30
---
",
]
`);
});

it("cannot overwrite event", async () => {
const event = {
title: "Test Event",
Expand All @@ -213,7 +262,12 @@ describe("Note Calendar Tests", () => {
)
.done()
);
const calendar = new FullNoteCalendar(obsidian, color, dirName);
const calendar = new FullNoteCalendar(
obsidian,
color,
dirName,
timeNotInNoteTitle
);
await assertFailed(
() => calendar.createEvent(parseEvent(event)),
/already exists/
Expand All @@ -240,7 +294,12 @@ describe("Note Calendar Tests", () => {
)
.done()
);
const calendar = new FullNoteCalendar(obsidian, color, dirName);
const calendar = new FullNoteCalendar(
obsidian,
color,
dirName,
timeNotInNoteTitle
);

const firstFile = obsidian.getAbstractFileByPath(
join("events", filename)
Expand Down
42 changes: 36 additions & 6 deletions src/calendars/FullNoteCalendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,26 @@ import { TFile, TFolder, parseYaml } from "obsidian";
import { rrulestr } from "rrule";
import { EventPathLocation } from "../core/EventStore";
import { ObsidianInterface } from "../ObsidianAdapter";
import { OFCEvent, EventLocation, validateEvent } from "../types";
import {
OFCEvent,
EventLocation,
validateEvent,
isRangeTimeData,
} from "../types";
import { EditableCalendar, EditableEventResponse } from "./EditableCalendar";

const basenameFromEvent = (event: OFCEvent): string => {
const basenameFromEvent = (
event: OFCEvent,
timeInNoteTitle: boolean
): string => {
switch (event.type) {
case undefined:
case "single":
if (timeInNoteTitle && isRangeTimeData(event)) {
return `${event.date} ${event.startTime.replace(":", "")} ${
event.title
}`;
}
return `${event.date} ${event.title}`;
case "recurring":
return `(Every ${event.daysOfWeek.join(",")}) ${event.title}`;
Expand All @@ -17,7 +30,8 @@ const basenameFromEvent = (event: OFCEvent): string => {
}
};

const filenameForEvent = (event: OFCEvent) => `${basenameFromEvent(event)}.md`;
const filenameForEvent = (event: OFCEvent, timeInNoteTitle: boolean) =>
`${basenameFromEvent(event, timeInNoteTitle)}.md`;

const FRONTMATTER_SEPARATOR = "---";

Expand Down Expand Up @@ -146,15 +160,25 @@ function modifyFrontmatterString(
export default class FullNoteCalendar extends EditableCalendar {
app: ObsidianInterface;
private _directory: string;
private _timeInNoteTitle: boolean;

constructor(app: ObsidianInterface, color: string, directory: string) {
constructor(
app: ObsidianInterface,
color: string,
directory: string,
timeInNoteTitle: boolean
) {
super(color);
this.app = app;
this._directory = directory;
this._timeInNoteTitle = timeInNoteTitle;
}
get directory(): string {
return this._directory;
}
get timeInNoteTitle(): boolean {
return this._timeInNoteTitle;
}

get type(): "local" {
return "local";
Expand Down Expand Up @@ -216,7 +240,10 @@ export default class FullNoteCalendar extends EditableCalendar {
}

async createEvent(event: OFCEvent): Promise<EventLocation> {
const path = `${this.directory}/${filenameForEvent(event)}`;
const path = `${this.directory}/${filenameForEvent(
event,
this.timeInNoteTitle
)}`;
if (this.app.getAbstractFileByPath(path)) {
throw new Error(`Event at ${path} already exists.`);
}
Expand All @@ -239,7 +266,10 @@ export default class FullNoteCalendar extends EditableCalendar {
);
}

const updatedPath = `${file.parent.path}/${filenameForEvent(event)}`;
const updatedPath = `${file.parent.path}/${filenameForEvent(
event,
this.timeInNoteTitle
)}`;
return { file: { path: updatedPath }, lineNumber: undefined };
}

Expand Down
3 changes: 2 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ export default class FullCalendarPlugin extends Plugin {
? new FullNoteCalendar(
new ObsidianIO(this.app),
info.color,
info.directory
info.directory,
info.timeInNoteTitle
)
: null,
dailynote: (info) =>
Expand Down
2 changes: 1 addition & 1 deletion src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ export type Authentication = {
password: string;
};

export type CalDAVSource = Extract<CalendarInfo, { type: "caldav" }>;
export type CalDAVSource = Extract<CalendarInfo, { type: "caldav" }>;
23 changes: 23 additions & 0 deletions src/ui/components/AddCalendarSource.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ export const AddCalendarSource = ({
const isCalDAV = source.type === "caldav";

const [setting, setSettingState] = useState(source);
const [isTimeInNoteTitle, setIsTimeInNoteTitle] = useState(false);
const [submitting, setSubmitingState] = useState(false);
const [submitText, setSubmitText] = useState(
isCalDAV ? "Import Calendars" : "Add Calendar"
Expand Down Expand Up @@ -272,6 +273,28 @@ export const AddCalendarSource = ({
directories={directories}
/>
)}
{source.type === "local" && (
<div className="setting-item">
<div className="setting-item-info">
<div className="setting-item-name">
Time in note title
</div>
<div className="setting-item-description">
Format note title as &lt;YYYY-MM-DD&gt;
&lt;HHDD&gt; &lt;Event title&gt;.md.
</div>
</div>
<div className="setting-item-control">
<input
checked={isTimeInNoteTitle}
onChange={(e) =>
setIsTimeInNoteTitle(e.target.checked)
}
type="checkbox"
/>
</div>
</div>
)}
{source.type === "dailynote" && (
<HeadingInput
source={setting}
Expand Down