tags filter

This commit is contained in:
delphi 2025-03-05 16:20:47 +08:00
parent d441d465db
commit 87edf82f09
5 changed files with 283 additions and 6 deletions

View file

@ -147,6 +147,7 @@ export class CuboxApi {
folderFilter?: string[];
typeFilter?: string[];
statusFilter?: string[];
tagsFilter?: string[];
} = { lastCardId: null }
): Promise<{ articles: CuboxArticle[], hasMore: boolean, lastCardId: string | null }> {
try {
@ -182,6 +183,14 @@ export class CuboxApi {
if (params.statusFilter.includes('annotated')) searchParams.append('isAnnotated', 'true');
}
}
// 添加标签过滤
if (params.tagsFilter && params.tagsFilter.length > 0) {
params.tagsFilter.forEach(tag => {
// 如果是空字符串,表示"No Tags"
searchParams.append('tagId', tag);
});
}
const path = `/c/api/third-party/card/list?${searchParams.toString()}`;
const response = await this.request(path) as ListResponse;

View file

@ -6,6 +6,7 @@ import { FolderSelectModal } from './modal/folderSelectModal';
import { filenameTemplateInstructions, metadataTemplateInstructions, contentTemplateInstructions } from './templateInstructions';
import { TypeSelectModal } from './modal/typeSelectModal';
import { StatusSelectModal } from './modal/statusSelectModal';
import { TagSelectModal } from './modal/tagSelectModal';
interface CuboxSyncSettings {
domain: string; // 可以是 'cubox.cc' | 'cubox.pro' | ''
@ -13,6 +14,7 @@ interface CuboxSyncSettings {
folderFilter: string[];
typeFilter: string[];
statusFilter: string[];
tagsFilter: string[]; // 添加标签过滤
syncFrequency: number;
targetFolder: string;
filenameTemplate: string;
@ -31,6 +33,7 @@ const DEFAULT_SETTINGS: CuboxSyncSettings = {
folderFilter: [],
typeFilter: [],
statusFilter: ['all'],
tagsFilter: [], // 默认为空数组,表示同步所有标签
syncFrequency: 30, // 分钟
targetFolder: 'Cubox',
filenameTemplate: '{{cardTitle}}-{{createDate}}',
@ -199,7 +202,8 @@ export default class CuboxSyncPlugin extends Plugin {
lastCardId: lastCardId,
folderFilter: this.settings.folderFilter,
typeFilter: this.settings.typeFilter,
statusFilter: this.settings.statusFilter
statusFilter: this.settings.statusFilter,
tagsFilter: this.settings.tagsFilter,
}
);
@ -426,7 +430,47 @@ class CuboxSyncSettingTab extends PluginSettingTab {
}
}));
new Setting(containerEl)
.setName('Tag Filter')
.setDesc('Manage Cubox tags to be synced')
.addButton(button => button
.setButtonText(this.getTagFilterButtonText())
.setCta()
.onClick(async () => {
// 确保 API 已初始化
if (!this.plugin.settings.apiKey) {
new Notice('请先设置 API Key');
return;
}
// 显示加载状态
button.setButtonText('Loading tags...');
try {
// 先获取标签数据
const tags = await this.plugin.cuboxApi.getTags();
// 打开标签选择模态框,传入已获取的标签数据
const modal = new TagSelectModal(
this.app,
tags, // 直接传入获取到的标签数据
this.plugin.settings.tagsFilter,
async (selectedTags) => {
this.plugin.settings.tagsFilter = selectedTags;
await this.plugin.saveSettings();
// 更新按钮文本
button.setButtonText(this.getTagFilterButtonText());
}
);
modal.open();
} catch (error) {
console.error('获取标签列表失败:', error);
new Notice('获取标签列表失败,请检查网络连接和 API Key');
// 恢复按钮文本
button.setButtonText(this.getTagFilterButtonText());
}
}));
new Setting(containerEl)
.setName('Type Filter')
.setDesc('Manage Cubox content types to be synced')
@ -695,4 +739,14 @@ class CuboxSyncSettingTab extends PluginSettingTab {
return `已选择 ${statusFilters.length} 个状态`;
}
}
private getTagFilterButtonText(): string {
if (!this.plugin.settings.tagsFilter || this.plugin.settings.tagsFilter.length === 0) {
return 'All Tags';
} else if (this.plugin.settings.tagsFilter.includes('')) {
return 'No Tags';
} else {
return `${this.plugin.settings.tagsFilter.length} Tags Selected`;
}
}
}

