diff --git a/apps/app/src/components/DistributorBadges.tsx b/apps/app/src/components/DistributorBadges.tsx index 322df716..1a2abb9e 100644 --- a/apps/app/src/components/DistributorBadges.tsx +++ b/apps/app/src/components/DistributorBadges.tsx @@ -1,20 +1,48 @@ import { Badge } from "./ui/badge"; +import { + getDistributorUrl, + getDistributorDisplayName, + type DistributorConfig, +} from "../lib/distributor-urls"; export function DistributorBadges({ distribute, }: { - distribute: { plugin: string }[]; + distribute: DistributorConfig[]; }) { return (
{distribute.map((distributor, index) => { - const pluginName = distributor.plugin.replace("@curatedotfun/", ""); + const displayName = getDistributorDisplayName(distributor); + const url = getDistributorUrl(distributor); + + const badgeContent = ( + + {displayName} + + ); + + // If we have a URL, wrap in a link, otherwise just render the badge + if (url) { + return ( + + {badgeContent} + + ); + } + return ( - {pluginName} + {displayName} ); })} diff --git a/apps/app/src/lib/distributor-urls.ts b/apps/app/src/lib/distributor-urls.ts new file mode 100644 index 00000000..f533011a --- /dev/null +++ b/apps/app/src/lib/distributor-urls.ts @@ -0,0 +1,261 @@ +/** + * Distributor URL utility functions + * + * This module provides functions to generate appropriate URLs for different + * distributor types based on their configuration. + */ + +export interface DistributorConfig { + plugin: string; + config?: Record; +} + +/** + * Checks if a value is a template variable (contains {{ }}) + */ +function isTemplateVariable(value: unknown): boolean { + return typeof value === "string" && value.includes("{{"); +} + +/** + * Safely gets a string value from config, returning null if it's a template + */ +function getConfigString( + config: Record | undefined, + key: string, +): string | null { + const value = config?.[key]; + if (typeof value === "string" && !isTemplateVariable(value)) { + return value; + } + return null; +} + +/** + * Generate URLs for RSS distributor + */ +function getRssUrl(config: Record | undefined): string | null { + // RSS distributors have a serviceUrl in their config that points to the RSS feed + const serviceUrl = getConfigString(config, "serviceUrl"); + return serviceUrl; +} + +/** + * Generate URLs for Twitter distributor + */ +function getTwitterUrl(config: Record | undefined): string { + // Twitter links to the specific profile if available + const username = + getConfigString(config, "username") || getConfigString(config, "handle"); + if (username) { + return `https://twitter.com/${username.replace("@", "")}`; + } + return "https://twitter.com"; +} + +/** + * Generate URLs for Telegram distributor + */ +function getTelegramUrl(config: Record | undefined): string { + // Telegram links to the channel if chatId is available + const chatId = + getConfigString(config, "chatId") || getConfigString(config, "channelId"); + if (chatId) { + const cleanChatId = chatId.replace("@", ""); + return `https://t.me/${cleanChatId}`; + } + return "https://telegram.org"; +} + +/** + * Generate URLs for Notion distributor + */ +function getNotionUrl(config: Record | undefined): string { + // Notion links to the table/database if available + const databaseId = getConfigString(config, "databaseId"); + if (databaseId) { + return `https://notion.so/${databaseId}`; + } + return "https://notion.so"; +} + +/** + * Generate URLs for Crosspost distributor + */ +function getCrosspostUrl(config: Record | undefined): string { + // Crosspost links to the Open Crosspost platform + // Could potentially link to specific account if signerId is available + const signerId = getConfigString(config, "signerId"); + if (signerId) { + return `https://near.social/mob.near/widget/MainPage.N.Profile.Page?accountId=${signerId}`; + } + return "https://opencrosspost.com"; +} + +/** + * Generate URLs for Discord distributor + */ +function getDiscordUrl(config: Record | undefined): string { + // Discord links to the specific channel if available + const channelId = getConfigString(config, "channelId"); + if (channelId) { + // Discord channel URLs format: https://discord.com/channels/serverId/channelId + // Since we don't have serverId, we use @me which works for direct channel links + return `https://discord.com/channels/@me/${channelId}`; + } + return "https://discord.com"; +} + +/** + * Generate URLs for NEAR Social distributor + */ +function getNearSocialUrl(config: Record | undefined): string { + // NEAR Social links to the specific account if available + const accountId = getConfigString(config, "accountId"); + if (accountId) { + return `https://near.social/mob.near/widget/MainPage.N.Profile.Page?accountId=${accountId}`; + } + return "https://near.social"; +} + +/** + * Generate URLs for Supabase distributor + */ +function getSupabaseUrl(config: Record | undefined): string { + // Supabase links to the project dashboard if URL is available + const url = getConfigString(config, "url"); + if (url) { + // Extract project reference from Supabase URL + const match = url.match(/https:\/\/([^.]+)\.supabase\.co/); + if (match) { + const projectRef = match[1]; + return `https://supabase.com/dashboard/project/${projectRef}`; + } + } + return "https://supabase.com"; +} + +/** + * Get the clean plugin name without the @curatedotfun/ prefix + */ +function getPluginName(plugin: string): string { + return plugin.replace("@curatedotfun/", ""); +} + +/** + * Main function to get distributor URL based on plugin type and configuration + */ +export function getDistributorUrl( + distributor: DistributorConfig, +): string | null { + const pluginName = getPluginName(distributor.plugin); + + switch (pluginName) { + case "rss": + return getRssUrl(distributor.config); + + case "twitter": + case "twitter-distributor": + return getTwitterUrl(distributor.config); + + case "telegram": + case "telegram-distributor": + return getTelegramUrl(distributor.config); + + case "notion": + case "notion-distributor": + return getNotionUrl(distributor.config); + + case "crosspost": + case "crosspost-distributor": + return getCrosspostUrl(distributor.config); + + case "discord": + case "discord-distributor": + return getDiscordUrl(distributor.config); + + case "near-social": + case "near-social-distributor": + case "nearsocial": + return getNearSocialUrl(distributor.config); + + case "supabase": + case "supabase-distributor": + return getSupabaseUrl(distributor.config); + + default: + // For unknown distributors, return null to show badge without link + return null; + } +} + +/** + * Get a display-friendly name for the distributor + */ +export function getDistributorDisplayName( + distributor: DistributorConfig, +): string { + const pluginName = getPluginName(distributor.plugin); + + // Convert plugin names to display names + switch (pluginName) { + case "near-social": + case "nearsocial": + return "NEAR Social"; + case "rss": + return "RSS"; + default: + // Capitalize first letter for other distributors + return pluginName.charAt(0).toUpperCase() + pluginName.slice(1); + } +} + +/** + * Check if a distributor has a functional URL (not just a fallback) + */ +export function hasSpecificUrl(distributor: DistributorConfig): boolean { + const pluginName = getPluginName(distributor.plugin); + const config = distributor.config; + + switch (pluginName) { + case "rss": + return !!getConfigString(config, "serviceUrl"); + + case "twitter": + case "twitter-distributor": + return !!( + getConfigString(config, "username") || getConfigString(config, "handle") + ); + + case "telegram": + case "telegram-distributor": + return !!( + getConfigString(config, "chatId") || + getConfigString(config, "channelId") + ); + + case "notion": + case "notion-distributor": + return !!getConfigString(config, "databaseId"); + + case "crosspost": + case "crosspost-distributor": + return !!getConfigString(config, "signerId"); + + case "discord": + case "discord-distributor": + return !!getConfigString(config, "channelId"); + + case "near-social": + case "near-social-distributor": + case "nearsocial": + return !!getConfigString(config, "accountId"); + + case "supabase": + case "supabase-distributor": + return !!getConfigString(config, "url"); + + default: + return false; + } +}