| .. | .. |
|---|
| 23 | 23 | import '../services/navigate_notifier.dart'; |
|---|
| 24 | 24 | import '../services/push_service.dart'; |
|---|
| 25 | 25 | import '../theme/app_theme.dart'; |
|---|
| 26 | +import '../services/purchase_service.dart'; |
|---|
| 26 | 27 | import '../widgets/command_bar.dart'; |
|---|
| 27 | 28 | import '../widgets/input_bar.dart'; |
|---|
| 28 | 29 | import '../widgets/message_bubble.dart'; |
|---|
| 30 | +import '../widgets/paywall_banner.dart'; |
|---|
| 29 | 31 | import '../widgets/session_drawer.dart'; |
|---|
| 30 | 32 | import '../widgets/status_dot.dart'; |
|---|
| 31 | 33 | import '../widgets/toast_overlay.dart'; |
|---|
| .. | .. |
|---|
| 1332 | 1334 | _sendCommand('create'); |
|---|
| 1333 | 1335 | } |
|---|
| 1334 | 1336 | |
|---|
| 1337 | + /// Called when the user taps an upgrade CTA in the drawer or paywall banner. |
|---|
| 1338 | + Future<void> _handleUpgrade() async { |
|---|
| 1339 | + await PurchaseService.instance.purchaseFullAccess(); |
|---|
| 1340 | + } |
|---|
| 1341 | + |
|---|
| 1335 | 1342 | void _handleSessionRename(Session session, String newName) { |
|---|
| 1336 | 1343 | _sendCommand('rename', {'sessionId': session.id, 'name': newName}); |
|---|
| 1337 | 1344 | final sessions = ref.read(sessionsProvider); |
|---|
| .. | .. |
|---|
| 1385 | 1392 | |
|---|
| 1386 | 1393 | @override |
|---|
| 1387 | 1394 | Widget build(BuildContext context) { |
|---|
| 1388 | | - final messages = ref.watch(messagesProvider); |
|---|
| 1395 | + final allMessages = ref.watch(messagesProvider); |
|---|
| 1396 | + final isPro = ref.watch(isProProvider); |
|---|
| 1397 | + // Free tier: filter out messages older than 15 minutes on display. |
|---|
| 1398 | + // Storage is unchanged — messages reappear if the user later upgrades. |
|---|
| 1399 | + final messages = isPro |
|---|
| 1400 | + ? allMessages |
|---|
| 1401 | + : allMessages.where((m) { |
|---|
| 1402 | + final ts = DateTime.fromMillisecondsSinceEpoch(m.timestamp); |
|---|
| 1403 | + final age = DateTime.now().difference(ts); |
|---|
| 1404 | + return age <= kFreeTierMessageTtl; |
|---|
| 1405 | + }).toList(); |
|---|
| 1389 | 1406 | final wsStatus = ref.watch(wsStatusProvider); |
|---|
| 1390 | 1407 | final isTyping = ref.watch(isTypingProvider); |
|---|
| 1391 | 1408 | final connectionDetail = ref.watch(connectionDetailProvider); |
|---|
| .. | .. |
|---|
| 1452 | 1469 | sessions: sessions, |
|---|
| 1453 | 1470 | activeSessionId: activeSession?.id, |
|---|
| 1454 | 1471 | unreadCounts: unreadCounts, |
|---|
| 1472 | + isPro: ref.watch(isProProvider), |
|---|
| 1455 | 1473 | onSelect: (s) => _switchSession(s.id), |
|---|
| 1456 | 1474 | onRemove: _handleSessionRemove, |
|---|
| 1457 | 1475 | onRename: _handleSessionRename, |
|---|
| 1458 | 1476 | onReorder: _handleSessionReorder, |
|---|
| 1459 | 1477 | onNewSession: _handleNewSession, |
|---|
| 1460 | 1478 | onRefresh: _refreshSessions, |
|---|
| 1479 | + onUpgrade: _handleUpgrade, |
|---|
| 1461 | 1480 | ), |
|---|
| 1462 | 1481 | body: Column( |
|---|
| 1463 | 1482 | children: [ |
|---|
| 1483 | + const PaywallBanner(), |
|---|
| 1464 | 1484 | Expanded( |
|---|
| 1465 | 1485 | child: ListView.builder( |
|---|
| 1466 | 1486 | controller: _scrollController, |
|---|