From ff8c3d1cee6494721244a1a920e057a694057297 Mon Sep 17 00:00:00 2001 From: CatsJuice Date: Tue, 18 Mar 2025 08:13:57 +0000 Subject: [PATCH] feat(core): intilize integration module and basic readwise impl (#10726) close AF-2257, AF-2261, AF-2260, AF-2259 ### Feat - New `Integration` Module - Basic Readwise integration - connect - import - disconnect - Common Integration UI - Common Integration Writer (Transform markdown to AFFiNE Doc) ### Not Implemented > will be implemented in down-stack - delete docs when disconnect - readwise settiing UI - integration property rendering --- .../component/src/ui/loading/loading.tsx | 13 +- .../setting/workspace-setting/index.tsx | 18 +- .../workspace-setting/integration/card.css.ts | 71 +++ .../workspace-setting/integration/card.tsx | 86 ++++ .../integration/index.css.ts | 15 + .../workspace-setting/integration/index.tsx | 26 ++ .../integration/readwise/connect.tsx | 152 ++++++ .../integration/readwise/connected.css.ts | 36 ++ .../integration/readwise/connected.tsx | 85 ++++ .../integration/readwise/import-dialog.css.ts | 197 ++++++++ .../integration/readwise/import-dialog.tsx | 436 ++++++++++++++++++ .../integration/readwise/index.css.ts | 63 +++ .../integration/readwise/index.tsx | 36 ++ .../pages/workspace/all-page/all-page.tsx | 7 + .../core/src/modules/db/schema/schema.ts | 18 +- .../core/src/modules/dialogs/constant.ts | 2 +- .../core/src/modules/feature-flag/constant.ts | 7 + packages/frontend/core/src/modules/index.ts | 2 + .../core/src/modules/integration/constant.ts | 39 ++ .../integration/entities/readwise-crawler.ts | 175 +++++++ .../modules/integration/entities/readwise.ts | 175 +++++++ .../modules/integration/entities/writer.ts | 94 ++++ .../core/src/modules/integration/index.ts | 38 ++ .../services/integration-property.ts | 38 ++ .../integration/services/integration.ts | 19 + .../integration/store/integration-ref.ts | 47 ++ .../src/modules/integration/store/readwise.ts | 84 ++++ .../core/src/modules/integration/type.ts | 87 ++++ .../src/modules/integration/utils/encrypt.ts | 28 ++ .../src/modules/integration/views/icon.tsx | 15 + .../i18n/src/i18n-completenesses.json | 36 +- packages/frontend/i18n/src/i18n.gen.ts | 152 ++++++ packages/frontend/i18n/src/resources/en.json | 36 ++ 33 files changed, 2308 insertions(+), 25 deletions(-) create mode 100644 packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/card.css.ts create mode 100644 packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/card.tsx create mode 100644 packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/index.css.ts create mode 100644 packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/index.tsx create mode 100644 packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/readwise/connect.tsx create mode 100644 packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/readwise/connected.css.ts create mode 100644 packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/readwise/connected.tsx create mode 100644 packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/readwise/import-dialog.css.ts create mode 100644 packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/readwise/import-dialog.tsx create mode 100644 packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/readwise/index.css.ts create mode 100644 packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/readwise/index.tsx create mode 100644 packages/frontend/core/src/modules/integration/constant.ts create mode 100644 packages/frontend/core/src/modules/integration/entities/readwise-crawler.ts create mode 100644 packages/frontend/core/src/modules/integration/entities/readwise.ts create mode 100644 packages/frontend/core/src/modules/integration/entities/writer.ts create mode 100644 packages/frontend/core/src/modules/integration/index.ts create mode 100644 packages/frontend/core/src/modules/integration/services/integration-property.ts create mode 100644 packages/frontend/core/src/modules/integration/services/integration.ts create mode 100644 packages/frontend/core/src/modules/integration/store/integration-ref.ts create mode 100644 packages/frontend/core/src/modules/integration/store/readwise.ts create mode 100644 packages/frontend/core/src/modules/integration/type.ts create mode 100644 packages/frontend/core/src/modules/integration/utils/encrypt.ts create mode 100644 packages/frontend/core/src/modules/integration/views/icon.tsx diff --git a/packages/frontend/component/src/ui/loading/loading.tsx b/packages/frontend/component/src/ui/loading/loading.tsx index 237f5c5fa1..ec0ebeaff7 100644 --- a/packages/frontend/component/src/ui/loading/loading.tsx +++ b/packages/frontend/component/src/ui/loading/loading.tsx @@ -1,3 +1,4 @@ +import { cssVarV2 } from '@toeverything/theme/v2'; import { assignInlineVars } from '@vanilla-extract/dynamic'; import clsx from 'clsx'; @@ -9,19 +10,23 @@ export interface LoadingProps { speed?: number; progress?: number; strokeColor?: string; + strokeWidth?: number; + className?: string; } export const Loading = ({ size, speed = 1.2, progress = 0.2, + strokeWidth = 4, strokeColor, + className, }: LoadingProps) => { // allow `string` such as `16px` | `100%` | `1em` const sizeWithUnit = size ? withUnit(size, 'px') : '16px'; return ( ; case 'workspace:license': return ; + case 'workspace:integrations': + return ; default: return null; } @@ -58,6 +63,11 @@ export const useWorkspaceSettingList = (): SettingSidebarItem[] => { const workspaceService = useService(WorkspaceService); const information = useWorkspaceInfo(workspaceService.workspace); const serverService = useService(ServerService); + const featureFlagService = useService(FeatureFlagService); + + const enableIntegration = useLiveData( + featureFlagService.flags.enable_integration.$ + ); const isSelfhosted = useLiveData( serverService.server.config$.selector( @@ -90,6 +100,12 @@ export const useWorkspaceSettingList = (): SettingSidebarItem[] => { icon: , testId: 'workspace-setting:members', }, + enableIntegration && { + key: 'workspace:integrations', + title: t['com.affine.integration.integrations'](), + icon: , + testId: 'workspace-setting:integrations', + }, { key: 'workspace:storage', title: t['Storage'](), @@ -109,7 +125,7 @@ export const useWorkspaceSettingList = (): SettingSidebarItem[] => { testId: 'workspace-setting:license', }, ].filter((item): item is SettingSidebarItem => !!item); - }, [showBilling, showLicense, t]); + }, [enableIntegration, showBilling, showLicense, t]); return items; }; diff --git a/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/card.css.ts b/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/card.css.ts new file mode 100644 index 0000000000..38df092106 --- /dev/null +++ b/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/card.css.ts @@ -0,0 +1,71 @@ +import { cssVar } from '@toeverything/theme'; +import { cssVarV2 } from '@toeverything/theme/v2'; +import { style } from '@vanilla-extract/css'; + +import { spaceY } from './index.css'; + +export const card = style({ + padding: '8px 12px 12px 12px', + borderRadius: 8, + border: '1px solid ' + cssVarV2.layer.insideBorder.border, + height: 186, + display: 'flex', + flexDirection: 'column', + background: cssVarV2.layer.background.overlayPanel, +}); +export const cardHeader = style({ + display: 'flex', + alignItems: 'center', + gap: 8, + height: 42, + marginBottom: 4, +}); +export const cardIcon = style({ + width: 32, + height: 32, + borderRadius: 4, + background: cssVarV2.integrations.background.iconSolid, + boxShadow: cssVar('buttonShadow'), + border: `0.5px solid ${cssVarV2.layer.insideBorder.border}`, + fontSize: 24, + padding: 4, + lineHeight: 0, +}); +export const cardContent = style([ + spaceY, + { + display: 'flex', + flexDirection: 'column', + gap: 2, + }, +]); +export const cardTitle = style({ + fontSize: 14, + fontWeight: 500, + lineHeight: '22px', + color: cssVarV2.text.primary, +}); +export const cardDesc = style([ + spaceY, + { + fontSize: 12, + lineHeight: '20px', + fontWeight: 400, + color: cssVarV2.text.secondary, + }, +]); +export const cardFooter = style({ + display: 'flex', + alignItems: 'center', + gap: 8, + justifyContent: 'space-between', + selectors: { + '&:not(:empty)': { + marginTop: 8, + height: 28, + }, + }, +}); +export const settingIcon = style({ + color: cssVarV2.icon.secondary, +}); diff --git a/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/card.tsx b/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/card.tsx new file mode 100644 index 0000000000..1aa83adb5a --- /dev/null +++ b/packages/frontend/core/src/desktop/dialogs/setting/workspace-setting/integration/card.tsx @@ -0,0 +1,86 @@ +import { IconButton, type IconButtonProps } from '@affine/component'; +import { SettingsIcon } from '@blocksuite/icons/rc'; +import clsx from 'clsx'; +import type { HTMLAttributes, ReactNode } from 'react'; + +import { + card, + cardContent, + cardDesc, + cardFooter, + cardHeader, + cardIcon, + cardTitle, + settingIcon, +} from './card.css'; +import { spaceX } from './index.css'; + +export const IntegrationCard = ({ + className, + ...props +}: HTMLAttributes) => { + return
  • ; +}; + +export const IntegrationCardIcon = ({ + className, + ...props +}: HTMLAttributes) => { + return
    ; +}; + +export const IntegrationSettingIcon = ({ + className, + ...props +}: IconButtonProps) => { + return ( + } + variant="plain" + {...props} + /> + ); +}; + +export const IntegrationCardHeader = ({ + className, + icon, + onSettingClick, + ...props +}: HTMLAttributes & { + onSettingClick?: () => void; + icon?: ReactNode; +}) => { + return ( +
    + {icon} +
    + +
    + ); +}; + +export const IntegrationCardContent = ({ + className, + title, + desc, + ...props +}: HTMLAttributes & { + title?: string; + desc?: string; +}) => { + return ( +
    +
    {title}
    +
    {desc}
    +
    + ); +}; + +export const IntegrationCardFooter = ({ + className, + ...props +}: HTMLAttributes) => { + return