mirror of
https://github.com/olcubo/obsidian-cubox.git
synced 2026-07-22 05:43:25 +00:00
tags filter
This commit is contained in:
parent
d441d465db
commit
87edf82f09
5 changed files with 283 additions and 6 deletions
|
|
@ -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;
|
||||
|
|
|
|||
58
src/main.ts
58
src/main.ts
|
|
@ -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`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
215
src/modal/tagSelectModal.ts
Normal 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');
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue