feat: add auto sync database list handling and notifications for missing entries

This commit is contained in:
Jiaxin Peng
2025-11-06 16:27:24 +00:00
parent 7661bc94c7
commit ae4488546c
6 changed files with 98 additions and 5 deletions

View File

@@ -86,6 +86,7 @@ export const en = {
CheckConsole: "Check the console for more information \n opt+cmd+i/ctrl+shift+i", CheckConsole: "Check the console for more information \n opt+cmd+i/ctrl+shift+i",
SettingsMigrated: "✨ Plugin settings updated! Auto sync feature added, check plugin settings", SettingsMigrated: "✨ Plugin settings updated! Auto sync feature added, check plugin settings",
AutoSyncNoNotionID: "⚠️ Auto sync skipped: This document has not been synced to Notion, please upload manually first", AutoSyncNoNotionID: "⚠️ Auto sync skipped: This document has not been synced to Notion, please upload manually first",
AutoSyncMissingDatabaseList: "⚠️ Auto sync skipped: Add an \"aytosync-database\" list in the frontmatter to choose databases",
AutoSyncMultipleSync: "🔄 Auto sync: Syncing to {count} database(s)...", AutoSyncMultipleSync: "🔄 Auto sync: Syncing to {count} database(s)...",
AutoSyncFailed: "Auto sync to {database} failed: {error}", AutoSyncFailed: "Auto sync to {database} failed: {error}",
AutoSyncError: "Auto sync failed for {filename}: {error}", AutoSyncError: "Auto sync failed for {filename}: {error}",

View File

@@ -78,6 +78,7 @@ export const ja = {
CheckConsole: "詳細情報を確認するには、コンソールを開いてください \n opt+cmd+i/ctrl+shift+i", CheckConsole: "詳細情報を確認するには、コンソールを開いてください \n opt+cmd+i/ctrl+shift+i",
SettingsMigrated: "✨ プラグイン設定が更新されました!自動同期機能が追加されました。設定を確認してください", SettingsMigrated: "✨ プラグイン設定が更新されました!自動同期機能が追加されました。設定を確認してください",
AutoSyncNoNotionID: "⚠️ 自動同期をスキップ:このドキュメントは Notion に同期されていません。まず手動でアップロードしてください", AutoSyncNoNotionID: "⚠️ 自動同期をスキップ:このドキュメントは Notion に同期されていません。まず手動でアップロードしてください",
AutoSyncMissingDatabaseList: "⚠️ 自動同期をスキップfrontmatter に aytosync-database リストを設定してください",
AutoSyncMultipleSync: "🔄 自動同期:{count} 個のデータベースに同期中...", AutoSyncMultipleSync: "🔄 自動同期:{count} 個のデータベースに同期中...",
AutoSyncFailed: "{database} への自動同期に失敗しました:{error}", AutoSyncFailed: "{database} への自動同期に失敗しました:{error}",
AutoSyncError: "{filename} の自動同期に失敗しました:{error}", AutoSyncError: "{filename} の自動同期に失敗しました:{error}",

View File

@@ -81,6 +81,7 @@ export const zh = {
CheckConsole: "opt+cmd+i/ctrl+shift+i\n打开控制台查看更多信息", CheckConsole: "opt+cmd+i/ctrl+shift+i\n打开控制台查看更多信息",
SettingsMigrated: "✨ 插件设置已更新!新增自动同步功能,请在设置中查看", SettingsMigrated: "✨ 插件设置已更新!新增自动同步功能,请在设置中查看",
AutoSyncNoNotionID: "⚠️ 自动同步跳过:此文档未同步到 Notion请先手动上传", AutoSyncNoNotionID: "⚠️ 自动同步跳过:此文档未同步到 Notion请先手动上传",
AutoSyncMissingDatabaseList: "⚠️ 自动同步跳过:请在 frontmatter 中设置 aytosync-database 列表",
AutoSyncMultipleSync: "🔄 自动同步:正在同步到 {count} 个数据库...", AutoSyncMultipleSync: "🔄 自动同步:正在同步到 {count} 个数据库...",
AutoSyncFailed: "自动同步到 {database} 失败:{error}", AutoSyncFailed: "自动同步到 {database} 失败:{error}",
AutoSyncError: "自动同步 {filename} 失败:{error}", AutoSyncError: "自动同步 {filename} 失败:{error}",

View File

@@ -4,6 +4,7 @@ import { i18nConfig } from "src/lang/I18n";
import ribbonCommands from "src/commands/NotionCommands"; import ribbonCommands from "src/commands/NotionCommands";
import { ObsidianSettingTab, PluginSettings, DEFAULT_SETTINGS, DatabaseDetails } from "src/ui/settingTabs"; import { ObsidianSettingTab, PluginSettings, DEFAULT_SETTINGS, DatabaseDetails } from "src/ui/settingTabs";
import { uploadCommandNext, uploadCommandGeneral, uploadCommandCustom } from "src/upload/uploadCommand"; import { uploadCommandNext, uploadCommandGeneral, uploadCommandCustom } from "src/upload/uploadCommand";
import { AUTO_SYNC_DATABASE_KEY, parseAutoSyncDatabaseList } from "src/utils/frontmatter";
// Remember to rename these classes and interfaces! // Remember to rename these classes and interfaces!
@@ -17,6 +18,7 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
private syncingFiles: Set<string> = new Set(); private syncingFiles: Set<string> = new Set();
private lastFrontmatterCache: Map<string, any> = new Map(); private lastFrontmatterCache: Map<string, any> = new Map();
private lastContentHashCache: Map<string, string> = new Map(); private lastContentHashCache: Map<string, string> = new Map();
private missingAutoSyncNoticeShown: Set<string> = new Set();
async onload() { async onload() {
await this.loadSettings(); await this.loadSettings();
@@ -67,6 +69,7 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
} }
this.lastFrontmatterCache.clear(); this.lastFrontmatterCache.clear();
this.lastContentHashCache.clear(); this.lastContentHashCache.clear();
this.missingAutoSyncNoticeShown.clear();
} }
async loadSettings() { async loadSettings() {
@@ -327,21 +330,50 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
} }
} }
// Find all databases this file belongs to by checking for NotionID-{abName} const autoSyncTargets = parseAutoSyncDatabaseList(frontMatter[AUTO_SYNC_DATABASE_KEY]);
const foundDatabases: Array<{ dbDetails: DatabaseDetails, notionId: string }> = [];
if (autoSyncTargets.length === 0) {
if (!this.missingAutoSyncNoticeShown.has(file.path)) {
new Notice(i18nConfig.AutoSyncMissingDatabaseList, 10000);
this.missingAutoSyncNoticeShown.add(file.path);
}
console.log(`[AutoSync] No auto sync database list found in ${file.path}`);
return;
}
this.missingAutoSyncNoticeShown.delete(file.path);
const dbByShortName = new Map<string, DatabaseDetails>();
for (const key in this.settings.databaseDetails) { for (const key in this.settings.databaseDetails) {
const dbDetails = this.settings.databaseDetails[key]; const dbDetails = this.settings.databaseDetails[key];
const notionIDKey = `NotionID-${dbDetails.abName}`; dbByShortName.set(dbDetails.abName.toLowerCase(), dbDetails);
}
const foundDatabases: Array<{ dbDetails: DatabaseDetails, notionId: string }> = [];
const unresolvedTargets: string[] = [];
for (const target of autoSyncTargets) {
const lookupKey = target.toLowerCase();
const dbDetails = dbByShortName.get(lookupKey);
if (!dbDetails) {
unresolvedTargets.push(target);
continue;
}
const notionIDKey = `NotionID-${dbDetails.abName}`;
if (frontMatter[notionIDKey]) { if (frontMatter[notionIDKey]) {
foundDatabases.push({ foundDatabases.push({
dbDetails: dbDetails, dbDetails,
notionId: String(frontMatter[notionIDKey]) notionId: String(frontMatter[notionIDKey])
}); });
} }
} }
if (unresolvedTargets.length > 0) {
console.log(`[AutoSync] Frontmatter auto sync targets not found in settings: ${unresolvedTargets.join(", ")}`);
}
// If no NotionID found, notify user to upload manually first // If no NotionID found, notify user to upload manually first
if (foundDatabases.length === 0) { if (foundDatabases.length === 0) {
console.log(`[AutoSync] No NotionID found in ${file.path}, skipping auto sync`); console.log(`[AutoSync] No NotionID found in ${file.path}, skipping auto sync`);
@@ -403,4 +435,3 @@ export default class ObsidianSyncNotionPlugin extends Plugin {
} }
} }
} }