View file

@ -11,7 +11,6 @@ export class StatusSelectModal extends Modal {
{ id: 'all', name: 'All Items' },
{ id: 'read', name: 'Already Read Items' },
{ id: 'starred', name: 'Starred Items' },
{ id: 'archived', name: 'Archived Items' },
{ id: 'annotated', name: 'Annotated Items' }
];
private selectedStatuses: Set<string> = new Set();
@ -57,7 +56,7 @@ export class StatusSelectModal extends Modal {
});
// 保存按钮
const saveButton = this.footerEl.createEl('button', { text: '保存', cls: 'mod-cta' });
const saveButton = this.footerEl.createEl('button', { text: '确认', cls: 'mod-cta' });
saveButton.addEventListener('click', () => {
// 返回选中的状态
const selectedIds = Array.from(this.selectedStatuses);

215
src/modal/tagSelectModal.ts Normal file
View file

@ -0,0 +1,215 @@
import { App, Modal, Setting, Notice } from 'obsidian';
import { CuboxTag } from '../cuboxApi';
import { ModalStyleManager } from '../utils/modalStyles';
// 定义虚拟的 ALL 标签 ID 和 NO_TAGS 标签 ID
const ALL_TAGS_ID = 'all_tags';
const NO_TAGS_ID = 'no_tags';
export class TagSelectModal extends Modal {
private selectedTags: Set<string> = new Set();
private onConfirm: (selectedTags: string[]) => void;
private tags: CuboxTag[] = [];
private listEl: HTMLElement;
private footerEl: HTMLElement;
constructor(
app: App,
tags: CuboxTag[],
initialSelectedTags: string[],
onConfirm: (selectedTags: string[]) => void
) {
super(app);
this.tags = tags;
// 初始化已选择的标签
if (initialSelectedTags && initialSelectedTags.length > 0) {
initialSelectedTags.forEach(id => {
// 特殊处理空字符串,将其转换为 NO_TAGS_ID
if (id === '') {
this.selectedTags.add(NO_TAGS_ID);
} else if (id) {
this.selectedTags.add(id);
}
});
} else {
// 如果没有初始选择,默认选中"All Items"
this.selectedTags.add(ALL_TAGS_ID);
this.selectedTags.add(NO_TAGS_ID);
}
this.onConfirm = onConfirm;
}
async onOpen() {
const { contentEl } = this;
// 设置标题
contentEl.createEl('h2', { text: 'Manage Cubox tags to be synced' });
contentEl.addClass('cubox-tag-select-modal');
// 创建固定的容器结构
this.listEl = contentEl.createDiv({ cls: 'tag-list-container' });
this.footerEl = contentEl.createDiv({ cls: 'modal-footer' });
// 创建标签列表
this.createTagList();
// 添加确认和取消按钮
// 取消按钮
const cancelButton = this.footerEl.createEl('button', { text: '取消' });
cancelButton.addEventListener('click', () => {
this.close();
});
// 确认按钮
const confirmButton = this.footerEl.createEl('button', { text: '确认', cls: 'mod-cta' });
confirmButton.addEventListener('click', () => {
// 处理选中的标签
let resultTags: string[] = [];
// 如果选中了ALL_TAGS_ID则传递空数组表示同步所有内容
if (this.selectedTags.has(ALL_TAGS_ID)) {
resultTags = [];
} else {
// 将选中的标签ID添加到结果中
resultTags = Array.from(this.selectedTags);
// 特殊处理 NO_TAGS_ID将其转换为空字符串
if (resultTags.includes(NO_TAGS_ID)) {
// 移除 NO_TAGS_ID
resultTags = resultTags.filter(id => id !== NO_TAGS_ID);
// 添加空字符串表示无标签
resultTags.push('');
}
}
this.onConfirm(resultTags);
this.close();
});
// 添加样式
this.addStyles();
}
private addStyles() {
// 使用通用样式管理器添加样式
ModalStyleManager.addModalStyles(
'cubox-tag-modal-styles',
'cubox-tag-select-modal',
'tag-list-container'
);
}
private createTagList() {
// 清除现有列表
this.listEl.empty();
// 添加"All Items"选项
new Setting(this.listEl)
.setName('All Items')
.addToggle(toggle => toggle
.setValue(this.selectedTags.has(ALL_TAGS_ID))
.onChange(value => {
this.handleTagToggle(ALL_TAGS_ID, value);
this.redraw();
}));
// 添加"No Tags"选项
new Setting(this.listEl)
.setName('No Tags')
.addToggle(toggle => toggle
.setValue(this.selectedTags.has(NO_TAGS_ID))
.onChange(value => {
this.handleTagToggle(NO_TAGS_ID, value);
this.redraw();
}));
// 添加每个标签的选项
this.tags.forEach(tag => {
new Setting(this.listEl)
.setName(tag.nested_name)
.addToggle(toggle => toggle
.setValue(this.isTagSelected(tag.id))
.onChange(value => {
this.handleTagToggle(tag.id, value);
this.redraw();
}));
});
}
private isTagSelected(tagId: string): boolean {
// 如果选中了ALL_TAGS_ID则所有标签都被选中
if (this.selectedTags.has(ALL_TAGS_ID)) {
return true;
}
// 否则检查特定标签是否被选中
return this.selectedTags.has(tagId);
}
private handleTagToggle(tagId: string, isSelected: boolean) {
if (tagId === ALL_TAGS_ID) {
if (isSelected) {
// 如果选择了"All Items"清除其他所有选择只保留ALL_TAGS_ID
this.selectedTags.clear();
this.selectedTags.add(ALL_TAGS_ID);
this.selectedTags.add(NO_TAGS_ID);
} else {
// 如果取消了"All Items"且没有其他选择,选中第一个标签
this.selectedTags.delete(ALL_TAGS_ID);
if (this.selectedTags.size === 0) {
// 如果有标签,选择第一个标签,否则选择"No Tags"
if (this.tags.length > 0) {
this.selectedTags.add(this.tags[0].id);
} else {
this.selectedTags.add(NO_TAGS_ID);
}
}
}
} else {
if (isSelected) {
// 添加新选择的标签
this.selectedTags.add(tagId);
// 检查是否所有标签都被选中,如果是,自动选中"All Items"
if (this.areAllTagsSelected()) {
this.selectedTags.clear();
this.selectedTags.add(ALL_TAGS_ID);
}
} else {
// 如果取消选择某个标签,则取消"All Items"的选择
this.selectedTags.delete(ALL_TAGS_ID);
// 正常移除取消选择的标签
this.selectedTags.delete(tagId);
// 如果没有任何选择,默认选中"All Items"
if (this.selectedTags.size === 0) {
this.selectedTags.add(ALL_TAGS_ID);
}
}
}
}
// 检查是否所有标签都被选中(包括"No Tags"
private areAllTagsSelected(): boolean {
// 如果没有标签返回false
if (this.tags.length === 0) return false;
// 检查每个标签是否都在selectedTags中并且"No Tags"也被选中
return this.tags.every(tag => this.selectedTags.has(tag.id)) &&
this.selectedTags.has(NO_TAGS_ID);
}
private redraw() {
this.createTagList();
}
onClose() {
const { contentEl } = this;
contentEl.empty();
// 使用通用样式管理器移除样式
ModalStyleManager.removeModalStyles('cubox-tag-modal-styles');
}
}

View file

@ -71,13 +71,13 @@ export class TypeSelectModal extends Modal {
const footerEl = contentEl.createDiv({ cls: 'modal-footer' });
// 添加取消按钮
const cancelButton = footerEl.createEl('button', { text: 'Cancel' });
const cancelButton = footerEl.createEl('button', { text: '取消' });
cancelButton.addEventListener('click', () => {
this.close();
});
// 添加保存按钮
const saveButton = footerEl.createEl('button', { text: 'Save', cls: 'mod-cta' });
const saveButton = footerEl.createEl('button', { text: '确认', cls: 'mod-cta' });
saveButton.addEventListener('click', () => {
// 过滤掉"All Items",只保留实际类型
const selectedTypes = Array.from(this.selectedTypes)