diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 813f4b4..e00930e 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -16,6 +16,7 @@ import { telegramHandler, unknownHandler, zoomHandler, + mediumHandler, } from './platforms'; import { DeepLinkResult } from './types'; import { normalizeUrl } from './utils/normalizeUrl'; @@ -39,6 +40,7 @@ const handlers = [ twitchHandler, telegramHandler, zoomHandler, + mediumHandler, ]; export function generateDeepLink(url: string): DeepLinkResult { const webUrl = normalizeUrl(url); diff --git a/packages/core/src/platforms/index.ts b/packages/core/src/platforms/index.ts index af9e16e..117ecf6 100644 --- a/packages/core/src/platforms/index.ts +++ b/packages/core/src/platforms/index.ts @@ -3,6 +3,7 @@ import { facebookHandler } from './facebook'; import { githubHandler } from './github'; import { instagramHandler } from './instagram'; import { linkedinHandler } from './linkedin'; +import { mediumHandler } from './medium'; import { pinterestHandler } from './pinterest'; import { redditHandler } from './reddit'; import { snapchatHandler } from './snapchat'; @@ -34,4 +35,5 @@ export { twitchHandler, unknownHandler, zoomHandler, + mediumHandler, }; diff --git a/packages/core/src/platforms/medium.ts b/packages/core/src/platforms/medium.ts new file mode 100644 index 0000000..81ed80b --- /dev/null +++ b/packages/core/src/platforms/medium.ts @@ -0,0 +1,61 @@ +import { DeepLinkHandler } from '../types'; + +/** + * Medium URL formats: + * - https://medium.com/@username/article-slug + * - https://medium.com/publication-name/article-slug + * - https://username.medium.com/article-slug + */ +export const mediumHandler: DeepLinkHandler = { + match: (url) => { + const userProfilePattern = /medium\.com\/@([^/?#]+)(?:\/([^/?#]+))?/; + + const publicationPattern = /medium\.com\/([^/@?#]+)\/([^/?#]+)/; + + const customDomainPattern = /([^/]+)\.medium\.com\/([^/?#]+)/; + + let match = url.match(userProfilePattern); + if (match) { + return match as RegExpMatchArray; + } + + match = url.match(publicationPattern); + if (match) { + // Return array with [full match, publication name, article slug] + return match as RegExpMatchArray; + } + + match = url.match(customDomainPattern); + if (match) { + // Return array with: [full match, username, article slug] + return match as RegExpMatchArray; + } + + return null; + }, + build: (webUrl, match) => { + let path = ''; + try { + const urlObj = new URL(webUrl); + path = urlObj.pathname; // "/@username/article-slug" or "/publication/article-slug" + } catch { + const pathMatch = webUrl.match(/(?:medium\.com|\.medium\.com)(\/[^?#]*)/); + path = pathMatch ? pathMatch[1] : '/'; + } + + if (!path.startsWith('/')) { + path = '/' + path; + } + + const iosDeepLink = `medium://${path}`; + + const androidDeepLink = `intent://${path}#Intent;scheme=medium;package=com.medium.reader;S.browser_fallback_url=${encodeURIComponent(webUrl)};end`; + + return { + webUrl, + ios: iosDeepLink, + android: androidDeepLink, + platform: 'medium', + }; + }, +}; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index cea4ac6..7bbcc5e 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -15,6 +15,7 @@ export type Platform = | 'telegram' | 'zoom' | 'substack' + | 'medium' | 'unknown'; export interface DeepLinkResult {