packages/feed/docs/planning/mobile/05-native-features.md
All native features are implemented in apps/mobile/src/lib/. They lazy-load Capacitor plugins via dynamic import() and are no-ops on web.
native-init.ts is the single entry point. Called once from the mobile layout on mount. It sets up:
The mobile layout also observes theme changes via MutationObserver on the <html> element's class and data-theme attributes, and calls updateTheme() to keep the status bar in sync.
File: haptics.ts
Wraps Capacitor's @capacitor/haptics plugin:
| Function | Use Case |
|---|---|
tapLight() | Button presses, toggles, selections |
tapMedium() | Successful actions (placing a trade, sending a message) |
tapHeavy() | Significant events (minting an NFT, completing onboarding) |
notifySuccess() | Confirmed trades, successful transactions |
notifyWarning() | Approaching limits, low balance |
notifyError() | Failed transactions, validation errors |
selectionChanged() | Scrolling through lists, picker changes |
All functions are fire-and-forget. No-ops on web.
Integration points (where to call these in components):
tapLight() — Like button, follow button, tab switches, bottom nav tapstapMedium() — Post created, comment submitted, trade placedtapHeavy() — NFT minted, agent created, on-chain registrationnotifySuccess() — Trade confirmed, transaction receiptnotifyError() — Trade failed, insufficient balanceselectionChanged() — Market list scrolling, leaderboard paginationFiles: push-notifications.ts, plus API routes in apps/web/
initPushNotifications() handles:
POST /api/notifications/register-deviceunregisterPushNotifications() removes device tokens on logout via POST /api/notifications/unregister-device.
Device tokens stored in Redis:
push:device:{userId} (hash)platform, token, updatedAtFile: status-bar.ts
| Function | What It Does |
|---|---|
setStatusBarStyle(theme) | Sets light/dark text color. On Android, also sets background color. |
enableEdgeToEdge() | Makes status bar overlay content (Android). iOS does this by default. |
The mobile layout auto-syncs the status bar with the app theme by observing DOM class/attribute changes.
File: deep-links.ts
| Listener | Behavior |
|---|---|
| Android back button | window.history.back() if history exists, otherwise App.minimizeApp() |
| App state change | Logs resume from background (hook for future data refresh logic) |
OAuth deep links (Privy) are handled separately by AppUrlListener — see 04-privy-auth.md.
File: platform.ts
| Function | Returns |
|---|---|
isNativePlatform() | true if running in Capacitor shell |
getPlatform() | 'ios', 'android', 'web', or 'ssr' |
isIOS() | Shorthand |
isAndroid() | Shorthand |
Detection priority:
window.Capacitor.isNativePlatform() (injected by native shell)capacitor:// → iOShttps://localhost + Android user agent → AndroidwebResults are cached after first detection.
Source files:
apps/mobile/resources/icon-only.png — 1024×1024 app icon sourceapps/mobile/resources/icon-foreground.png — Foreground for adaptive iconsCapacitor config:
SplashScreen: {
launchAutoHide: true,
launchShowDuration: 2000,
backgroundColor: '#0a0a0a',
splashFullScreen: true,
splashImmersive: true,
}
Generate all sizes: After npx cap add ios/android:
cd apps/mobile && bun run generate:assets
This uses @capacitor/assets to produce all required icon sizes (mdpi through xxxhdpi for Android, all iOS sizes) and splash screens.
The native init sets up keyboard listeners that track the keyboard height via a CSS variable:
/* Available in any component */
var(--keyboard-height) /* e.g., '300px' when keyboard is open, '0px' when closed */
Use this to adjust layouts when the keyboard opens (e.g., chat input, comment forms).