View File

@@ -2,6 +2,7 @@ import { App, Notice, TFile } from "obsidian";
import ObsidianSyncNotionPlugin from "../main"; import ObsidianSyncNotionPlugin from "../main";
import { DatabaseDetails } from "../ui/settingTabs"; import { DatabaseDetails } from "../ui/settingTabs";
import { i18nConfig } from "src/lang/I18n"; import { i18nConfig } from "src/lang/I18n";
import { AUTO_SYNC_DATABASE_KEY, ensureAutoSyncDatabaseEntry } from "src/utils/frontmatter";
export async function updateYamlInfo( export async function updateYamlInfo(
yamlContent: string, yamlContent: string,
@@ -33,6 +34,12 @@ export async function updateYamlInfo(
// add new notionID and link // add new notionID and link
yamlContent[notionIDKey] = id; yamlContent[notionIDKey] = id;
(NotionLinkDisplay) ? yamlContent[linkKey] = url : null; (NotionLinkDisplay) ? yamlContent[linkKey] = url : null;
// ensure auto sync database list contains current short name
yamlContent[AUTO_SYNC_DATABASE_KEY] = ensureAutoSyncDatabaseEntry(
yamlContent[AUTO_SYNC_DATABASE_KEY],
abName
);
}); });
try { try {

52
src/utils/frontmatter.ts Normal file
View File

@@ -0,0 +1,52 @@
export const AUTO_SYNC_DATABASE_KEY = "aytosync-database";
function toCandidateList(value: unknown): string[] {
if (Array.isArray(value)) {
return value.map(item => String(item ?? "").trim());
}
if (typeof value === "string") {
if (value.includes(",")) {
return value.split(",").map(item => item.trim());
}
return [value.trim()];
}
return [];
}
export function parseAutoSyncDatabaseList(value: unknown): string[] {
const candidates = toCandidateList(value)
.map(name => name.replace(/^\[|\]$/g, "").trim()) // strip stray brackets
.filter(Boolean);
const seen = new Map<string, string>();
for (const name of candidates) {
const key = name.toLowerCase();
if (!seen.has(key)) {
seen.set(key, name);
}
}
return Array.from(seen.values());
}
export function ensureAutoSyncDatabaseEntry(value: unknown, abName: string): string[] {
const current = parseAutoSyncDatabaseList(value);
const lower = abName.toLowerCase();
let contains = false;
const updated = current.map(name => {
if (name.toLowerCase() === lower) {
contains = true;
return abName;
}
return name;
});
if (!contains) {
updated.push(abName);
}
return updated;
}