diff --git a/CHANGELOG.md b/CHANGELOG.md index 09c2e1a..016f359 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +### [2024-03-29] v1.4.0: 优化预约功能 + +- 重构了预约插入功能,[#184](https://github.com/frostime/siyuan-dailynote-today/issues/184) +- 重构了获取今日日记的方法,依赖更新的 `custom-daily-note-` 属性 +- 如果当天有预约,则图标加高亮 [#183](https://github.com/frostime/siyuan-dailynote-today/issues/183) + + ### [2023-12-09] v1.3.1: 允许用户在同步后创建日记 - 允许用户在同步后创建日记 diff --git a/package.json b/package.json index cb35af0..102d128 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "siyuan-dailynote-today", - "version": "1.2.9", + "version": "1.4.0", "description": "", "main": ".src/index.js", "keywords": [], diff --git a/plugin.json b/plugin.json index 6dc1d50..4fcac36 100644 --- a/plugin.json +++ b/plugin.json @@ -6,8 +6,8 @@ "en_US": "Daily Note Today" }, "url": "https://github.com/frostime/siyuan-dailynote-today", - "version": "1.3.2", - "minAppVersion": "2.11.1", + "version": "1.4.0", + "minAppVersion": "3.0.0", "description": { "zh_CN": "自动创建日记 + 快速打开日记 + 日程 TODO 管理 + 移动块功能", "en_US": "Automatically open today's notes + Quickly open today's notes + TODO mangagement for today + Move blocks" diff --git a/src/components/toolbar-menu.ts b/src/components/toolbar-menu.ts index 74f902e..98e806c 100644 --- a/src/components/toolbar-menu.ts +++ b/src/components/toolbar-menu.ts @@ -1,24 +1,23 @@ -import { IMenuItemOption, Menu, Plugin, showMessage } from "siyuan"; -import { currentDiaryStatus, openDiary, updateTodayReservation } from "../func"; +import { IMenuItemOption, Menu, showMessage } from "siyuan"; +import { currentDiaryStatus, openDiary } from "../func"; import notebooks from "../global-notebooks"; import { reservation, settings } from "../global-status"; import { i18n, isMobile, debug } from "../utils"; import { eventBus } from "../event-bus"; import { iconDiary } from "./svg"; -import * as serverApi from '@/serverApi'; +import type DailyNoteTodayPlugin from '@/index'; +// import * as serverApi from '@/serverApi'; let ContextMenuListener: EventListener; let UpdateDailyNoteStatusListener: EventListener; export class ToolbarMenuItem { - plugin: Plugin; + plugin: DailyNoteTodayPlugin; ele: HTMLElement; iconStatus: Map; - private onProtyleLoadedBindThis = this.onProtyleLoaded.bind(this); - - constructor(plugin: Plugin) { + constructor(plugin: DailyNoteTodayPlugin) { this.plugin = plugin; ContextMenuListener = (event: MouseEvent) => this.contextMenu(event); UpdateDailyNoteStatusListener = () => this.updateDailyNoteStatus(); @@ -41,32 +40,11 @@ export class ToolbarMenuItem { } - /** - * #TODO: 将这个函数重构放到 reservation.ts 下 - * #TODO: 2.10.16 版本之后 SiYuan 的 createDailyNote 返回 doc id, 所以可以不需要再使用监听了 - * 根据预约情况, 监听日记本的加载, 如果是今天的日记本, 则更新预约状态 - * @returns - */ - startMonitorDailyNoteForReservation() { - if (!reservation.isTodayReserved) { - return - } - this.plugin.eventBus.on("loaded-protyle-static", this.onProtyleLoadedBindThis); - // 3分钟后, 取消监听, 防止不必要的性能损耗 - setTimeout( - () => { - this.plugin.eventBus.off("loaded-protyle-static", this.onProtyleLoadedBindThis); - }, - 1000 * 60 * 2 - ); - } - release() { this.ele.removeEventListener('contextmenu', ContextMenuListener); eventBus.unSubscribe('moveBlocks', UpdateDailyNoteStatusListener); this.ele.remove(); this.ele = null; - this.plugin.eventBus.off("loaded-protyle-static", this.onProtyleLoadedBindThis); debug('TopBarIcon released'); } @@ -152,59 +130,20 @@ export class ToolbarMenuItem { let item: IMenuItemOption = { label: notebook.name, icon: this.iconStatus.get(notebook.id), - click: async () => openDiary(notebook), + click: async () => { + if (notebook.id === notebooks.default.id) { + await openDiary(notebook); + this.plugin.routineHandler.tryAutoInsertResv(); + } else { + openDiary(notebook); + } + } } menuItems.push(item); } return menuItems; } - /** - * 监听自动打开日记后,插入当天预约用 - */ - private async onProtyleLoaded({ detail }) { - console.debug(detail); - const protyle = detail.protyle; - const block_ = protyle.block; - if (block_.id != block_.rootID) { - return; - } - //是否为文档 - const headElement: HTMLElement = protyle?.model?.headElement; - if (!headElement) { - return; - } - //笔记本是否是默认笔记本 - const notebookId = protyle.notebookId; - if (notebookId !== notebooks.default.id) { - return; - } - - - const CheckReservation = async (blockId: BlockId, cnt: number =1) => { - if (cnt > 3) { - return; - } - debug("检查", blockId); - const block: Block = await serverApi.getBlockByID(blockId); - if (block === undefined) { - console.warn(`New opened docId ${blockId} undefined`); - //能调用这个函数,说明这个文档一定存在,查不到就继续等继续差 - setTimeout(() => CheckReservation(blockId, cnt++), 1000 * cnt); - return - } - // console.log(block.hpath); - if (notebooks.default.dailynoteHpath === block.hpath) { - debug('Got Today\'s daily note'); - this.plugin.eventBus.off("loaded-protyle-static", this.onProtyleLoadedBindThis); - await updateTodayReservation(notebooks.default, true); - } - } - debug("打开新的文档, 2s后检查", block_.id); - //如果是新创建的日记,那么在这一刻后端是拿不到对应的块的,所以需要先等一下 - setTimeout(() => CheckReservation(block_.id), 1000 * 2); - } - async updateDailyNoteStatus() { let diaryStatus: Map = await currentDiaryStatus(); notebooks.notebooks.forEach((notebook) => { diff --git a/src/func/dailynote/basic.ts b/src/func/dailynote/basic.ts index a860a59..ea76072 100644 --- a/src/func/dailynote/basic.ts +++ b/src/func/dailynote/basic.ts @@ -3,7 +3,7 @@ * @Author : Yp Z * @Date : 2023-11-12 18:06:46 * @FilePath : /src/func/dailynote/basic.ts - * @LastEditTime : 2023-11-12 18:15:30 + * @LastEditTime : 2024-03-30 21:57:20 * @Description : */ import * as serverApi from '@/serverApi'; @@ -31,6 +31,20 @@ export async function getDailynoteSprig(notebookId: string): Promise { return sprig; } +/** + * + */ +export async function queryTodayDailyNoteDoc(notebookId: NotebookId): Promise { + let td = formatDate(new Date()); + const sql = ` + select distinct B.* from blocks as B join attributes as A + on B.id = A.block_id + where A.name = 'custom-dailynote-${td}' and B.box = '${notebookId}' + `; + const blocks: Block[] = await serverApi.sql(sql); + return blocks; +} + /** * 要求思源解析模板 * @param sprig diff --git a/src/func/dailynote/open-dn.ts b/src/func/dailynote/open-dn.ts index a405330..f06e03d 100644 --- a/src/func/dailynote/open-dn.ts +++ b/src/func/dailynote/open-dn.ts @@ -3,7 +3,7 @@ * @Author : frostime * @Date : 2023-11-12 19:53:10 * @FilePath : /src/func/dailynote/open-dn.ts - * @LastEditTime : 2023-12-04 20:30:15 + * @LastEditTime : 2024-03-29 21:27:13 * @Description : */ import { showMessage, confirm, openTab, openMobileFileById } from 'siyuan'; @@ -23,6 +23,9 @@ export async function createDiary(notebook: Notebook, todayDiaryHpath: string) { let doc_id = await serverApi.createDocWithMd(notebook.id, todayDiaryHpath, ""); info(`创建日记: ${notebook.name} ${todayDiaryHpath}`); setCustomDNAttr(doc_id); + + notebook.dailyNoteDocId = doc_id; + return doc_id; } @@ -33,6 +36,9 @@ export async function createDiary(notebook: Notebook, todayDiaryHpath: string) { export async function openDiary(notebook: Notebook) { let appId = utils.app.appId; let dailynote = await serverApi.createDailyNote(notebook.id, appId); + if (dailynote) { + notebook.dailyNoteDocId = dailynote?.id; + } showMessage(`${i18n.Open}: ${notebook.name}`, 2000, 'info'); //打开文档 if (isMobile === true) { diff --git a/src/func/index.ts b/src/func/index.ts index a559f75..dc9b4bf 100644 --- a/src/func/index.ts +++ b/src/func/index.ts @@ -1,16 +1,20 @@ /** * Copyright (c) 2023 frostime all rights reserved. */ -import { settings } from '@/global-status'; +import { reservation, settings } from '@/global-status'; import { autoOpenDailyNote, checkDuplicateDiary } from './dailynote'; import type DailyNoteTodayPlugin from '@/index'; import type { EventBus } from 'siyuan'; import { debouncer } from '@/utils'; +import { updateTodayReservation } from './reserve'; +import { updateStyleSheet } from './style'; +import notebooks from '@/global-notebooks'; export * from './dailynote'; export * from './misc'; export * from './reserve'; +export * from './style'; // const WAIT_TIME_FOR_SYNC_CHECK: Milisecond = 1000 * 60 * 5; @@ -19,7 +23,7 @@ const MAX_CHECK_SYNC_TIMES: number = 10; //为了避免每次同步都检查, /** * 处理插件加载完成后一系列关于日记、同步、预约等复杂的逻辑 */ -export class StartupEventHandler { +export class RoutineEventHandler { plugin: DailyNoteTodayPlugin; eventBus: EventBus; @@ -32,7 +36,9 @@ export class StartupEventHandler { autoOpenAfterSync: false, isSyncChecked: false, - hasCheckSyncFor: 0 + hasCheckSyncFor: 0, + + hasAutoInsertResv: false //今天是否已经自动插入了预约 } constructor(plugin: DailyNoteTodayPlugin) { @@ -57,14 +63,15 @@ export class StartupEventHandler { async onPluginLoad() { + this.updateResvIconStyle(); const SYNC_ENABLED = window.siyuan.config.sync.enabled; if (!SYNC_ENABLED) { //Case 1: 如果思源没有开启同步, 就直接创建, 并无需绑定同步事件 - await this.tryToOpen(); + await this.tryAutoOpenDN(); } else if (this.flag.autoOpenAfterSync === false) { //Case 2: 如果思源开启了同步, 但是用户没有设置在同步后打开, 就直接创建 this.bindSyncEvent(); - this.tryToOpen(); + await this.tryAutoOpenDN(); } else { //Case 3: 如果思源开启了同步, 并且用户设置了在同步后打开, 就绑定同步事件 this.bindSyncEvent(); @@ -75,7 +82,7 @@ export class StartupEventHandler { console.debug('sync-end', detail); if (this.flag.hasOpened === false && this.flag.autoOpenAfterSync === true) { - this.tryToOpen(); + this.tryAutoOpenDN(); } if (!this.flag.isSyncChecked) { @@ -84,10 +91,28 @@ export class StartupEventHandler { } - public resetSyncCheckStatus() { + public resetStatusFlag() { this.flag.isSyncChecked = false; //重置同步检查状态 this.flag.hasCheckSyncFor = 0; //重置同步检查次数 + this.flag.hasOpened = false; //重置是否已经打开 + + this.flag.hasAutoInsertResv = false; //重置是否已经插入预约 + } + + /** + * 如果今天有预约,就在 head 中插入特殊的样式 + */ + public updateResvIconStyle() { + if (reservation.isTodayReserved()) { + updateStyleSheet(` + span[data-type="siyuan-dailynote-todaydock_tab"][data-title="${this.plugin.i18n.DockReserve.arial}"] { + background-color: var(--b3-theme-primary-lightest); + } + `); + } else { + updateStyleSheet(''); + } } /********** Duplicate **********/ @@ -107,13 +132,36 @@ export class StartupEventHandler { } private checkDuplicateDiary_Debounce: typeof this.checkDuplicateDiary = null; - private async tryToOpen() { + /** + * 尝试自动打开日记 + */ + private async tryAutoOpenDN() { if (this.flag.openOnStart === false) return; await autoOpenDailyNote(); this.flag.hasOpened = true; + //在自动打开日记后一段时间后,进行相关的检查处理 setTimeout(() => { + this.tryAutoInsertResv(); this.checkDuplicateDiary(); }, 1500); } + /** + * 尝试自动插入今天的预约 + */ + public async tryAutoInsertResv() { + //如果已经插入过了,就不再插入 + if (this.flag.hasAutoInsertResv === true) return; + + //如果今天没有预约,就不插入 + if (!reservation.isTodayReserved()) return; + + let succeed = updateTodayReservation(notebooks.default); + + if (succeed) { + this.flag.hasAutoInsertResv = true; + } + + } + } diff --git a/src/func/misc.ts b/src/func/misc.ts index 7215586..23cbb58 100644 --- a/src/func/misc.ts +++ b/src/func/misc.ts @@ -1,9 +1,9 @@ -import { openTab } from 'siyuan'; +import { openTab, showMessage } from 'siyuan'; import { warn, error, debug, app } from "@/utils"; import * as serverApi from '@/serverApi'; import { settings } from '@/global-status'; -import { getDailynoteSprig, renderDailynotePath } from './dailynote'; +import { getDailynoteSprig, queryTodayDailyNoteDoc } from './dailynote'; const default_sprig = `/daily note/{{now | date "2006/01"}}/{{now | date "2006-01-02"}}` const hiddenNotebook: Set = new Set(["思源笔记用户指南", "SiYuan User Guide"]); @@ -35,13 +35,17 @@ export async function queryNotebooks(): Promise | null> { for (let notebook of all_notebooks) { let sprig = await getDailynoteSprig(notebook.id); notebook.dailynoteSprig = sprig != "" ? sprig : default_sprig; - notebook.dailynoteHpath = await renderDailynotePath(notebook.dailynoteSprig); + notebook.dailynoteHpath = ""; - //防止出现不符合规范的 sprig, 不过根据 debug 情况看似乎不会出现这种情况 - if (notebook.dailynoteHpath == "") { - warn(`Invalid daily note srpig of ${notebook.name}`); - notebook.dailynoteSprig = default_sprig; - notebook.dailynoteHpath = await renderDailynotePath(default_sprig); + const docs = await queryTodayDailyNoteDoc(notebook.id); + if (docs.length > 0) { + const doc = docs[0]; + notebook.dailyNoteDocId = doc.id; + notebook.dailynoteHpath = doc.hpath; + } + + if (docs.length > 1) { + showMessage(`Warning: More than one daily-notes are founded in ${notebook.name}`, 5, "error"); } if (notebook.icon == "") { diff --git a/src/func/reserve/index.ts b/src/func/reserve/index.ts index 765d829..3bbd47e 100644 --- a/src/func/reserve/index.ts +++ b/src/func/reserve/index.ts @@ -1,5 +1,5 @@ import { confirm } from 'siyuan'; -import { error, i18n, debug } from "@/utils"; +import { i18n, debug } from "@/utils"; import * as serverApi from '@/serverApi'; import { reservation, settings } from '@/global-status'; import { Retrieve, RetvFactory } from './retrieve'; @@ -10,37 +10,53 @@ export * from './retrieve'; export * from './reserve'; -export async function initTodayReservation(notebook: Notebook) { - let todayDiaryPath = notebook.dailynoteHpath; - let docId; - let retry = 0; - const MAX_RETRY = 5; - const INTERVAL = 2500; - while (retry < MAX_RETRY) { - //插件自动创建日记的情况下可能会出现第一次拿不到的情况, 需要重试几次 - let docs = await getDocsByHpath(todayDiaryPath!, notebook); - debug(`In initResrv, retry: ${retry}`); - if (docs[0]?.id !== undefined) { - docId = docs[0].id; - break; - } - await new Promise(resolve => setTimeout(resolve, INTERVAL)); - retry++; - } - if (docId === undefined) { - error(`无法获取今日日记的 docId`); - return; - } - updateDocReservation(docId, false); -} +/** + * @Deprecated 目前不再使用, 只使用 updateTodayReservation + */ +// export async function initTodayReservation(notebook: Notebook) { +// let todayDiaryPath = notebook.dailynoteHpath; +// let docId; +// let retry = 0; +// const MAX_RETRY = 5; +// const INTERVAL = 2500; +// while (retry < MAX_RETRY) { +// //插件自动创建日记的情况下可能会出现第一次拿不到的情况, 需要重试几次 +// let docs = await getDocsByHpath(todayDiaryPath!, notebook); +// debug(`In initResrv, retry: ${retry}`); +// if (docs[0]?.id !== undefined) { +// docId = docs[0].id; +// break; +// } +// await new Promise(resolve => setTimeout(resolve, INTERVAL)); +// retry++; +// } +// if (docId === undefined) { +// error(`无法获取今日日记的 docId`); +// return; +// } +// updateDocReservation(docId, false); +// } +/** + * 给定笔记本,将今日的预约块插入笔记本的 daily note 中 + * @param notebook + * @param refresh + * @returns + * - boolean, true 代表走完了流程; false 代表没有走完流程(比如日记不存在) + */ export async function updateTodayReservation(notebook: Notebook, refresh: boolean = false) { - let todayDiaryPath = notebook.dailynoteHpath; - let docs = await getDocsByHpath(todayDiaryPath!, notebook); - let docId = docs[0].id; + let docId = notebook?.dailyNoteDocId; //dailyNoteDocId 是在 openDiary 和 createDiary 时动态设置的 + if (!docId) return false; updateDocReservation(docId, refresh); + return true; } +/** + * 给定文档 ID, 插入今日的预约块 + * @param docId + * @param refresh + * @returns + */ export async function updateDocReservation(docId: string, refresh: boolean = false) { let resvBlockIds = reservation.getTodayReservations(); if (resvBlockIds.length == 0) { diff --git a/src/func/style.ts b/src/func/style.ts new file mode 100644 index 0000000..eeb0704 --- /dev/null +++ b/src/func/style.ts @@ -0,0 +1,18 @@ +const DynamicStyleSheetId = 'daily-note-today-dynamic-style-sheet'; + +export const updateStyleSheet = (css: string) => { + let style = document.getElementById(DynamicStyleSheetId); + if (style === null) { + style = document.createElement('style'); + style.id = DynamicStyleSheetId; + document.head.appendChild(style); + } + style.innerHTML = css; +} + +export const removeStyleSheet = () => { + let style = document.getElementById(DynamicStyleSheetId); + if (style !== null) { + style.remove(); + } +} diff --git a/src/global-notebooks.ts b/src/global-notebooks.ts index 035fe25..7b785cb 100644 --- a/src/global-notebooks.ts +++ b/src/global-notebooks.ts @@ -80,7 +80,6 @@ class Notebooks { } else { this.default = this.get(0); } - settings.save(); } } diff --git a/src/index.scss b/src/index.scss index fca84a1..6479c15 100644 --- a/src/index.scss +++ b/src/index.scss @@ -2,4 +2,4 @@ p, li, th, td { font-size: 1rem !important; } -} \ No newline at end of file +} diff --git a/src/index.ts b/src/index.ts index 6e821f0..d727ccc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,19 +7,19 @@ import ShowReserve from './components/dock-reserve.svelte'; import { ToolbarMenuItem } from './components/toolbar-menu'; import { GutterMenu } from './components/gutter-menu'; -import { StartupEventHandler } from './func'; +import { RoutineEventHandler } from './func'; import { updateTodayReservation, reserveBlock, dereserveBlock } from './func/reserve'; +import { updateStyleSheet, removeStyleSheet } from './func'; import { debug, info, setApp, setI18n, setIsMobile, setPlugin, getFocusedBlock } from './utils'; import { settings, reservation } from './global-status'; import notebooks from './global-notebooks'; -// import { ContextMenu } from './components/legacy-menu'; + import { eventBus } from './event-bus'; -import { changelog } from 'sy-plugin-changelog'; +// import { changelog } from 'sy-plugin-changelog'; import "./index.scss"; -import type { TypoDialog } from 'sy-plugin-changelog/dist/utils'; export default class DailyNoteTodayPlugin extends Plugin { @@ -31,13 +31,9 @@ export default class DailyNoteTodayPlugin extends Plugin { toolbarItem: ToolbarMenuItem; - // component_setting: Setting; - // setting_ui: any; - - // menu: ContextMenu; gutterMenu: GutterMenu; - startupHandler: StartupEventHandler; + routineHandler: RoutineEventHandler; async onload() { debug('Plugin load'); @@ -67,11 +63,10 @@ export default class DailyNoteTodayPlugin extends Plugin { eventBus.subscribe(eventBus.EventUpdateAll, () => { this.updateAll() }); - this.startupHandler = new StartupEventHandler(this); - - this.toolbarItem.startMonitorDailyNoteForReservation(); + updateStyleSheet(''); - this.startupHandler.onPluginLoad(); + this.routineHandler = new RoutineEventHandler(this); + this.routineHandler.onPluginLoad(); let end = performance.now(); debug(`启动耗时: ${end - start} ms`); @@ -222,17 +217,17 @@ export default class DailyNoteTodayPlugin extends Plugin { today.toDateString(); let msg = `${this.i18n.NewDay[0]} ${today.toLocaleDateString()} ${this.i18n.NewDay[1]}` - //新的一天,重置同步检查状态 - this.startupHandler.resetSyncCheckStatus(); + //新的一天,重置检查状态 + this.routineHandler.resetStatusFlag(); + this.routineHandler.updateResvIconStyle(); showMessage(msg, 5000, 'info'); } onunload() { debug('Plugin unload') + removeStyleSheet(); this.toolbarItem.release(); - settings.save(); - reservation.save(); if (this.upToDate) { debug(`清理定时器 ${this.upToDate}`); clearTimeout(this.upToDate); diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 6d0bb5f..0aa476d 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -46,6 +46,7 @@ type Notebook = { icon: string; sort: number; closed: boolean; + dailyNoteDocId?: DocumentId; dailynoteSprig?: string; dailynoteHpath?: string; }