import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:provider/provider.dart'; import 'package:simplecloudnotifier/api/api_client.dart'; import 'package:simplecloudnotifier/models/channel.dart'; import 'package:simplecloudnotifier/models/scan_result.dart'; import 'package:simplecloudnotifier/models/subscription.dart'; import 'package:simplecloudnotifier/models/user.dart'; import 'package:simplecloudnotifier/pages/channel_view/channel_view.dart'; import 'package:simplecloudnotifier/state/app_auth.dart'; import 'package:simplecloudnotifier/state/application_log.dart'; import 'package:simplecloudnotifier/utils/navi.dart'; import 'package:simplecloudnotifier/utils/toaster.dart'; import 'package:simplecloudnotifier/utils/ui.dart'; class ChannelScannerResultChannelSubscribe extends StatefulWidget { final ScanResultChannelSubscribe value; const ChannelScannerResultChannelSubscribe({required this.value}) : super(); @override State createState() => _ChannelScannerResultChannelSubscribeState(); } class _ChannelScannerResultChannelSubscribeState extends State { Future<(ChannelPreview, UserPreview)?> _fetchDataFuture; _ChannelScannerResultChannelSubscribeState() : _fetchDataFuture = Future.value(null); // Initial dummy future Subscription? overrideSubscription = null; @override void initState() { super.initState(); final auth = Provider.of(context, listen: false); setState(() { _fetchDataFuture = _fetchData(auth); }); } Future<(ChannelPreview, UserPreview)?> _fetchData(AppAuth auth) async { ChannelPreview? channel = null; try { channel = await APIClient.getChannelPreview(auth, widget.value.channelID); } catch (e, stackTrace) { Toaster.error("Error", 'Failed to fetch channel preview: ${e.toString()}'); ApplicationLog.error('Failed to fetch channel (preview) for ${widget.value.channelID}', trace: stackTrace); return null; } UserPreview? user = null; try { user = await APIClient.getUserPreview(auth, widget.value.ownerUserID); } catch (e, stackTrace) { Toaster.error("Error", 'Failed to fetch user preview: ${e.toString()}'); ApplicationLog.error('Failed to fetch user (preview) for ${widget.value.ownerUserID}', trace: stackTrace); return null; } return (channel, user); } @override Widget build(BuildContext context) { return FutureBuilder<(ChannelPreview, UserPreview)?>( future: _fetchDataFuture, builder: (context, snapshot) { final auth = Provider.of(context, listen: false); if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return Text('Error: ${snapshot.error}'); //TODO better error display } if (snapshot.data == null) { return Column( spacing: 32, children: [ Icon(FontAwesomeIcons.solidTriangleExclamation, size: 64, color: Colors.red[900]), Text("Failed to parse QR", textAlign: TextAlign.center), ], ); } final (channel, user) = snapshot.data!; final sub = overrideSubscription ?? channel.subscription; return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text("SCN Channel", style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 20), textAlign: TextAlign.center), const SizedBox(height: 16), Row( children: [ ConstrainedBox(child: Text("Name: ", style: const TextStyle(fontWeight: FontWeight.bold)), constraints: const BoxConstraints(minWidth: 100)), Expanded(child: SingleChildScrollView(child: Text(channel.displayName), scrollDirection: Axis.horizontal)), ], ), Row( children: [ ConstrainedBox(child: Text("InternalName: ", style: const TextStyle(fontWeight: FontWeight.bold)), constraints: const BoxConstraints(minWidth: 100)), Expanded(child: SingleChildScrollView(child: Text(channel.internalName, style: const TextStyle(fontStyle: FontStyle.italic)), scrollDirection: Axis.horizontal)), ], ), Row( children: [ ConstrainedBox(child: Text("ChannelID: ", style: const TextStyle(fontWeight: FontWeight.bold)), constraints: const BoxConstraints(minWidth: 100)), Expanded(child: SingleChildScrollView(child: Text(channel.channelID, style: const TextStyle(fontStyle: FontStyle.italic)), scrollDirection: Axis.horizontal)), ], ), Row( children: [ ConstrainedBox(child: Text("Messages: ", style: const TextStyle(fontWeight: FontWeight.bold)), constraints: const BoxConstraints(minWidth: 100)), Expanded(child: SingleChildScrollView(child: Text(channel.messagesSent.toString()), scrollDirection: Axis.horizontal)), ], ), if (channel.descriptionName != null && channel.descriptionName!.isNotEmpty) Row( children: [ ConstrainedBox(child: Text("Description:", style: const TextStyle(fontWeight: FontWeight.bold)), constraints: const BoxConstraints(minWidth: 100)), Expanded(child: SingleChildScrollView(child: Text(channel.descriptionName!), scrollDirection: Axis.horizontal)), ], ), const SizedBox(height: 16), Row( children: [ ConstrainedBox(child: Text("Owner:", style: const TextStyle(fontWeight: FontWeight.bold)), constraints: const BoxConstraints(minWidth: 100)), Expanded(child: SingleChildScrollView(child: Text((user.username ?? user.userID) + ((auth.userID != null && auth.userID! == user.userID) ? "\n(you)" : "")), scrollDirection: Axis.horizontal)), ], ), Row( children: [ ConstrainedBox(child: Text("UserID:", style: const TextStyle(fontWeight: FontWeight.bold)), constraints: const BoxConstraints(minWidth: 100)), Expanded(child: SingleChildScrollView(child: Text(user.userID, style: const TextStyle(fontStyle: FontStyle.italic)), scrollDirection: Axis.horizontal)), ], ), const SizedBox(height: 16), Row( children: [ ConstrainedBox(child: Text("Status:", style: const TextStyle(fontWeight: FontWeight.bold)), constraints: const BoxConstraints(minWidth: 100)), Expanded(child: SingleChildScrollView(child: Text(_formatSubscriptionStatus(sub)), scrollDirection: Axis.horizontal)), ], ), const SizedBox(height: 48), if (sub == null) UI.button( text: 'Request Subscription', onPressed: _onSubscribe, color: Theme.of(context).colorScheme.primary, textColor: Theme.of(context).colorScheme.onPrimary, ), if (sub != null && sub.confirmed) UI.button( text: 'Go to channel', onPressed: () { Navi.pushOnRoot(context, () => ChannelViewPage(channelID: widget.value.channelID, preloadedData: null, needsReload: null)); }, color: Theme.of(context).colorScheme.primary, textColor: Theme.of(context).colorScheme.onPrimary, ), ], ); }, ); } void _onSubscribe() async { final auth = Provider.of(context, listen: false); try { var sub = await APIClient.subscribeToChannelbyID(auth, widget.value.channelID, subscribeKey: widget.value.subscribeKey); if (sub.confirmed) { Toaster.success("Success", "Subscription request sent and auto-confirmed"); } else { Toaster.success("Success", "Subscription request sent - pending confirmation"); } setState(() { overrideSubscription = sub; }); } catch (e) { Toaster.error("Error", 'Failed to send subscription-request: ${e.toString()}'); } } String _formatSubscriptionStatus(Subscription? sub) { if (sub == null) { return "Not Subscribed"; } else if (sub.confirmed) { if (sub.active) { return "Already Subscribed"; } else { return "Already Subscribed (inactive)"; } } else { return "Unconfirmed Subscription"; } } }