2024-05-21 23:20:34 +02:00
import ' package:flutter/material.dart ' ;
2024-06-08 20:01:23 +02:00
import ' package:flutter/services.dart ' ;
2024-06-07 23:44:32 +02:00
import ' package:font_awesome_flutter/font_awesome_flutter.dart ' ;
import ' package:intl/intl.dart ' ;
2024-05-21 23:20:34 +02:00
import ' package:provider/provider.dart ' ;
2024-06-08 20:01:23 +02:00
import ' package:share_plus/share_plus.dart ' ;
2024-05-21 23:20:34 +02:00
import ' package:simplecloudnotifier/api/api_client.dart ' ;
import ' package:simplecloudnotifier/components/layout/scaffold.dart ' ;
2024-06-07 23:44:32 +02:00
import ' package:simplecloudnotifier/models/channel.dart ' ;
import ' package:simplecloudnotifier/models/keytoken.dart ' ;
2024-06-15 21:29:51 +02:00
import ' package:simplecloudnotifier/models/scn_message.dart ' ;
2024-06-12 01:17:00 +02:00
import ' package:simplecloudnotifier/models/user.dart ' ;
2024-07-13 00:11:13 +02:00
import ' package:simplecloudnotifier/pages/channel_view/channel_view.dart ' ;
2024-06-02 17:09:57 +02:00
import ' package:simplecloudnotifier/state/app_auth.dart ' ;
2024-06-13 17:00:08 +02:00
import ' package:simplecloudnotifier/state/app_bar_state.dart ' ;
2024-07-13 00:11:13 +02:00
import ' package:simplecloudnotifier/utils/navi.dart ' ;
2024-06-08 20:01:23 +02:00
import ' package:simplecloudnotifier/utils/toaster.dart ' ;
2024-06-08 12:55:58 +02:00
import ' package:simplecloudnotifier/utils/ui.dart ' ;
2024-05-21 23:20:34 +02:00
class MessageViewPage extends StatefulWidget {
2024-07-13 01:05:32 +02:00
const MessageViewPage ( {
super . key ,
required this . messageID ,
required this . preloadedData ,
} ) ;
2024-05-21 23:20:34 +02:00
2024-07-13 01:05:32 +02:00
final String messageID ; // Potentially trimmed
final ( SCNMessage , ) ? preloadedData ; // Message is potentially trimmed, whole object is potentially null
2024-05-21 23:20:34 +02:00
@ override
State < MessageViewPage > createState ( ) = > _MessageViewPageState ( ) ;
}
class _MessageViewPageState extends State < MessageViewPage > {
2024-06-15 21:29:51 +02:00
late Future < ( SCNMessage , ChannelPreview , KeyTokenPreview , UserPreview ) > ? mainFuture ;
( SCNMessage , ChannelPreview , KeyTokenPreview , UserPreview ) ? mainFutureSnapshot = null ;
2024-06-07 23:44:32 +02:00
static final _dateFormat = DateFormat ( ' yyyy-MM-dd kk:mm ' ) ;
2024-05-21 23:20:34 +02:00
2024-06-08 20:01:23 +02:00
bool _monospaceMode = false ;
2024-07-13 01:05:32 +02:00
SCNMessage ? message = null ;
2024-05-21 23:20:34 +02:00
@ override
void initState ( ) {
2024-07-13 01:05:32 +02:00
if ( widget . preloadedData ! = null ) {
message = widget . preloadedData ! . $1 ;
}
2024-06-07 23:44:32 +02:00
mainFuture = fetchData ( ) ;
2024-06-13 17:00:08 +02:00
super . initState ( ) ;
2024-05-21 23:20:34 +02:00
}
2024-06-15 21:29:51 +02:00
Future < ( SCNMessage , ChannelPreview , KeyTokenPreview , UserPreview ) > fetchData ( ) async {
2024-06-13 17:00:08 +02:00
try {
await Future . delayed ( const Duration ( seconds: 0 ) , ( ) { } ) ; // this is annoyingly important - otherwise we call setLoadingIndeterminate directly in initStat() and get an exception....
2024-05-21 23:20:34 +02:00
2024-06-13 17:00:08 +02:00
AppBarState ( ) . setLoadingIndeterminate ( true ) ;
2024-06-07 23:44:32 +02:00
2024-06-13 17:00:08 +02:00
final acc = Provider . of < AppAuth > ( context , listen: false ) ;
2024-06-07 23:44:32 +02:00
2024-07-13 01:05:32 +02:00
final msg = await APIClient . getMessage ( acc , widget . messageID ) ;
2024-06-07 23:44:32 +02:00
2024-06-13 17:00:08 +02:00
final fut_chn = APIClient . getChannelPreview ( acc , msg . channelID ) ;
final fut_key = APIClient . getKeyTokenPreview ( acc , msg . usedKeyID ) ;
final fut_usr = APIClient . getUserPreview ( acc , msg . senderUserID ) ;
2024-06-08 20:01:23 +02:00
2024-06-13 17:00:08 +02:00
final chn = await fut_chn ;
final key = await fut_key ;
final usr = await fut_usr ;
2024-06-08 20:01:23 +02:00
2024-06-13 17:00:08 +02:00
//await Future.delayed(const Duration(seconds: 10), () {});
2024-06-08 20:01:23 +02:00
2024-06-13 17:00:08 +02:00
final r = ( msg , chn , key , usr ) ;
2024-06-08 20:01:23 +02:00
2024-06-13 17:00:08 +02:00
mainFutureSnapshot = r ;
return r ;
} finally {
AppBarState ( ) . setLoadingIndeterminate ( false ) ;
}
2024-05-21 23:20:34 +02:00
}
@ override
void dispose ( ) {
super . dispose ( ) ;
}
@ override
Widget build ( BuildContext context ) {
return SCNScaffold (
title: ' Message ' ,
2024-05-23 17:41:51 +02:00
showSearch: false ,
2024-06-08 20:01:23 +02:00
showShare: true ,
onShare: _share ,
2024-06-15 21:29:51 +02:00
child: FutureBuilder < ( SCNMessage , ChannelPreview , KeyTokenPreview , UserPreview ) > (
2024-06-07 23:44:32 +02:00
future: mainFuture ,
2024-05-21 23:20:34 +02:00
builder: ( context , snapshot ) {
if ( snapshot . hasData ) {
2024-06-12 01:17:00 +02:00
final ( msg , chn , tok , usr ) = snapshot . data ! ;
2024-06-13 17:00:08 +02:00
return _buildMessageView ( context , msg , chn , tok , usr ) ;
2024-05-21 23:20:34 +02:00
} else if ( snapshot . hasError ) {
return Center ( child: Text ( ' ${ snapshot . error } ' ) ) ; //TODO nice error page
2024-07-13 01:05:32 +02:00
} else if ( message ! = null & & ! this . message ! . trimmed ) {
return _buildMessageView ( context , this . message ! , null , null , null ) ;
2024-05-23 17:41:51 +02:00
} else {
return const Center ( child: CircularProgressIndicator ( ) ) ;
2024-05-21 23:20:34 +02:00
}
} ,
) ,
) ;
}
2024-05-23 17:41:51 +02:00
2024-06-08 20:01:23 +02:00
void _share ( ) async {
2024-07-13 01:05:32 +02:00
if ( this . message = = null ) return ;
var msg = this . message ! ;
2024-06-08 20:01:23 +02:00
if ( mainFutureSnapshot ! = null ) {
2024-06-12 01:17:00 +02:00
( msg , _ , _ , _ ) = mainFutureSnapshot ! ;
2024-06-08 20:01:23 +02:00
}
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 " ) ;
}
}
}
2024-06-15 21:29:51 +02:00
Widget _buildMessageView ( BuildContext context , SCNMessage message , ChannelPreview ? channel , KeyTokenPreview ? token , UserPreview ? user ) {
2024-06-08 20:01:23 +02:00
final userAccUserID = context . select < AppAuth , String ? > ( ( v ) = > v . userID ) ;
2024-06-07 23:44:32 +02:00
return SingleChildScrollView (
child: Padding (
padding: const EdgeInsets . fromLTRB ( 24 , 16 , 24 , 16 ) ,
child: Column (
crossAxisAlignment: CrossAxisAlignment . stretch ,
children: [
2024-06-13 17:00:08 +02:00
. . . _buildMessageHeader ( context , message , channel ) ,
2024-06-07 23:44:32 +02:00
SizedBox ( height: 8 ) ,
2024-06-12 01:17:00 +02:00
if ( message . content ! = null ) . . . _buildMessageContent ( context , message ) ,
2024-06-07 23:44:32 +02:00
SizedBox ( height: 8 ) ,
2024-06-25 20:49:40 +02:00
if ( message . senderName ! = null )
UI . metaCard (
context: context ,
icon: FontAwesomeIcons . solidSignature ,
title: ' Sender ' ,
values: [ message . senderName ! ] ,
mainAction: ( ) = > { /*TODO*/ } ,
) ,
UI . metaCard (
context: context ,
icon: FontAwesomeIcons . solidGearCode ,
title: ' KeyToken ' ,
values: [ message . usedKeyID , token ? . name ? ? ' ... ' ] ,
mainAction: ( ) = > { /*TODO*/ } ,
) ,
UI . metaCard (
context: context ,
icon: FontAwesomeIcons . solidIdCardClip ,
title: ' MessageID ' ,
values: [ message . messageID , message . userMessageID ? ? ' ' ] ,
) ,
UI . metaCard (
context: context ,
icon: FontAwesomeIcons . solidSnake ,
title: ' Channel ' ,
values: [ message . channelID , channel ? . displayName ? ? message . channelInternalName ] ,
2024-07-13 00:11:13 +02:00
mainAction: ( channel ! = null )
? ( ) {
Navi . push ( context , ( ) = > ChannelViewPage ( channelID: channel . channelID , preloadedData: null , needsReload: null ) ) ;
}
: null ,
2024-06-25 20:49:40 +02:00
) ,
UI . metaCard (
context: context ,
icon: FontAwesomeIcons . solidTimer ,
title: ' Timestamp ' ,
values: [ message . timestamp ] ,
) ,
UI . metaCard (
context: context ,
icon: FontAwesomeIcons . solidUser ,
title: ' User ' ,
values: [ user ? . userID ? ? ' ... ' , user ? . username ? ? ' ' ] ,
mainAction: ( ) = > { /*TODO*/ } ,
) ,
UI . metaCard (
context: context ,
icon: FontAwesomeIcons . solidBolt ,
title: ' Priority ' ,
values: [ _prettyPrintPriority ( message . priority ) ] ,
mainAction: ( ) = > { /*TODO*/ } ,
) ,
2024-06-08 20:01:23 +02:00
if ( message . senderUserID = = userAccUserID ) UI . button ( text: " Delete Message " , onPressed: ( ) { /*TODO*/ } , color: Colors . red [ 900 ] ) ,
2024-06-07 23:44:32 +02:00
] ,
) ,
) ,
) ;
}
2024-06-15 21:29:51 +02:00
String _resolveChannelName ( ChannelPreview ? channel , SCNMessage message ) {
2024-06-12 01:17:00 +02:00
return channel ? . displayName ? ? message . channelInternalName ;
2024-06-07 23:44:32 +02:00
}
2024-06-15 21:29:51 +02:00
List < Widget > _buildMessageHeader ( BuildContext context , SCNMessage message , ChannelPreview ? channel ) {
2024-06-07 23:44:32 +02:00
return [
Row (
children: [
2024-06-08 12:55:58 +02:00
UI . channelChip (
context: context ,
text: _resolveChannelName ( channel , message ) ,
2024-06-07 23:44:32 +02:00
margin: const EdgeInsets . fromLTRB ( 0 , 0 , 4 , 0 ) ,
2024-06-08 12:55:58 +02:00
fontSize: 16 ,
2024-06-07 23:44:32 +02:00
) ,
Expanded ( child: SizedBox ( ) ) ,
Text ( _dateFormat . format ( DateTime . parse ( message . timestamp ) ) , style: const TextStyle ( fontSize: 14 ) ) ,
] ,
) ,
SizedBox ( height: 8 ) ,
2024-06-15 12:37:38 +02:00
Text ( _preformatTitle ( message ) , style: const TextStyle ( fontSize: 18 , fontWeight: FontWeight . bold ) ) ,
2024-06-07 23:44:32 +02:00
] ;
}
2024-06-15 21:29:51 +02:00
List < Widget > _buildMessageContent ( BuildContext context , SCNMessage message ) {
2024-06-07 23:44:32 +02:00
return [
Row (
children: [
2024-06-08 20:01:23 +02:00
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 ] ) ,
2024-06-07 23:44:32 +02:00
Expanded ( child: SizedBox ( ) ) ,
2024-06-08 12:55:58 +02:00
UI . buttonIconOnly (
2024-06-08 20:01:23 +02:00
onPressed: ( ) {
Clipboard . setData ( new ClipboardData ( text: message . content ? ? ' ' ) ) ;
Toaster . info ( " Clipboard " , ' Copied text to Clipboard ' ) ;
} ,
2024-06-08 12:55:58 +02:00
icon: FontAwesomeIcons . copy ,
2024-06-07 23:44:32 +02:00
) ,
2024-06-08 12:55:58 +02:00
UI . buttonIconOnly (
2024-06-08 20:01:23 +02:00
icon: _monospaceMode ? FontAwesomeIcons . lineColumns : FontAwesomeIcons . alignLeft ,
onPressed: ( ) {
setState ( ( ) {
_monospaceMode = ! _monospaceMode ;
} ) ;
} ,
2024-06-07 23:44:32 +02:00
) ,
] ,
) ,
2024-06-08 20:01:23 +02:00
_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 ,
)
2024-06-07 23:44:32 +02:00
] ;
}
2024-06-15 21:29:51 +02:00
String _preformatTitle ( SCNMessage message ) {
2024-06-15 12:37:38 +02:00
return message . title . replaceAll ( ' \n ' , ' ' ) . replaceAll ( ' \r ' , ' ' ) . replaceAll ( ' \t ' , ' ' ) ;
}
2024-06-16 00:46:46 +02:00
String _prettyPrintPriority ( int priority ) {
switch ( priority ) {
case 0 :
return ' Low (0) ' ;
case 1 :
return ' Normal (1) ' ;
case 2 :
return ' High (2) ' ;
default :
return ' Unknown ( $ priority ) ' ;
}
}
2024-05-21 23:20:34 +02:00
}