diff --git a/packages/frontend/core/src/components/tags/tags-editor.tsx b/packages/frontend/core/src/components/tags/tags-editor.tsx
index 6bfe4e2d38..e7880f447d 100644
--- a/packages/frontend/core/src/components/tags/tags-editor.tsx
+++ b/packages/frontend/core/src/components/tags/tags-editor.tsx
@@ -27,10 +27,12 @@ export interface TagsEditorProps {
}
export interface TagsInlineEditorProps extends TagsEditorProps {
- placeholder?: string;
+ placeholder?: ReactNode;
className?: string;
readonly?: boolean;
title?: ReactNode; // only used for mobile
+ modalMenu?: boolean;
+ menuClassName?: string;
}
type TagOption = TagLike | { readonly create: true; readonly value: string };
@@ -364,6 +366,8 @@ const DesktopTagsInlineEditor = ({
readonly,
placeholder,
className,
+ modalMenu,
+ menuClassName,
...props
}: TagsInlineEditorProps) => {
const empty = !props.selectedTags || props.selectedTags.length === 0;
@@ -379,11 +383,14 @@ const DesktopTagsInlineEditor = ({
align: 'start',
sideOffset: 0,
avoidCollisions: false,
- className: styles.tagsMenu,
+ className: clsx(styles.tagsMenu, menuClassName),
onClick(e) {
e.stopPropagation();
},
}}
+ rootOptions={{
+ modal: modalMenu,
+ }}
items={}
>
+
+
@@ -174,3 +178,105 @@ const StartImport = ({ onImport }: { onImport: () => void }) => {
);
};
+
+const TagsSetting = () => {
+ const t = useI18n();
+ const tagService = useService(TagService);
+ const readwise = useService(IntegrationService).readwise;
+ const allTags = useLiveData(tagService.tagList.tags$);
+ const tagColors = tagService.tagColors;
+ const tagIds = useLiveData(
+ useMemo(() => readwise.setting$('tags'), [readwise])
+ );
+ const adaptedTags = useLiveData(
+ useMemo(() => {
+ return LiveData.computed(get => {
+ return allTags.map(tag => ({
+ id: tag.id,
+ value: get(tag.value$),
+ color: get(tag.color$),
+ }));
+ });
+ }, [allTags])
+ );
+ const adaptedTagColors = useMemo(() => {
+ return tagColors.map(color => ({
+ id: color[0],
+ value: color[1],
+ name: color[0],
+ }));
+ }, [tagColors]);
+
+ const updateReadwiseTags = useCallback(
+ (tagIds: string[]) => {
+ readwise.updateSetting(
+ 'tags',
+ tagIds.filter(id => !!allTags.some(tag => tag.id === id))
+ );
+ },
+ [allTags, readwise]
+ );
+
+ const onCreateTag = useCallback(
+ (name: string, color: string) => {
+ const tag = tagService.tagList.createTag(name, color);
+ return { id: tag.id, value: tag.value$.value, color: tag.color$.value };
+ },
+ [tagService.tagList]
+ );
+ const onSelectTag = useCallback(
+ (tagId: string) => {
+ updateReadwiseTags([...(tagIds ?? []), tagId]);
+ },
+ [tagIds, updateReadwiseTags]
+ );
+ const onDeselectTag = useCallback(
+ (tagId: string) => {
+ updateReadwiseTags(tagIds?.filter(id => id !== tagId) ?? []);
+ },
+ [tagIds, updateReadwiseTags]
+ );
+ const onDeleteTag = useCallback(
+ (tagId: string) => {
+ tagService.tagList.deleteTag(tagId);
+ updateReadwiseTags(tagIds ?? []);
+ },
+ [tagIds, updateReadwiseTags, tagService.tagList]
+ );
+ const onTagChange = useCallback(
+ (id: string, property: keyof TagLike, value: string) => {
+ if (property === 'value') {
+ tagService.tagList.tagByTagId$(id).value?.rename(value);
+ } else if (property === 'color') {
+ tagService.tagList.tagByTagId$(id).value?.changeColor(value);
+ }
+ },
+ [tagService.tagList]
+ );
+ return (
+ -
+
+ {t['com.affine.integration.readwise.setting.tags-label']()}
+
+
+ {t['com.affine.integration.readwise.setting.tags-placeholder']()}
+
+ }
+ className={styles.tagsEditor}
+ tagMode="inline-tag"
+ tags={adaptedTags}
+ selectedTags={tagIds ?? []}
+ onCreateTag={onCreateTag}
+ onSelectTag={onSelectTag}
+ onDeselectTag={onDeselectTag}
+ tagColors={adaptedTagColors}
+ onTagChange={onTagChange}
+ onDeleteTag={onDeleteTag}
+ modalMenu={true}
+ menuClassName={styles.tagsMenu}
+ />
+
+ );
+};
diff --git a/packages/frontend/core/src/modules/integration/entities/readwise.ts b/packages/frontend/core/src/modules/integration/entities/readwise.ts
index ca262587e4..b60a2693a2 100644
--- a/packages/frontend/core/src/modules/integration/entities/readwise.ts
+++ b/packages/frontend/core/src/modules/integration/entities/readwise.ts
@@ -86,6 +86,7 @@ export class ReadwiseIntegration extends Entity<{ writer: IntegrationWriter }> {
const updateStrategy = this.readwiseStore.getSetting('updateStrategy');
const syncNewHighlights =
this.readwiseStore.getSetting('syncNewHighlights');
+ const tags = this.readwiseStore.getSetting('tags');
const chunks = chunk(highlights, 2);
const total = highlights.length;
let finished = 0;
@@ -120,6 +121,7 @@ export class ReadwiseIntegration extends Entity<{ writer: IntegrationWriter }> {
updateStrategy,
integrationId,
userId,
+ tags,
});
}
finished++;
@@ -144,15 +146,17 @@ export class ReadwiseIntegration extends Entity<{ writer: IntegrationWriter }> {
integrationId: string;
userId: string;
updateStrategy?: ReadwiseConfig['updateStrategy'];
+ tags?: string[];
}
) {
- const { updateStrategy, integrationId } = options;
+ const { updateStrategy, integrationId, tags } = options;
const { text, ...highlightWithoutText } = highlight;
const writtenDocId = await this.writer.writeDoc({
content: text,
title: book.title,
docId,
+ tags,
comment: highlight.note,
updateStrategy: updateStrategy ?? 'append',
});
diff --git a/packages/frontend/core/src/modules/integration/entities/writer.ts b/packages/frontend/core/src/modules/integration/entities/writer.ts
index 3ad501a7bc..5e89c62ca0 100644
--- a/packages/frontend/core/src/modules/integration/entities/writer.ts
+++ b/packages/frontend/core/src/modules/integration/entities/writer.ts
@@ -1,13 +1,17 @@
import { MarkdownTransformer } from '@blocksuite/affine/blocks/root';
import { Entity } from '@toeverything/infra';
+import type { TagService } from '../../tag';
import {
getAFFiNEWorkspaceSchema,
type WorkspaceService,
} from '../../workspace';
export class IntegrationWriter extends Entity {
- constructor(private readonly workspaceService: WorkspaceService) {
+ constructor(
+ private readonly workspaceService: WorkspaceService,
+ private readonly tagService: TagService
+ ) {
super();
}
@@ -32,18 +36,24 @@ export class IntegrationWriter extends Entity {
* Update strategy, default is `override`
*/
updateStrategy?: 'override' | 'append';
+ /**
+ * Tags to apply to the doc
+ */
+ tags?: string[];
}) {
const {
title,
content,
comment,
docId,
+ tags,
updateStrategy = 'override',
} = options;
const workspace = this.workspaceService.workspace;
let markdown = comment ? `${content}\n---\n${comment}` : content;
+ let finalDocId: string;
if (!docId) {
const newDocId = await MarkdownTransformer.importMarkdownToDoc({
collection: workspace.docCollection,
@@ -52,7 +62,8 @@ export class IntegrationWriter extends Entity {
fileName: title,
});
- return newDocId;
+ if (!newDocId) throw new Error('Failed to create a new doc');
+ finalDocId = newDocId;
} else {
const collection = workspace.docCollection;
@@ -88,7 +99,16 @@ export class IntegrationWriter extends Entity {
} else {
throw new Error('Invalid update strategy');
}
- return doc.id;
+ finalDocId = doc.id;
}
+ await this.applyTags(finalDocId, tags);
+ return finalDocId;
+ }
+
+ public async applyTags(docId: string, tags?: string[]) {
+ if (!tags?.length) return;
+ tags.forEach(tag => {
+ this.tagService.tagList.tagByTagId$(tag).value?.tag(docId);
+ });
}
}
diff --git a/packages/frontend/core/src/modules/integration/index.ts b/packages/frontend/core/src/modules/integration/index.ts
index 82106f5749..1d74466920 100644
--- a/packages/frontend/core/src/modules/integration/index.ts
+++ b/packages/frontend/core/src/modules/integration/index.ts
@@ -4,6 +4,7 @@ import { WorkspaceServerService } from '../cloud';
import { WorkspaceDBService } from '../db';
import { DocScope, DocService, DocsService } from '../doc';
import { GlobalState } from '../storage';
+import { TagService } from '../tag';
import { WorkspaceScope, WorkspaceService } from '../workspace';
import { ReadwiseIntegration } from './entities/readwise';
import { ReadwiseCrawler } from './entities/readwise-crawler';
@@ -27,8 +28,8 @@ export function configureIntegrationModule(framework: Framework) {
WorkspaceServerService,
])
.service(IntegrationService)
- .entity(IntegrationWriter, [WorkspaceService])
.entity(ReadwiseCrawler, [ReadwiseStore])
+ .entity(IntegrationWriter, [WorkspaceService, TagService])
.entity(ReadwiseIntegration, [
IntegrationRefStore,
ReadwiseStore,
diff --git a/packages/frontend/core/src/modules/integration/type.ts b/packages/frontend/core/src/modules/integration/type.ts
index 38e95fdc57..0ef9c85fa9 100644
--- a/packages/frontend/core/src/modules/integration/type.ts
+++ b/packages/frontend/core/src/modules/integration/type.ts
@@ -87,6 +87,10 @@ export interface ReadwiseConfig {
* The update strategy
*/
updateStrategy?: 'override' | 'append';
+ /**
+ * Tag id list to be used when creating highlights
+ */
+ tags?: string[];
}
// ===============================
// Zotero
diff --git a/packages/frontend/i18n/src/i18n.gen.ts b/packages/frontend/i18n/src/i18n.gen.ts
index acdd7153d8..301b0bdb01 100644
--- a/packages/frontend/i18n/src/i18n.gen.ts
+++ b/packages/frontend/i18n/src/i18n.gen.ts
@@ -7365,6 +7365,14 @@ export function useAFFiNEI18N(): {
* `Import`
*/
["com.affine.integration.readwise.setting.start-import-button"](): string;
+ /**
+ * `Apply tags to highlight imports`
+ */
+ ["com.affine.integration.readwise.setting.tags-label"](): string;
+ /**
+ * `Click to add tags`
+ */
+ ["com.affine.integration.readwise.setting.tags-placeholder"](): string;
/**
* `Author`
*/
diff --git a/packages/frontend/i18n/src/resources/en.json b/packages/frontend/i18n/src/resources/en.json
index af11c0188b..4c1620bcea 100644
--- a/packages/frontend/i18n/src/resources/en.json
+++ b/packages/frontend/i18n/src/resources/en.json
@@ -1834,6 +1834,8 @@
"com.affine.integration.readwise.setting.start-import-name": "Start Importing",
"com.affine.integration.readwise.setting.start-import-desc": "Using the settings above",
"com.affine.integration.readwise.setting.start-import-button": "Import",
+ "com.affine.integration.readwise.setting.tags-label": "Apply tags to highlight imports",
+ "com.affine.integration.readwise.setting.tags-placeholder": "Click to add tags",
"com.affine.integration.readwise-prop.author": "Author",
"com.affine.integration.readwise-prop.source": "Source",
"com.affine.integration.readwise-prop.created": "Created",