more message view stuff
This commit is contained in:
@@ -28,6 +28,7 @@ class _MessageListPageState extends State<MessageListPage> {
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
//TODO init with state from cache - also allow tho show cache on error
|
||||
_pagingController.addPageRequestListener((pageKey) {
|
||||
_fetchPage(pageKey);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||
import 'package:simplecloudnotifier/api/api_exception.dart';
|
||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||
@@ -10,6 +12,7 @@ import 'package:simplecloudnotifier/models/channel.dart';
|
||||
import 'package:simplecloudnotifier/models/keytoken.dart';
|
||||
import 'package:simplecloudnotifier/models/message.dart';
|
||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||
import 'package:simplecloudnotifier/utils/toaster.dart';
|
||||
import 'package:simplecloudnotifier/utils/ui.dart';
|
||||
|
||||
class MessageViewPage extends StatefulWidget {
|
||||
@@ -23,8 +26,11 @@ class MessageViewPage extends StatefulWidget {
|
||||
|
||||
class _MessageViewPageState extends State<MessageViewPage> {
|
||||
late Future<(Message, ChannelWithSubscription?, KeyToken?)>? mainFuture;
|
||||
(Message, ChannelWithSubscription?, KeyToken?)? mainFutureSnapshot = null;
|
||||
static final _dateFormat = DateFormat('yyyy-MM-dd kk:mm');
|
||||
|
||||
bool _monospaceMode = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -38,7 +44,7 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
||||
|
||||
ChannelWithSubscription? chn = null;
|
||||
try {
|
||||
chn = await APIClient.getChannel(acc, msg.channelID);
|
||||
chn = await APIClient.getChannel(acc, msg.channelID); //TODO getShortChannel (?) -> no perm
|
||||
} on APIException catch (e) {
|
||||
if (e.error == APIError.USER_AUTH_FAILED) {
|
||||
chn = null;
|
||||
@@ -49,7 +55,7 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
||||
|
||||
KeyToken? tok = null;
|
||||
try {
|
||||
tok = await APIClient.getKeyToken(acc, msg.usedKeyID);
|
||||
tok = await APIClient.getKeyToken(acc, msg.usedKeyID); //TODO getShortKeyToken (?) -> no perm
|
||||
} on APIException catch (e) {
|
||||
if (e.error == APIError.USER_AUTH_FAILED) {
|
||||
tok = null;
|
||||
@@ -58,7 +64,15 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
||||
}
|
||||
}
|
||||
|
||||
return (msg, chn, tok);
|
||||
//TODO getShortUser for sender
|
||||
|
||||
//await Future.delayed(const Duration(seconds: 2), () {});
|
||||
|
||||
final r = (msg, chn, tok);
|
||||
|
||||
mainFutureSnapshot = r;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -71,19 +85,18 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
||||
return SCNScaffold(
|
||||
title: 'Message',
|
||||
showSearch: false,
|
||||
//TODO showShare: true
|
||||
showShare: true,
|
||||
onShare: _share,
|
||||
child: FutureBuilder<(Message, ChannelWithSubscription?, KeyToken?)>(
|
||||
future: mainFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
final msg = snapshot.data!.$1;
|
||||
final chn = snapshot.data!.$2;
|
||||
final tok = snapshot.data!.$3;
|
||||
return _buildMessageView(context, msg, chn, tok);
|
||||
final (msg, chn, tok) = snapshot.data!;
|
||||
return _buildMessageView(context, msg, chn, tok, false);
|
||||
} else if (snapshot.hasError) {
|
||||
return Center(child: Text('${snapshot.error}')); //TODO nice error page
|
||||
} else if (!widget.message.trimmed) {
|
||||
return _buildLoadingView(context, widget.message);
|
||||
return _buildMessageView(context, widget.message, null, null, true);
|
||||
} else {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
@@ -92,39 +105,58 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMessageView(BuildContext context, Message message, ChannelWithSubscription? channel, KeyToken? token) {
|
||||
//TODO loading true/false indicator
|
||||
void _share() async {
|
||||
var msg = widget.message;
|
||||
if (mainFutureSnapshot != null) {
|
||||
(msg, _, _) = mainFutureSnapshot!;
|
||||
}
|
||||
|
||||
if (msg.content != null) {
|
||||
final result = await Share.share(msg.content!, subject: msg.title);
|
||||
|
||||
if (result.status == ShareResultStatus.unavailable) {
|
||||
Toaster.error('Error', "Failed to open share dialog");
|
||||
}
|
||||
} else {
|
||||
final result = await Share.share(msg.title);
|
||||
|
||||
if (result.status == ShareResultStatus.unavailable) {
|
||||
Toaster.error('Error', "Failed to open share dialog");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildMessageView(BuildContext context, Message message, ChannelWithSubscription? channel, KeyToken? token, bool loading) {
|
||||
final userAccUserID = context.select<AppAuth, String?>((v) => v.userID);
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(24, 16, 24, 16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
..._buildMessageHeader(context, message, channel, token),
|
||||
..._buildMessageHeader(context, message, channel, token, loading),
|
||||
SizedBox(height: 8),
|
||||
if (message.content != null) ..._buildMessageContent(context, message, channel, token),
|
||||
SizedBox(height: 8),
|
||||
if (message.senderName != null) _buildMetaCard(context, FontAwesomeIcons.solidSignature, 'Sender', [message.senderName!], () => {/*TODO*/}),
|
||||
if (token != null) _buildMetaCard(context, FontAwesomeIcons.solidGearCode, 'KeyToken', [token.keytokenID, token.name], () => {/*TODO*/}),
|
||||
_buildMetaCard(context, FontAwesomeIcons.solidGearCode, 'KeyToken', [message.usedKeyID, if (token != null) token.name], () => {/*TODO*/}),
|
||||
_buildMetaCard(context, FontAwesomeIcons.solidIdCardClip, 'MessageID', [message.messageID, if (message.userMessageID != null) message.userMessageID!], null),
|
||||
if (channel != null) _buildMetaCard(context, FontAwesomeIcons.solidSnake, 'Channel', [message.channelID, channel.channel.displayName], () => {/*TODO*/}),
|
||||
_buildMetaCard(context, FontAwesomeIcons.solidSnake, 'Channel', [message.channelID, channel?.channel.displayName ?? message.channelInternalName], () => {/*TODO*/}),
|
||||
_buildMetaCard(context, FontAwesomeIcons.solidTimer, 'Timestamp', [message.timestamp], null),
|
||||
_buildMetaCard(context, FontAwesomeIcons.solidUser, 'User', ['TODO'], () => {/*TODO*/}), //TODO
|
||||
if (message.senderUserID == userAccUserID) UI.button(text: "Delete Message", onPressed: () {/*TODO*/}, color: Colors.red[900]),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLoadingView(BuildContext context, Message message) {
|
||||
//TODO loading / skeleton use limitdata
|
||||
return SizedBox();
|
||||
}
|
||||
|
||||
String _resolveChannelName(ChannelWithSubscription? channel, Message message) {
|
||||
return channel?.channel.displayName ?? message.channelInternalName;
|
||||
}
|
||||
|
||||
List<Widget> _buildMessageHeader(BuildContext context, Message message, ChannelWithSubscription? channel, KeyToken? token) {
|
||||
List<Widget> _buildMessageHeader(BuildContext context, Message message, ChannelWithSubscription? channel, KeyToken? token, bool loading) {
|
||||
return [
|
||||
Row(
|
||||
children: [
|
||||
@@ -139,7 +171,24 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Text(message.title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
if (!loading) Text(message.title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
if (loading)
|
||||
Stack(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Flexible(child: Text(message.title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold))),
|
||||
SizedBox(height: 20, width: 20),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(child: SizedBox(width: 0)),
|
||||
SizedBox(child: CircularProgressIndicator(), height: 20, width: 20),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -147,22 +196,45 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
||||
return [
|
||||
Row(
|
||||
children: [
|
||||
if (message.priority == 2) FaIcon(FontAwesomeIcons.solidTriangleExclamation, size: 16, color: Colors.red[900]),
|
||||
if (message.priority == 0) FaIcon(FontAwesomeIcons.solidDown, size: 16, color: Colors.lightBlue[900]),
|
||||
Expanded(child: SizedBox()),
|
||||
UI.buttonIconOnly(
|
||||
onPressed: () {/*TODO*/},
|
||||
onPressed: () {
|
||||
Clipboard.setData(new ClipboardData(text: message.content ?? ''));
|
||||
Toaster.info("Clipboard", 'Copied text to Clipboard');
|
||||
},
|
||||
icon: FontAwesomeIcons.copy,
|
||||
),
|
||||
UI.buttonIconOnly(
|
||||
icon: FontAwesomeIcons.lineColumns,
|
||||
onPressed: () {/*TODO*/},
|
||||
icon: _monospaceMode ? FontAwesomeIcons.lineColumns : FontAwesomeIcons.alignLeft,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_monospaceMode = !_monospaceMode;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
UI.box(
|
||||
context: context,
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(message.content ?? ''),
|
||||
),
|
||||
_monospaceMode
|
||||
? UI.box(
|
||||
context: context,
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Text(
|
||||
message.content ?? '',
|
||||
style: TextStyle(fontFamily: "monospace", fontFamilyFallback: <String>["Courier"]),
|
||||
),
|
||||
),
|
||||
borderColor: (message.priority == 2) ? Colors.red[900] : null,
|
||||
)
|
||||
: UI.box(
|
||||
context: context,
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(message.content ?? ''),
|
||||
borderColor: (message.priority == 2) ? Colors.red[900] : null,
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user