12 KiB
Share Feature Architecture
Feature: codebase/features/share/
Import alias: @platform/share
Purpose: Unified social sharing methodology for the Lilith Platform.
Scope
The share feature consolidates all social sharing concerns into one importable module:
- SEO metadata management — OG tags, Twitter cards, canonical URLs, JSON-LD structured data
- Social share buttons — WhatsApp, Telegram, Twitter/X, Facebook, LinkedIn, Reddit, Pinterest, Email, Copy to Clipboard
- Web Share API — Native mobile/desktop share sheet with fallback to button grid
- Share URL construction — Platform-specific URL encoding with UTM parameters
- Share analytics — Track which platform, what content, completion status
What share is NOT
- Not a URL shortener — That's the
linkyfeature (@platform/linky) - Not SEO content generation — That stays in the
seofeature (ML pipeline, webmap router) - Not link management — No CRUD for user-owned links (that's
linky) - Not a linktree/bio page — That's the
portalfeature consuminglinky
Directory Structure
codebase/features/share/
├── services.yaml # Service registry definition
├── shared/ # @platform/share exports
│ ├── package.json
│ ├── tsconfig.json
│ └── src/
│ ├── index.ts # Barrel export
│ ├── types/
│ │ ├── index.ts
│ │ ├── meta.ts # PageMetaConfig, StructuredDataObject, SchemaType
│ │ ├── share.ts # ShareContent, ShareOptions, ShareResult
│ │ ├── analytics.ts # ShareEventPayload, ShareAnalyticsSummary
│ │ └── enums.ts # SharePlatform, ShareContentType
│ ├── constants/
│ │ ├── index.ts
│ │ └── platforms.ts # URL templates, brand colors per platform
│ └── utils/
│ ├── index.ts
│ ├── share-url.ts # buildShareUrl(), appendUtmParams()
│ └── structured-data.ts # Schema.org factory functions
├── frontend-public/ # React hooks + components
│ ├── package.json
│ ├── tsconfig.json
│ ├── vite.config.ts
│ └── src/
│ ├── index.ts
│ ├── hooks/
│ │ ├── usePageMeta.ts # Unified page metadata hook
│ │ ├── useShare.ts # Share action hook (Web Share API + fallback)
│ │ ├── useStructuredData.ts # Standalone JSON-LD injection
│ │ ├── useShareAnalytics.ts # Share event tracking
│ │ └── useCopyToClipboard.ts
│ ├── components/
│ │ ├── ShareButtons.tsx # General-purpose share button grid
│ │ ├── ShareSheet.tsx # Mobile bottom sheet share UI
│ │ ├── SharePreview.tsx # OG card preview component
│ │ └── icons/ # Platform SVG icon components
│ └── styles/
│ └── share-buttons.styles.ts
├── backend-api/ # NestJS share analytics service
│ └── src/
│ ├── main.ts
│ ├── app.module.ts
│ ├── entities/
│ │ └── share-event.entity.ts
│ ├── modules/
│ │ ├── health/
│ │ ├── ingestion/ # POST /share/track
│ │ └── analytics/ # GET /share/analytics
│ └── processors/
│ └── share-rollup.processor.ts
└── docs/
Shared Module Types
SharePlatform enum
enum SharePlatform {
WHATSAPP = 'whatsapp',
TELEGRAM = 'telegram',
TWITTER = 'twitter',
FACEBOOK = 'facebook',
LINKEDIN = 'linkedin',
REDDIT = 'reddit',
PINTEREST = 'pinterest',
EMAIL = 'email',
COPY = 'copy',
NATIVE = 'native', // Web Share API
}
ShareContentType enum
enum ShareContentType {
PROFILE = 'profile',
LISTING = 'listing',
PAGE = 'page',
INVITE = 'invite',
CONTENT = 'content',
}
PageMetaConfig
Unifies three existing implementations:
marketplace/frontend-public/src/hooks/usePageMeta.ts(best: cleanup, JSON-LD, OG+Twitter)landing/frontend-public/src/components/SEOHead.tsx(i18n-driven, keywords)seo/frontend-public/src/components/SEOHead.tsx(simplest)
interface PageMetaConfig {
title?: string; // Suffixed with " | {brandName}"
description?: string; // meta description + og:description
keywords?: string[]; // meta keywords
path?: string; // Canonical URL path (defaults to location.pathname)
ogImage?: string; // og:image (defaults to domain OG image)
ogType?: string; // og:type (defaults to 'website')
twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player';
twitterSite?: string; // @handle
structuredData?: StructuredDataObject | StructuredDataObject[];
robots?: string; // robots directive
alternateLanguages?: Record<string, string>; // hreflang
rawTitle?: boolean; // Disable brand suffix
}
DeploymentConfigLike
Minimal interface so any feature can provide domain/brand info without coupling to a specific config implementation:
interface DeploymentConfigLike {
domain: string;
branding: {
displayName: string;
tagline?: string;
};
twitterHandle?: string;
}
ShareContent
interface ShareContent {
url: string; // URL to share (before linky wrapping)
title: string; // Share headline
text?: string; // Share body text
imageUrl?: string; // Image for Pinterest etc.
hashtags?: string[]; // Twitter/Facebook hashtags
}
ShareOptions
interface ShareOptions {
content: ShareContent;
platform: SharePlatform;
contentType: ShareContentType;
contentId?: string;
utmCampaign?: string; // Defaults to 'social_share'
utmMedium?: string; // Defaults to platform name
useLinky?: boolean; // Generate linky tracking URL
}
Platform URL Templates
| Platform | Template |
|---|---|
https://wa.me/?text={text}%20{url} |
|
| Telegram | https://t.me/share/url?url={url}&text={text} |
| Twitter/X | https://twitter.com/intent/tweet?url={url}&text={text}&hashtags={hashtags} |
https://www.facebook.com/sharer/sharer.php?u={url} |
|
https://www.linkedin.com/sharing/share-offsite/?url={url} |
|
https://www.reddit.com/submit?url={url}&title={title} |
|
https://pinterest.com/pin/create/button/?url={url}&description={text}&media={image} |
|
mailto:?subject={title}&body={text}%0A%0A{url} |
UTM Parameter Strategy
All share URLs get UTM parameters appended before platform encoding:
utm_source=lilith
utm_medium={platform} (e.g., twitter, whatsapp, copy)
utm_campaign={campaign} (defaults to 'social_share')
utm_content={contentId} (optional, for attribution)
Share Analytics Data Model
ShareEvent entity
share_events table:
id UUID PRIMARY KEY
timestamp TIMESTAMPTZ (indexed)
platform VARCHAR(20) (indexed) — SharePlatform value
content_type VARCHAR(30) (indexed) — ShareContentType value
content_id VARCHAR(255) (indexed, nullable)
shared_url TEXT
source_domain VARCHAR(255) (indexed)
used_native BOOLEAN
linky_url TEXT (nullable)
session_id VARCHAR(255) (indexed)
user_id UUID (indexed, nullable)
metadata JSONB
API endpoints
POST /share/track — Ingest share event
GET /share/analytics — Aggregated summary (query: period, domain, contentType)
GET /share/analytics/:contentId — Per-content share stats
GET /health — Health check
Platform-analytics integration
The ingestion service also emits a SHARE EngagementMetric to the existing platform-analytics system, enabling share data to appear alongside views, clicks, and other engagement metrics in the provider dashboard.
Structured Data (JSON-LD)
Migrated from marketplace/frontend-public/src/utils/structuredData.ts. Factory functions for Schema.org types:
| Function | Schema Type | Use Case |
|---|---|---|
createOrganizationSchema() |
Organization | Brand identity, social profiles |
createWebSiteSchema() |
WebSite | Site entity with search action |
createBreadcrumbListSchema() |
BreadcrumbList | Navigation hierarchy |
createServiceSchema() |
Service | Creator service profiles |
createProfilePageSchema() |
ProfilePage | Individual profile pages |
createWebPageSchema() |
WebPage | Generic informational pages |
serializeStructuredData() |
— | JSON-LD serialization |
validateStructuredData() |
— | Validation before injection |
Frontend Hooks
usePageMeta(config, deployment)
The unified page metadata hook. Sets document.title, canonical URL, OG tags, Twitter cards, meta description, keywords, robots, hreflang, and JSON-LD. Cleans up on unmount.
useShare(options)
Returns shareTo(platform), shareNative(), canShareNative, availablePlatforms, isSharing. Detects Web Share API availability, constructs platform-specific URLs, fires analytics.
useStructuredData(data)
Standalone JSON-LD injection/cleanup. For features that need structured data without full meta management.
useCopyToClipboard()
Returns copy(text), copied, reset(). Clipboard API with fallback for older browsers. Auto-resets after 2 seconds.
useShareAnalytics()
Fires share events to the backend-api ingestion endpoint.
Frontend Components
ShareButtons
General-purpose share button grid. Accepts content, contentType, platforms (filter), compact, direction, preferNative, onShare. Uses useShare internally.
ShareSheet
Mobile-first bottom sheet. Contains SharePreview at top, ShareButtons below. Slides up with backdrop.
SharePreview
Visual OG card mockup. Pure presentation — renders from provided props (url, title, description, imageUrl, domain).
Beacon Integration
When useLinky: true is passed to share options, the share hook calls @platform/linky to generate a trackable short URL before constructing the platform share URL. This enables click tracking on shared links.
User clicks "Share to Twitter"
→ share constructs content URL with UTM params
→ (if useLinky) calls linky API to generate short URL
→ builds Twitter intent URL with short URL
→ opens in new window
→ fires share analytics event
Existing Implementations Being Consolidated
| File | Lines | Capability |
|---|---|---|
marketplace/.../usePageMeta.ts |
179 | OG, Twitter, canonical, JSON-LD, cleanup |
marketplace/.../structuredData.ts |
470 | 6 Schema.org factories |
marketplace/.../structuredData.d.ts |
276 | Type definitions |
marketplace/.../InvitationShareButtons.tsx |
246 | 4 share platforms |
landing/.../SEOHead.tsx |
110 | i18n-driven meta tags |
seo/.../SEOHead.tsx |
48 | Basic meta tags |
| Total | 1,329 | Scattered, duplicated |
These files remain in place until @platform/share is proven working. Migration is a separate task.