feat(editor): add embed iframe provider google docs (#10998)

Related [BS-2835](https://linear.app/affine-design/issue/BS-2835/支持更多-embed-iframe-providers)
This commit is contained in:
donteatfriedrice
2025-03-20 04:42:26 +00:00
parent bfe2525b50
commit e83f7eba00
2 changed files with 82 additions and 0 deletions

View File

@@ -0,0 +1,80 @@
import { EmbedIframeConfigExtension } from '../../extension/embed-iframe-config';
import {
type EmbedIframeUrlValidationOptions,
validateEmbedIframeUrl,
} from '../../utils';
const GOOGLE_DOCS_DEFAULT_WIDTH_IN_SURFACE = 800;
const GOOGLE_DOCS_DEFAULT_HEIGHT_IN_SURFACE = 600;
const GOOGLE_DOCS_DEFAULT_WIDTH_PERCENT = 100;
const GOOGLE_DOCS_DEFAULT_HEIGHT_IN_NOTE = 600;
const googleDocsUrlValidationOptions: EmbedIframeUrlValidationOptions = {
protocols: ['https:'],
hostnames: ['docs.google.com'],
};
/**
* Checks if the URL has a valid sharing parameter
* @param parsedUrl Parsed URL object
* @returns Boolean indicating if the URL has a valid sharing parameter
*/
function hasValidSharingParam(parsedUrl: URL): boolean {
const usp = parsedUrl.searchParams.get('usp');
return usp === 'sharing';
}
/**
* Validates if a URL is a valid Google Docs URL
* Valid format: https://docs.google.com/document/d/doc-id/edit?usp=sharing
* @param url The URL to validate
* @param strictMode Whether to strictly validate sharing parameters
* @returns Boolean indicating if the URL is a valid Google Docs URL
*/
function isValidGoogleDocsUrl(url: string, strictMode = true): boolean {
try {
if (!validateEmbedIframeUrl(url, googleDocsUrlValidationOptions)) {
return false;
}
const parsedUrl = new URL(url);
if (strictMode && !hasValidSharingParam(parsedUrl)) {
return false;
}
const pathSegments = parsedUrl.pathname.split('/').filter(Boolean);
return (
pathSegments[0] === 'document' &&
pathSegments[1] === 'd' &&
pathSegments.length >= 3 &&
!!pathSegments[2]
);
} catch (e) {
console.warn('Invalid Google Docs URL:', e);
return false;
}
}
const googleDocsConfig = {
name: 'google-docs',
match: (url: string) => isValidGoogleDocsUrl(url),
buildOEmbedUrl: (url: string) => {
if (!isValidGoogleDocsUrl(url)) {
return undefined;
}
return url;
},
useOEmbedUrlDirectly: true,
options: {
widthInSurface: GOOGLE_DOCS_DEFAULT_WIDTH_IN_SURFACE,
heightInSurface: GOOGLE_DOCS_DEFAULT_HEIGHT_IN_SURFACE,
widthPercent: GOOGLE_DOCS_DEFAULT_WIDTH_PERCENT,
heightInNote: GOOGLE_DOCS_DEFAULT_HEIGHT_IN_NOTE,
allowFullscreen: true,
style: 'border: none; border-radius: 8px;',
},
};
export const GoogleDocsEmbedConfig =
EmbedIframeConfigExtension(googleDocsConfig);

View File

@@ -1,4 +1,5 @@
import { ExcalidrawEmbedConfig } from './excalidraw';
import { GoogleDocsEmbedConfig } from './google-docs';
import { GoogleDriveEmbedConfig } from './google-drive';
import { MiroEmbedConfig } from './miro';
import { SpotifyEmbedConfig } from './spotify';
@@ -8,4 +9,5 @@ export const EmbedIframeConfigExtensions = [
GoogleDriveEmbedConfig,
MiroEmbedConfig,
ExcalidrawEmbedConfig,
GoogleDocsEmbedConfig,
];