diff --git a/flutter/lib/api/api_client.dart b/flutter/lib/api/api_client.dart index 18fe726..7d4d8d8 100644 --- a/flutter/lib/api/api_client.dart +++ b/flutter/lib/api/api_client.dart @@ -127,6 +127,16 @@ class APIClient { ); } + static Future getUserPreview(TokenSource auth, String uid) async { + return await _request( + name: 'getUserPreview', + method: 'GET', + relURL: 'preview/users/$uid', + fn: UserPreview.fromJson, + authToken: auth.getToken(), + ); + } + static Future addClient(TokenSource auth, String fcmToken, String agentModel, String agentVersion, String? name, String clientType) async { return await _request( name: 'addClient', @@ -191,6 +201,16 @@ class APIClient { ); } + static Future getChannelPreview(TokenSource auth, String cid) async { + return await _request( + name: 'getChannelPreview', + method: 'GET', + relURL: 'preview/channels/${cid}', + fn: ChannelPreview.fromJson, + authToken: auth.getToken(), + ); + } + static Future<(String, List)> getMessageList(TokenSource auth, String pageToken, {int? pageSize, List? channelIDs}) async { return await _request( name: 'getMessageList', @@ -275,6 +295,16 @@ class APIClient { ); } + static Future getKeyTokenPreview(TokenSource auth, String kid) async { + return await _request( + name: 'getKeyTokenPreview', + method: 'GET', + relURL: 'preview/keys/$kid', + fn: KeyTokenPreview.fromJson, + authToken: auth.getToken(), + ); + } + static Future getKeyTokenByToken(String userid, String token) async { return await _request( name: 'getCurrentKeyToken', diff --git a/flutter/lib/models/channel.dart b/flutter/lib/models/channel.dart index 51b40c1..6ea330e 100644 --- a/flutter/lib/models/channel.dart +++ b/flutter/lib/models/channel.dart @@ -58,3 +58,29 @@ class ChannelWithSubscription { return jsonArr.map((e) => ChannelWithSubscription.fromJson(e as Map)).toList(); } } + +class ChannelPreview { + final String channelID; + final String ownerUserID; + final String internalName; + final String displayName; + final String? descriptionName; + + const ChannelPreview({ + required this.channelID, + required this.ownerUserID, + required this.internalName, + required this.displayName, + required this.descriptionName, + }); + + factory ChannelPreview.fromJson(Map json) { + return ChannelPreview( + channelID: json['channel_id'] as String, + ownerUserID: json['owner_user_id'] as String, + internalName: json['internal_name'] as String, + displayName: json['display_name'] as String, + descriptionName: json['description_name'] as String?, + ); + } +} diff --git a/flutter/lib/models/keytoken.dart b/flutter/lib/models/keytoken.dart index c8f70a2..a2d84d5 100644 --- a/flutter/lib/models/keytoken.dart +++ b/flutter/lib/models/keytoken.dart @@ -39,3 +39,32 @@ class KeyToken { return jsonArr.map((e) => KeyToken.fromJson(e as Map)).toList(); } } + +class KeyTokenPreview { + final String keytokenID; + final String name; + final String ownerUserID; + final bool allChannels; + final List channels; + final String permissions; + + const KeyTokenPreview({ + required this.keytokenID, + required this.name, + required this.ownerUserID, + required this.allChannels, + required this.channels, + required this.permissions, + }); + + factory KeyTokenPreview.fromJson(Map json) { + return KeyTokenPreview( + keytokenID: json['keytoken_id'] as String, + name: json['name'] as String, + ownerUserID: json['owner_user_id'] as String, + allChannels: json['all_channels'] as bool, + channels: (json['channels'] as List).map((e) => e as String).toList(), + permissions: json['permissions'] as String, + ); + } +} diff --git a/flutter/lib/models/user.dart b/flutter/lib/models/user.dart index 4a9b140..58bd769 100644 --- a/flutter/lib/models/user.dart +++ b/flutter/lib/models/user.dart @@ -90,3 +90,20 @@ class UserWithClientsAndKeys { ); } } + +class UserPreview { + final String userID; + final String? username; + + const UserPreview({ + required this.userID, + required this.username, + }); + + factory UserPreview.fromJson(Map json) { + return UserPreview( + userID: json['user_id'] as String, + username: json['username'] as String?, + ); + } +} diff --git a/flutter/lib/pages/message_view/message_view.dart b/flutter/lib/pages/message_view/message_view.dart index 3d5c9cb..5bba5ad 100644 --- a/flutter/lib/pages/message_view/message_view.dart +++ b/flutter/lib/pages/message_view/message_view.dart @@ -5,12 +5,11 @@ 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'; -import 'package:simplecloudnotifier/models/api_error.dart'; import 'package:simplecloudnotifier/models/channel.dart'; import 'package:simplecloudnotifier/models/keytoken.dart'; import 'package:simplecloudnotifier/models/message.dart'; +import 'package:simplecloudnotifier/models/user.dart'; import 'package:simplecloudnotifier/state/app_auth.dart'; import 'package:simplecloudnotifier/utils/toaster.dart'; import 'package:simplecloudnotifier/utils/ui.dart'; @@ -25,8 +24,8 @@ class MessageViewPage extends StatefulWidget { } class _MessageViewPageState extends State { - late Future<(Message, ChannelWithSubscription?, KeyToken?)>? mainFuture; - (Message, ChannelWithSubscription?, KeyToken?)? mainFutureSnapshot = null; + late Future<(Message, ChannelPreview, KeyTokenPreview, UserPreview)>? mainFuture; + (Message, ChannelPreview, KeyTokenPreview, UserPreview)? mainFutureSnapshot = null; static final _dateFormat = DateFormat('yyyy-MM-dd kk:mm'); bool _monospaceMode = false; @@ -37,38 +36,24 @@ class _MessageViewPageState extends State { mainFuture = fetchData(); } - Future<(Message, ChannelWithSubscription?, KeyToken?)> fetchData() async { + Future<(Message, ChannelPreview, KeyTokenPreview, UserPreview)> fetchData() async { final acc = Provider.of(context, listen: false); final msg = await APIClient.getMessage(acc, widget.message.messageID); - ChannelWithSubscription? chn = null; - try { - chn = await APIClient.getChannel(acc, msg.channelID); //TODO getShortChannel (?) -> no perm - } on APIException catch (e) { - if (e.error == APIError.USER_AUTH_FAILED) { - chn = null; - } else { - rethrow; - } - } + final fut_chn = APIClient.getChannelPreview(acc, msg.channelID); + final fut_key = APIClient.getKeyTokenPreview(acc, msg.usedKeyID); + final fut_usr = APIClient.getUserPreview(acc, msg.senderUserID); - KeyToken? tok = null; - try { - tok = await APIClient.getKeyToken(acc, msg.usedKeyID); //TODO getShortKeyToken (?) -> no perm - } on APIException catch (e) { - if (e.error == APIError.USER_AUTH_FAILED) { - tok = null; - } else { - rethrow; - } - } + final chn = await fut_chn; + final key = await fut_key; + final usr = await fut_usr; //TODO getShortUser for sender //await Future.delayed(const Duration(seconds: 2), () {}); - final r = (msg, chn, tok); + final r = (msg, chn, key, usr); mainFutureSnapshot = r; @@ -87,16 +72,16 @@ class _MessageViewPageState extends State { showSearch: false, showShare: true, onShare: _share, - child: FutureBuilder<(Message, ChannelWithSubscription?, KeyToken?)>( + child: FutureBuilder<(Message, ChannelPreview, KeyTokenPreview, UserPreview)>( future: mainFuture, builder: (context, snapshot) { if (snapshot.hasData) { - final (msg, chn, tok) = snapshot.data!; - return _buildMessageView(context, msg, chn, tok, false); + final (msg, chn, tok, usr) = snapshot.data!; + return _buildMessageView(context, msg, chn, tok, usr, false); } else if (snapshot.hasError) { return Center(child: Text('${snapshot.error}')); //TODO nice error page } else if (!widget.message.trimmed) { - return _buildMessageView(context, widget.message, null, null, true); + return _buildMessageView(context, widget.message, null, null, null, true); } else { return const Center(child: CircularProgressIndicator()); } @@ -108,7 +93,7 @@ class _MessageViewPageState extends State { void _share() async { var msg = widget.message; if (mainFutureSnapshot != null) { - (msg, _, _) = mainFutureSnapshot!; + (msg, _, _, _) = mainFutureSnapshot!; } if (msg.content != null) { @@ -126,7 +111,7 @@ class _MessageViewPageState extends State { } } - Widget _buildMessageView(BuildContext context, Message message, ChannelWithSubscription? channel, KeyToken? token, bool loading) { + Widget _buildMessageView(BuildContext context, Message message, ChannelPreview? channel, KeyTokenPreview? token, UserPreview? user, bool loading) { final userAccUserID = context.select((v) => v.userID); return SingleChildScrollView( @@ -135,16 +120,16 @@ class _MessageViewPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - ..._buildMessageHeader(context, message, channel, token, loading), + ..._buildMessageHeader(context, message, channel, loading), SizedBox(height: 8), - if (message.content != null) ..._buildMessageContent(context, message, channel, token), + if (message.content != null) ..._buildMessageContent(context, message), SizedBox(height: 8), if (message.senderName != null) _buildMetaCard(context, FontAwesomeIcons.solidSignature, 'Sender', [message.senderName!], () => {/*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), - _buildMetaCard(context, FontAwesomeIcons.solidSnake, 'Channel', [message.channelID, channel?.channel.displayName ?? message.channelInternalName], () => {/*TODO*/}), + _buildMetaCard(context, FontAwesomeIcons.solidSnake, 'Channel', [message.channelID, channel?.displayName ?? message.channelInternalName], () => {/*TODO*/}), _buildMetaCard(context, FontAwesomeIcons.solidTimer, 'Timestamp', [message.timestamp], null), - _buildMetaCard(context, FontAwesomeIcons.solidUser, 'User', ['TODO'], () => {/*TODO*/}), //TODO + _buildMetaCard(context, FontAwesomeIcons.solidUser, 'User', [if (user != null) user.userID, if (user?.username != null) user!.username!], () => {/*TODO*/}), //TODO if (message.senderUserID == userAccUserID) UI.button(text: "Delete Message", onPressed: () {/*TODO*/}, color: Colors.red[900]), ], ), @@ -152,11 +137,11 @@ class _MessageViewPageState extends State { ); } - String _resolveChannelName(ChannelWithSubscription? channel, Message message) { - return channel?.channel.displayName ?? message.channelInternalName; + String _resolveChannelName(ChannelPreview? channel, Message message) { + return channel?.displayName ?? message.channelInternalName; } - List _buildMessageHeader(BuildContext context, Message message, ChannelWithSubscription? channel, KeyToken? token, bool loading) { + List _buildMessageHeader(BuildContext context, Message message, ChannelPreview? channel, bool loading) { return [ Row( children: [ @@ -192,7 +177,7 @@ class _MessageViewPageState extends State { ]; } - List _buildMessageContent(BuildContext context, Message message, ChannelWithSubscription? channel, KeyToken? token) { + List _buildMessageContent(BuildContext context, Message message) { return [ Row( children: [