generated from tpl/obsidian-sample-plugin
A few small changes
This commit is contained in:
parent
891041c965
commit
19d56652eb
|
@ -43,7 +43,7 @@ export class ResetReadingStatusCommand extends EditorCheckCommand {
|
|||
frontMatter[this.settings.endDateProperty] = "";
|
||||
});
|
||||
|
||||
this.readingLog.removeEntriesForBook(file.basename);
|
||||
this.readingLog.deleteEntriesForBook(file.basename);
|
||||
|
||||
new Notice("Reading status reset for " + file.basename);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { parseYaml } from "obsidian";
|
||||
import { parseYaml, TFile } from "obsidian";
|
||||
import { ReadingCalendarSettingsSchema } from "./ReadingCalendarCodeBlock";
|
||||
import type { SvelteCodeBlockProps } from "./SvelteCodeBlockRenderer";
|
||||
import {
|
||||
|
@ -15,7 +15,8 @@
|
|||
setReadingLogContext,
|
||||
} from "@ui/stores/reading-log.svelte";
|
||||
import { ArrowLeft, ArrowRight } from "lucide-svelte";
|
||||
import { onMount } from "svelte";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import OpenFileLink from "@ui/components/OpenFileLink.svelte";
|
||||
|
||||
const { plugin, source }: SvelteCodeBlockProps = $props();
|
||||
|
||||
|
@ -85,6 +86,11 @@
|
|||
updateToday();
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
metadataStore.destroy();
|
||||
readingLog.destroy();
|
||||
});
|
||||
|
||||
const weeks = $derived.by(() => {
|
||||
// @ts-expect-error Moment is provided by Obsidian
|
||||
const firstDay = moment()
|
||||
|
@ -114,40 +120,53 @@
|
|||
return weeks;
|
||||
});
|
||||
|
||||
interface CoverData {
|
||||
src: string;
|
||||
alt: string;
|
||||
interface BookData {
|
||||
coverSrc: string;
|
||||
coverAlt: string;
|
||||
file: TFile;
|
||||
}
|
||||
|
||||
interface BookMapItem {
|
||||
totalPagesRead: number;
|
||||
covers: CoverData[];
|
||||
books: BookData[];
|
||||
}
|
||||
|
||||
const bookMap = $derived.by(() => {
|
||||
const bookMap = new Map<number, BookMapItem>();
|
||||
for (const item of readingLog.entries) {
|
||||
const key = item.createdAt.date();
|
||||
const bookMap = $derived(
|
||||
readingLog.entries.reduce(
|
||||
(acc, entry) => {
|
||||
const key = entry.createdAt.date();
|
||||
|
||||
let coverPath = metadataStore.metadata.find(
|
||||
(entry) => entry.file.basename === item.book,
|
||||
)?.frontmatter?.[settings.coverProperty];
|
||||
const metadata = metadataStore.metadata.find(
|
||||
(other) => other.file.basename === entry.book,
|
||||
);
|
||||
|
||||
coverPath = plugin.app.vault.getFileByPath(coverPath);
|
||||
|
||||
if (!coverPath) {
|
||||
continue;
|
||||
if (!metadata) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
coverPath = plugin.app.vault.getResourcePath(coverPath);
|
||||
|
||||
const value = bookMap.get(key) ?? { totalPagesRead: 0, covers: [] };
|
||||
value.totalPagesRead += item.pagesRead;
|
||||
value.covers.push({ src: coverPath, alt: item.book });
|
||||
bookMap.set(key, value);
|
||||
const coverPath = metadata.frontmatter?.[
|
||||
settings.coverProperty
|
||||
] as string;
|
||||
const coverFile = plugin.app.vault.getFileByPath(coverPath);
|
||||
let coverSrc = "";
|
||||
if (coverFile) {
|
||||
coverSrc = plugin.app.vault.getResourcePath(coverFile);
|
||||
}
|
||||
return bookMap;
|
||||
|
||||
const value = acc[key] ?? { totalPagesRead: 0, books: [] };
|
||||
value.totalPagesRead += entry.pagesRead;
|
||||
value.books.push({
|
||||
coverSrc,
|
||||
coverAlt: entry.book,
|
||||
file: metadata.file,
|
||||
});
|
||||
acc[key] = value;
|
||||
|
||||
return acc;
|
||||
},
|
||||
{} as Record<number, BookMapItem>,
|
||||
),
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="reading-calendar">
|
||||
|
@ -207,6 +226,7 @@
|
|||
<tr>
|
||||
{#each week as day}
|
||||
{@const isThisMonth = day.month() === month}
|
||||
{@const date = day.date()}
|
||||
<td
|
||||
class:is-today={day.isSame(today, "day")}
|
||||
class:is-weekend={day.day() === 0 ||
|
||||
|
@ -218,23 +238,25 @@
|
|||
>
|
||||
<div class="header">
|
||||
<span>{day.date()}</span>
|
||||
{#if isThisMonth && bookMap.has(day.date())}
|
||||
{@const data = bookMap.get(day.date())!}
|
||||
{#if isThisMonth && date in bookMap}
|
||||
{@const data = bookMap[date]}
|
||||
<span class="total-pages-read">
|
||||
Pages: {data.totalPagesRead}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="covers">
|
||||
{#if isThisMonth && bookMap.has(day.date())}
|
||||
{@const data = bookMap.get(day.date())!}
|
||||
{#each data.covers as cover}
|
||||
{#if cover}
|
||||
{#if isThisMonth && date in bookMap}
|
||||
{@const data = bookMap[date]}
|
||||
{#each data.books as book}
|
||||
<div class="cover">
|
||||
<OpenFileLink file={book.file}>
|
||||
<img
|
||||
src={cover.src}
|
||||
alt={cover.alt}
|
||||
src={book.coverSrc}
|
||||
alt={book.coverAlt}
|
||||
/>
|
||||
{/if}
|
||||
</OpenFileLink>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -312,59 +334,46 @@
|
|||
width: 100%;
|
||||
aspect-ratio: 2 / 3;
|
||||
|
||||
.cover {
|
||||
border-radius: var(--radius-l);
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
img {
|
||||
border-radius: var(--radius-l);
|
||||
}
|
||||
|
||||
&:has(img:first-child:nth-last-child(1)) {
|
||||
img {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
&:has(img:first-child:nth-last-child(2)) {
|
||||
img:first-child {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
&:has(.cover:first-child:nth-last-child(2)) {
|
||||
.cover:first-child {
|
||||
clip-path: polygon(0 0, 100% 0, 0 100%);
|
||||
}
|
||||
|
||||
img:last-child {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.cover:last-child {
|
||||
clip-path: polygon(100% 100%, 100% 0, 0 100%);
|
||||
}
|
||||
}
|
||||
|
||||
&:has(img:first-child:nth-last-child(3)) {
|
||||
img:first-child {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
&:has(.cover:first-child:nth-last-child(3)) {
|
||||
.cover:first-child {
|
||||
clip-path: polygon(0 0, 100% 0, 100% 20%, 0 50%);
|
||||
}
|
||||
|
||||
img:nth-child(2) {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.cover:nth-child(2) {
|
||||
clip-path: polygon(100% 80%, 100% 20%, 0 50%);
|
||||
}
|
||||
|
||||
img:last-child {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
.cover:last-child {
|
||||
clip-path: polygon(0 50%, 100% 80%, 100% 100%, 0% 100%);
|
||||
}
|
||||
}
|
||||
|
||||
&:has(img:first-child:nth-last-child(4)) {
|
||||
img:first-child {
|
||||
&:has(.cover:first-child:nth-last-child(4)) {
|
||||
.cover:first-child {
|
||||
position: absolute;
|
||||
height: 50%;
|
||||
width: 50%;
|
||||
|
@ -372,7 +381,7 @@
|
|||
left: 0;
|
||||
}
|
||||
|
||||
img:nth-child(2) {
|
||||
.cover:nth-child(2) {
|
||||
position: absolute;
|
||||
height: 50%;
|
||||
width: 50%;
|
||||
|
@ -380,7 +389,7 @@
|
|||
right: 0;
|
||||
}
|
||||
|
||||
img:nth-child(3) {
|
||||
.cover:nth-child(3) {
|
||||
position: absolute;
|
||||
height: 50%;
|
||||
width: 50%;
|
||||
|
@ -388,7 +397,7 @@
|
|||
left: 0;
|
||||
}
|
||||
|
||||
img:last-child {
|
||||
.cover:last-child {
|
||||
position: absolute;
|
||||
height: 50%;
|
||||
width: 50%;
|
||||
|
|
|
@ -16,8 +16,14 @@
|
|||
const href = $derived(
|
||||
makeUri(file instanceof TFile ? file.path : getLinkpath(file)),
|
||||
);
|
||||
|
||||
const title = $derived(
|
||||
file instanceof TFile
|
||||
? file.basename
|
||||
: file.split("/").pop()?.replace(".md", ""),
|
||||
);
|
||||
</script>
|
||||
|
||||
<a {href}>
|
||||
<a {href} {title}>
|
||||
{@render children?.()}
|
||||
</a>
|
||||
|
|
|
@ -111,12 +111,13 @@
|
|||
type: "linear",
|
||||
display: true,
|
||||
position: "left",
|
||||
ticks: { beginAtZero: true },
|
||||
beginAtZero: true,
|
||||
},
|
||||
y1: {
|
||||
type: "linear",
|
||||
display: !isMonthly,
|
||||
position: "right",
|
||||
beginAtZero: true,
|
||||
grid: { drawOnChartArea: false },
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import type { ReadingLog, ReadingLogEntry } from "@utils/ReadingLog";
|
||||
import { getContext, setContext } from "svelte";
|
||||
import { createDateFilter, type DateFilterStore } from "./date-filter.svelte";
|
||||
import {
|
||||
createDateFilter,
|
||||
type DateFilterStore,
|
||||
type DateFilterStoreOptions,
|
||||
} from "./date-filter.svelte";
|
||||
|
||||
export interface ReadingLogStore extends DateFilterStore {
|
||||
get entries(): ReadingLogEntry[];
|
||||
|
@ -12,13 +16,16 @@ export interface ReadingLogStore extends DateFilterStore {
|
|||
destroy(): void;
|
||||
}
|
||||
|
||||
export function createReadingLog(readingLog: ReadingLog): ReadingLogStore {
|
||||
export function createReadingLog(
|
||||
readingLog: ReadingLog,
|
||||
{ initialMonth = true, ...otherOpts }: DateFilterStoreOptions = {}
|
||||
): ReadingLogStore {
|
||||
let entries: ReadingLogEntry[] = $state(readingLog.getEntries());
|
||||
|
||||
const dateFilter = createDateFilter(
|
||||
() => entries,
|
||||
(entry) => entry.createdAt,
|
||||
{ initialMonth: true }
|
||||
{ initialMonth, ...otherOpts }
|
||||
);
|
||||
|
||||
async function addEntry(entry: ReadingLogEntry): Promise<void> {
|
||||
|
@ -34,18 +41,24 @@ export function createReadingLog(readingLog: ReadingLog): ReadingLogStore {
|
|||
}
|
||||
|
||||
async function removeEntry(entry: ReadingLogEntry): Promise<void> {
|
||||
await readingLog.removeEntry(entry);
|
||||
await readingLog.deleteEntry(entry);
|
||||
}
|
||||
|
||||
const loadHandler = readingLog.on("load", (payload) => {
|
||||
console.info("Reading log loaded");
|
||||
console.debug("Reading log entries:", payload.entries);
|
||||
entries = payload.entries;
|
||||
});
|
||||
|
||||
const createdHandler = readingLog.on("created", (payload) => {
|
||||
const createdHandler = readingLog.on("create", (payload) => {
|
||||
console.info("Reading log entry created");
|
||||
console.debug("Reading log entry:", payload.entry);
|
||||
entries.push(payload.entry);
|
||||
});
|
||||
|
||||
const updatedHandler = readingLog.on("updated", (payload) => {
|
||||
const updatedHandler = readingLog.on("update", (payload) => {
|
||||
console.info("Reading log entry updated");
|
||||
console.debug("Reading log entry:", payload.entry);
|
||||
const index = entries.findIndex(
|
||||
(entry) => entry.id === payload.entry.id
|
||||
);
|
||||
|
@ -54,7 +67,9 @@ export function createReadingLog(readingLog: ReadingLog): ReadingLogStore {
|
|||
}
|
||||
});
|
||||
|
||||
const removedHandler = readingLog.on("removed", (payload) => {
|
||||
const removedHandler = readingLog.on("delete", (payload) => {
|
||||
console.info("Reading log entry deleted");
|
||||
console.debug("Reading log entry:", payload.entry);
|
||||
const index = entries.findIndex(
|
||||
(entry) => entry.id === payload.entry.id
|
||||
);
|
||||
|
|
|
@ -14,9 +14,9 @@ export interface ReadingLogEntry {
|
|||
|
||||
interface ReadingLogEventMap {
|
||||
load: { entries: ReadingLogEntry[] };
|
||||
created: { entry: ReadingLogEntry };
|
||||
updated: { entry: ReadingLogEntry };
|
||||
removed: { entry: ReadingLogEntry };
|
||||
create: { entry: ReadingLogEntry };
|
||||
update: { entry: ReadingLogEntry };
|
||||
delete: { entry: ReadingLogEntry };
|
||||
}
|
||||
|
||||
const DEFAULT_FILENAME = "reading-log.json";
|
||||
|
@ -126,7 +126,7 @@ export class ReadingLog extends EventEmitter<ReadingLogEventMap> {
|
|||
public async addRawEntry(entry: ReadingLogEntry) {
|
||||
this.entries.push(entry);
|
||||
await this.save();
|
||||
this.emit("created", { entry });
|
||||
this.emit("create", { entry });
|
||||
}
|
||||
|
||||
public async updateEntry(entry: ReadingLogEntry): Promise<void> {
|
||||
|
@ -138,20 +138,20 @@ export class ReadingLog extends EventEmitter<ReadingLogEventMap> {
|
|||
|
||||
this.entries[index] = entry;
|
||||
await this.save();
|
||||
this.emit("updated", { entry });
|
||||
this.emit("update", { entry });
|
||||
}
|
||||
|
||||
public async removeEntry(entry: ReadingLogEntry): Promise<void> {
|
||||
public async deleteEntry(entry: ReadingLogEntry): Promise<void> {
|
||||
const index = this.entries.findIndex((other) => other.id === entry.id);
|
||||
|
||||
if (index !== -1) {
|
||||
const removed = this.entries.splice(index, 1);
|
||||
await this.save();
|
||||
this.emit("removed", { entry: removed[0] });
|
||||
this.emit("delete", { entry: removed[0] });
|
||||
}
|
||||
}
|
||||
|
||||
public async removeEntriesForBook(book: string): Promise<void> {
|
||||
public async deleteEntriesForBook(book: string): Promise<void> {
|
||||
this.entries = this.entries.filter((entry) => entry.book !== book);
|
||||
await this.save();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue