335 lines
10 KiB
Dart
Raw Normal View History

2024-06-02 17:09:57 +02:00
import 'package:flutter/material.dart';
2025-04-13 01:51:52 +02:00
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
2024-06-02 17:09:57 +02:00
import 'package:provider/provider.dart';
import 'package:qr_flutter/qr_flutter.dart';
2025-04-13 01:51:52 +02:00
import 'package:simplecloudnotifier/api/api_client.dart';
2024-06-02 17:09:57 +02:00
import 'package:simplecloudnotifier/state/application_log.dart';
2025-04-13 01:51:52 +02:00
import 'package:simplecloudnotifier/state/globals.dart';
2025-04-13 00:17:06 +02:00
import 'package:simplecloudnotifier/utils/toaster.dart';
2025-04-13 01:51:52 +02:00
import 'package:simplecloudnotifier/utils/ui.dart';
2024-06-02 17:09:57 +02:00
import 'package:url_launcher/url_launcher.dart';
import 'package:simplecloudnotifier/state/app_auth.dart';
class SendRootPage extends StatefulWidget {
2025-04-13 01:51:52 +02:00
const SendRootPage({super.key, required this.isVisiblePage});
final bool isVisiblePage;
2024-06-02 17:09:57 +02:00
@override
State<SendRootPage> createState() => _SendRootPageState();
}
class _SendRootPageState extends State<SendRootPage> {
late TextEditingController _msgTitle;
late TextEditingController _msgContent;
2025-04-13 01:51:52 +02:00
late TextEditingController _channelName;
late TextEditingController _senderName;
int _priority = 0;
bool _expanded = false;
2024-06-02 17:09:57 +02:00
@override
void initState() {
super.initState();
_msgTitle = TextEditingController();
_msgContent = TextEditingController();
2025-04-13 01:51:52 +02:00
_channelName = TextEditingController();
_senderName = TextEditingController();
2024-06-02 17:09:57 +02:00
}
@override
void dispose() {
_msgTitle.dispose();
_msgContent.dispose();
2025-04-13 01:51:52 +02:00
_channelName.dispose();
_senderName.dispose();
2024-06-02 17:09:57 +02:00
super.dispose();
}
@override
Widget build(BuildContext context) {
return Consumer<AppAuth>(
builder: (context, acc, child) {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
2025-04-13 01:51:52 +02:00
child: _expanded ? _buildExpanded(context, acc) : _buildSimple(context, acc),
2024-06-02 17:09:57 +02:00
),
);
},
);
}
2025-04-13 01:51:52 +02:00
Widget _buildSimple(BuildContext context, AppAuth acc) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_buildQRCode(context, acc),
const SizedBox(height: 16),
FractionallySizedBox(
widthFactor: 1.0,
child: TextField(
controller: _msgTitle,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Title',
),
),
),
const SizedBox(height: 16),
FractionallySizedBox(
widthFactor: 1.0,
child: TextField(
controller: _msgContent,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Text',
),
minLines: 2,
maxLines: null,
keyboardType: TextInputType.multiline,
),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: UI.button(
text: 'Send',
onPressed: () {
_sendSimple(acc);
},
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary,
),
),
const SizedBox(width: 8),
UI.buttonIconOnly(
icon: FontAwesomeIcons.layerPlus,
onPressed: _openExpanded,
square: true,
color: Theme.of(context).colorScheme.secondary,
iconColor: Theme.of(context).colorScheme.onSecondary,
),
],
),
const SizedBox(height: 32),
],
);
}
Widget _buildExpanded(BuildContext context, AppAuth acc) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FractionallySizedBox(
widthFactor: 1.0,
child: TextField(
controller: _channelName,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Channel',
),
),
),
const SizedBox(height: 16),
FractionallySizedBox(
widthFactor: 1.0,
child: TextField(
controller: _msgTitle,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Title',
),
),
),
const SizedBox(height: 16),
FractionallySizedBox(
widthFactor: 1.0,
child: TextField(
controller: _senderName,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Sender',
2025-04-13 01:51:52 +02:00
),
),
),
const SizedBox(height: 16),
SegmentedButton<int>(
showSelectedIcon: false,
segments: const <ButtonSegment<int>>[
ButtonSegment<int>(value: 0, label: Text('Low Priority')),
ButtonSegment<int>(value: 1, label: Text('Normal')),
ButtonSegment<int>(value: 2, label: Text('High Priority')),
],
selected: {_priority},
onSelectionChanged: (Set<int> newSelection) {
setState(() {
_priority = newSelection.isEmpty ? 1 : newSelection.first;
});
},
),
const SizedBox(height: 16),
FractionallySizedBox(
widthFactor: 1.0,
child: TextField(
controller: _msgContent,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Text',
),
minLines: 6,
maxLines: null,
keyboardType: TextInputType.multiline,
),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: UI.button(
text: 'Send',
onPressed: () {
_sendExpanded(acc);
},
color: Theme.of(context).colorScheme.primary,
textColor: Theme.of(context).colorScheme.onPrimary,
),
),
const SizedBox(width: 8),
UI.buttonIconOnly(
icon: FontAwesomeIcons.squareDashed,
onPressed: _closeExpanded,
square: true,
color: Theme.of(context).colorScheme.secondary,
iconColor: Theme.of(context).colorScheme.onSecondary,
),
],
),
const SizedBox(height: 32),
],
);
2024-06-02 17:09:57 +02:00
}
Widget _buildQRCode(BuildContext context, AppAuth acc) {
if (!acc.isAuth()) {
return const Placeholder();
}
return FutureBuilder(
future: acc.loadUser(force: false),
builder: ((context, snapshot) {
2025-04-13 01:51:52 +02:00
if (snapshot.connectionState == ConnectionState.active || snapshot.connectionState == ConnectionState.waiting) {
return const SizedBox(
width: 300.0,
height: 300.0,
child: Center(child: CircularProgressIndicator()),
2024-06-02 17:09:57 +02:00
);
}
2025-04-13 01:51:52 +02:00
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}'); //TODO better error display
}
if (snapshot.connectionState != ConnectionState.done) {
return Text('...'); //?
}
var url = (acc.tokenSend == null) ? 'https://simplecloudnotifier.de?preset_user_id=${acc.userID}' : 'https://simplecloudnotifier.de?preset_user_id=${acc.userID}&preset_user_key=${acc.tokenSend}';
return GestureDetector(
onTap: () {
_openWeb(url);
},
child: QrImageView(
data: url,
version: QrVersions.auto,
size: 300.0,
eyeStyle: QrEyeStyle(
eyeShape: QrEyeShape.square,
color: Theme.of(context).textTheme.bodyLarge?.color,
),
dataModuleStyle: QrDataModuleStyle(
dataModuleShape: QrDataModuleShape.square,
color: Theme.of(context).textTheme.bodyLarge?.color,
),
),
2024-06-02 17:09:57 +02:00
);
}),
);
}
2025-04-13 01:51:52 +02:00
void _sendSimple(AppAuth acc) async {
if (!acc.isAuth()) {
Toaster.error("Error", 'Must be logged in to send messages');
return;
}
try {
await APIClient.sendMessage(acc.userID!, acc.tokenSend!, _msgContent.text);
Toaster.success("Success", 'Message sent');
setState(() {
_msgTitle.clear();
_msgContent.clear();
});
} catch (e, stackTrace) {
Toaster.error("Error", 'Failed to send message: ${e.toString()}');
ApplicationLog.error('Failed to send message', trace: stackTrace);
}
}
void _sendExpanded(AppAuth acc) async {
if (!acc.isAuth()) {
Toaster.error("Error", 'Must be logged in to send messages');
return;
}
try {
await APIClient.sendMessage(acc.userID!, acc.tokenSend!, _msgContent.text, channel: _channelName.text, senderName: _senderName.text, priority: _priority);
Toaster.success("Success", 'Message sent');
setState(() {
_msgTitle.clear();
_msgContent.clear();
});
} catch (e, stackTrace) {
Toaster.error("Error", 'Failed to send message: ${e.toString()}');
ApplicationLog.error('Failed to send message', trace: stackTrace);
}
}
2024-06-02 17:09:57 +02:00
void _openWeb(String url) async {
try {
final Uri uri = Uri.parse(url);
2024-06-15 12:37:38 +02:00
ApplicationLog.debug('Opening URL: [ ${uri.toString()} ]');
2024-06-02 17:09:57 +02:00
if (await canLaunchUrl(uri)) {
await launchUrl(uri);
} else {
2025-04-13 00:17:06 +02:00
Toaster.error("Error", 'Cannot open URL on this system');
2024-06-02 17:09:57 +02:00
}
} catch (exc, trace) {
ApplicationLog.error('Failed to open URL: ' + exc.toString(), additional: 'URL: ${url}', trace: trace);
}
}
2025-04-13 01:51:52 +02:00
void _closeExpanded() {
setState(() {
_expanded = false;
_channelName.clear();
_priority = 1;
_senderName.clear();
});
}
void _openExpanded() {
final userAcc = Provider.of<AppAuth>(context, listen: false);
setState(() {
_expanded = true;
_channelName.text = userAcc.getUserOrNull()?.defaultChannel ?? 'main';
_priority = 1;
_senderName.text = Globals().deviceName;
});
}
2024-06-02 17:09:57 +02:00
}