channel_view page
This commit is contained in:
parent
e2dbe8866d
commit
2b23404461
@ -247,6 +247,16 @@ class APIClient {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<List<Subscription>> getChannelSubscriptions(TokenSource auth, String cid) async {
|
||||||
|
return await _request(
|
||||||
|
name: 'getChannelSubscriptions',
|
||||||
|
method: 'GET',
|
||||||
|
relURL: 'users/${auth.getUserID()}/channels/${cid}/subscriptions',
|
||||||
|
fn: (json) => Subscription.fromJsonArray(json['subscriptions'] as List<dynamic>),
|
||||||
|
authToken: auth.getToken(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
static Future<List<Client>> getClientList(TokenSource auth) async {
|
static Future<List<Client>> getClientList(TokenSource auth) async {
|
||||||
return await _request(
|
return await _request(
|
||||||
name: 'getClientList',
|
name: 'getClientList',
|
||||||
|
@ -86,6 +86,10 @@ class User {
|
|||||||
'max_user_message_id_length': maxUserMessageIDLength,
|
'max_user_message_id_length': maxUserMessageIDLength,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UserPreview toPreview() {
|
||||||
|
return UserPreview(userID: userID, username: username);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UserWithClientsAndKeys {
|
class UserWithClientsAndKeys {
|
||||||
|
@ -110,8 +110,10 @@ class _ChannelListItemState extends State<ChannelListItem> {
|
|||||||
Widget _buildIcon(BuildContext context) {
|
Widget _buildIcon(BuildContext context) {
|
||||||
if (widget.subscription == null) {
|
if (widget.subscription == null) {
|
||||||
return Icon(FontAwesomeIcons.solidSquareDashed, color: Theme.of(context).colorScheme.outline, size: 32); // not-subscribed
|
return Icon(FontAwesomeIcons.solidSquareDashed, color: Theme.of(context).colorScheme.outline, size: 32); // not-subscribed
|
||||||
|
} else if (widget.subscription!.confirmed && widget.channel.ownerUserID == widget.subscription!.subscriberUserID) {
|
||||||
|
return Icon(FontAwesomeIcons.solidSquareRss, color: Theme.of(context).colorScheme.onPrimaryContainer, size: 32); // subscribed (own channel)
|
||||||
} else if (widget.subscription!.confirmed) {
|
} else if (widget.subscription!.confirmed) {
|
||||||
return Icon(FontAwesomeIcons.solidSquareRss, color: Theme.of(context).colorScheme.onPrimaryContainer, size: 32); // subscribed
|
return Icon(FontAwesomeIcons.solidSquareShareNodes, color: Theme.of(context).colorScheme.onPrimaryContainer, size: 32); // subscribed (foreign channel)
|
||||||
} else {
|
} else {
|
||||||
return Icon(FontAwesomeIcons.solidSquareEnvelope, color: Theme.of(context).colorScheme.tertiary, size: 32); // requested
|
return Icon(FontAwesomeIcons.solidSquareEnvelope, color: Theme.of(context).colorScheme.tertiary, size: 32); // requested
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,17 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
import 'package:intl/intl.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:qr_flutter/qr_flutter.dart';
|
import 'package:qr_flutter/qr_flutter.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:simplecloudnotifier/api/api_client.dart';
|
import 'package:simplecloudnotifier/api/api_client.dart';
|
||||||
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
import 'package:simplecloudnotifier/components/layout/scaffold.dart';
|
||||||
import 'package:simplecloudnotifier/models/channel.dart';
|
import 'package:simplecloudnotifier/models/channel.dart';
|
||||||
import 'package:simplecloudnotifier/models/keytoken.dart';
|
|
||||||
import 'package:simplecloudnotifier/models/scn_message.dart';
|
|
||||||
import 'package:simplecloudnotifier/models/subscription.dart';
|
import 'package:simplecloudnotifier/models/subscription.dart';
|
||||||
import 'package:simplecloudnotifier/models/user.dart';
|
import 'package:simplecloudnotifier/models/user.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_auth.dart';
|
import 'package:simplecloudnotifier/state/app_auth.dart';
|
||||||
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
import 'package:simplecloudnotifier/state/app_bar_state.dart';
|
||||||
import 'package:simplecloudnotifier/utils/toaster.dart';
|
import 'package:simplecloudnotifier/types/immediate_future.dart';
|
||||||
import 'package:simplecloudnotifier/utils/ui.dart';
|
import 'package:simplecloudnotifier/utils/ui.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class ChannelViewPage extends StatefulWidget {
|
class ChannelViewPage extends StatefulWidget {
|
||||||
const ChannelViewPage({
|
const ChannelViewPage({
|
||||||
@ -32,10 +28,39 @@ class ChannelViewPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ChannelViewPageState extends State<ChannelViewPage> {
|
class _ChannelViewPageState extends State<ChannelViewPage> {
|
||||||
static final _dateFormat = DateFormat('yyyy-MM-dd kk:mm');
|
late ImmediateFuture<String?> _futureSubscribeKey;
|
||||||
|
late ImmediateFuture<List<Subscription>> _futureSubscriptions;
|
||||||
|
late ImmediateFuture<UserPreview> _futureOwner;
|
||||||
|
|
||||||
|
int _loadingIndeterminateCounter = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
final userAcc = Provider.of<AppAuth>(context, listen: false);
|
||||||
|
|
||||||
|
if (widget.channel.ownerUserID == userAcc.userID) {
|
||||||
|
if (widget.channel.subscribeKey != null) {
|
||||||
|
_futureSubscribeKey = ImmediateFuture<String?>.ofValue(widget.channel.subscribeKey);
|
||||||
|
} else {
|
||||||
|
_futureSubscribeKey = ImmediateFuture<String?>.ofFuture(_getSubScribeKey(userAcc));
|
||||||
|
}
|
||||||
|
_futureSubscriptions = ImmediateFuture<List<Subscription>>.ofFuture(_listSubscriptions(userAcc));
|
||||||
|
} else {
|
||||||
|
_futureSubscribeKey = ImmediateFuture<String?>.ofValue(null);
|
||||||
|
_futureSubscriptions = ImmediateFuture<List<Subscription>>.ofValue([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.channel.ownerUserID == userAcc.userID) {
|
||||||
|
var cacheUser = userAcc.getUserOrNull();
|
||||||
|
if (cacheUser != null) {
|
||||||
|
_futureOwner = ImmediateFuture<UserPreview>.ofValue(cacheUser.toPreview());
|
||||||
|
} else {
|
||||||
|
_futureOwner = ImmediateFuture<UserPreview>.ofFuture(_getOwner(userAcc));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_futureOwner = ImmediateFuture<UserPreview>.ofFuture(APIClient.getUserPreview(userAcc, widget.channel.ownerUserID));
|
||||||
|
}
|
||||||
|
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,93 +82,115 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
Widget _buildChannelView(BuildContext context) {
|
Widget _buildChannelView(BuildContext context) {
|
||||||
final userAccUserID = context.select<AppAuth, String?>((v) => v.userID);
|
final userAccUserID = context.select<AppAuth, String?>((v) => v.userID);
|
||||||
|
|
||||||
|
final isOwned = (widget.channel.ownerUserID == userAccUserID);
|
||||||
|
final isSubscribed = (widget.subscription != null && widget.subscription!.confirmed);
|
||||||
|
|
||||||
return SingleChildScrollView(
|
return SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(24, 16, 24, 16),
|
padding: const EdgeInsets.fromLTRB(24, 16, 24, 16),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
..._buildChannelHeader(context),
|
|
||||||
SizedBox(height: 8),
|
|
||||||
_buildQRCode(context),
|
_buildQRCode(context),
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
//TODO icons
|
UI.metaCard(
|
||||||
_buildMetaCard(context, FontAwesomeIcons.solidQuestion, 'ChannelID', ['...'], null),
|
|
||||||
_buildMetaCard(context, FontAwesomeIcons.solidQuestion, 'InternalName', ['...'], null),
|
|
||||||
_buildMetaCard(context, FontAwesomeIcons.solidQuestion, 'DisplayName', ['...'], null), //TODO edit icon on right to edit name
|
|
||||||
_buildMetaCard(context, FontAwesomeIcons.solidQuestion, 'Subscription (own)', ['...'], null), //TODO sub/unsub icon on right
|
|
||||||
//TODO list foreign subscriptions (with accept/decline/delete button on right)
|
|
||||||
_buildMetaCard(context, FontAwesomeIcons.solidQuestion, 'Messages', ['...'], () {/*TODO*/}),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Widget> _buildChannelHeader(BuildContext context) {
|
|
||||||
return [
|
|
||||||
Text(widget.channel.displayName, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildMetaCard(BuildContext context, IconData icn, String title, List<String> values, void Function()? action) {
|
|
||||||
final container = UI.box(
|
|
||||||
context: context,
|
context: context,
|
||||||
padding: EdgeInsets.fromLTRB(16, 2, 4, 2),
|
icon: FontAwesomeIcons.solidIdCardClip,
|
||||||
child: Row(
|
title: 'ChannelID',
|
||||||
children: [
|
values: [widget.channel.channelID],
|
||||||
FaIcon(icn, size: 18),
|
),
|
||||||
SizedBox(width: 16),
|
UI.metaCard(
|
||||||
Column(
|
context: context,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
icon: FontAwesomeIcons.solidInputNumeric,
|
||||||
children: [
|
title: 'InternalName',
|
||||||
Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
values: [widget.channel.internalName],
|
||||||
for (final val in values) Text(val, style: const TextStyle(fontSize: 14)),
|
),
|
||||||
],
|
UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidInputText,
|
||||||
|
title: 'DisplayName',
|
||||||
|
values: [widget.channel.displayName],
|
||||||
|
iconActions: isOwned ? [(FontAwesomeIcons.penToSquare, _rename)] : [],
|
||||||
|
),
|
||||||
|
UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidDiagramSubtask,
|
||||||
|
title: 'Subscription (own)',
|
||||||
|
values: [_formatSubscriptionStatus(widget.subscription)],
|
||||||
|
iconActions: isSubscribed ? [(FontAwesomeIcons.solidSquareXmark, _unsubscribe)] : [(FontAwesomeIcons.solidSquareRss, _subscribe)],
|
||||||
|
),
|
||||||
|
_buildForeignSubscriptions(context),
|
||||||
|
_buildOwnerCard(context, isOwned),
|
||||||
|
UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidEnvelope,
|
||||||
|
title: 'Messages',
|
||||||
|
values: [widget.channel.messagesSent.toString()],
|
||||||
|
mainAction: () {/*TODO*/},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (action == null) {
|
Widget _buildForeignSubscriptions(BuildContext context) {
|
||||||
return Padding(
|
return FutureBuilder(
|
||||||
padding: EdgeInsets.symmetric(vertical: 4, horizontal: 0),
|
future: _futureSubscriptions.future,
|
||||||
child: container,
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
for (final sub in snapshot.data!.where((sub) => sub.subscriptionID != widget.subscription?.subscriptionID))
|
||||||
|
UI.metaCard(
|
||||||
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidDiagramSuccessor,
|
||||||
|
title: 'Subscription (other)',
|
||||||
|
values: [_formatSubscriptionStatus(sub)],
|
||||||
|
iconActions: _getForignSubActions(sub),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Padding(
|
return SizedBox();
|
||||||
padding: EdgeInsets.symmetric(vertical: 4, horizontal: 0),
|
}
|
||||||
child: InkWell(
|
},
|
||||||
splashColor: Theme.of(context).splashColor,
|
|
||||||
onTap: action,
|
|
||||||
child: container,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
String _preformatTitle(SCNMessage message) {
|
Widget _buildOwnerCard(BuildContext context, bool isOwned) {
|
||||||
return message.title.replaceAll('\n', '').replaceAll('\r', '').replaceAll('\t', ' ');
|
return FutureBuilder(
|
||||||
}
|
future: _futureOwner.future,
|
||||||
|
builder: (context, snapshot) {
|
||||||
String _prettyPrintPriority(int priority) {
|
if (snapshot.hasData) {
|
||||||
switch (priority) {
|
return UI.metaCard(
|
||||||
case 0:
|
context: context,
|
||||||
return 'Low (0)';
|
icon: FontAwesomeIcons.solidUser,
|
||||||
case 1:
|
title: 'Owner',
|
||||||
return 'Normal (1)';
|
values: [widget.channel.ownerUserID + (isOwned ? ' (you)' : ''), if (snapshot.data?.username != null) snapshot.data!.username!],
|
||||||
case 2:
|
);
|
||||||
return 'High (2)';
|
} else {
|
||||||
default:
|
return UI.metaCard(
|
||||||
return 'Unknown ($priority)';
|
context: context,
|
||||||
|
icon: FontAwesomeIcons.solidUser,
|
||||||
|
title: 'Owner',
|
||||||
|
values: [widget.channel.ownerUserID + (isOwned ? ' (you)' : '')],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildQRCode(BuildContext context) {
|
Widget _buildQRCode(BuildContext context) {
|
||||||
var text = 'TODO' + widget.channel.channelID; //TODO subkey+channelid with deeplink-y
|
return FutureBuilder(
|
||||||
|
future: _futureSubscribeKey.future,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.hasData && snapshot.data != null) {
|
||||||
|
var text = 'TODO' + '\n' + widget.channel.channelID + '\n' + snapshot.data!; //TODO deeplink-y (also perhaps just bas64 everything together?)
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
//TODO share
|
Share.share(text, subject: widget.channel.displayName);
|
||||||
},
|
},
|
||||||
child: Center(
|
child: Center(
|
||||||
child: QrImageView(
|
child: QrImageView(
|
||||||
@ -161,5 +208,120 @@ class _ChannelViewPageState extends State<ChannelViewPage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else if (snapshot.hasData && snapshot.data == null) {
|
||||||
|
return const SizedBox(
|
||||||
|
width: 300.0,
|
||||||
|
height: 300.0,
|
||||||
|
child: Center(child: Icon(FontAwesomeIcons.solidSnake, size: 64)),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return const SizedBox(
|
||||||
|
width: 300.0,
|
||||||
|
height: 300.0,
|
||||||
|
child: Center(child: CircularProgressIndicator()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _rename() {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void _subscribe() {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void _unsubscribe() {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cancelForeignSubscription(Subscription sub) {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void _confirmForeignSubscription(Subscription sub) {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void _denyForeignSubscription(Subscription sub) {
|
||||||
|
//TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatSubscriptionStatus(Subscription? subscription) {
|
||||||
|
if (subscription == null) {
|
||||||
|
return 'Not Subscribed';
|
||||||
|
} else if (subscription.confirmed) {
|
||||||
|
return 'Subscribed';
|
||||||
|
} else {
|
||||||
|
return 'Requested';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String?> _getSubScribeKey(AppAuth auth) async {
|
||||||
|
try {
|
||||||
|
await Future.delayed(const Duration(seconds: 0), () {}); // this is annoyingly important - otherwise we call setLoadingIndeterminate directly in initStat() and get an exception....
|
||||||
|
|
||||||
|
_incLoadingIndeterminateCounter(1);
|
||||||
|
|
||||||
|
var channel = await APIClient.getChannel(auth, widget.channel.channelID);
|
||||||
|
|
||||||
|
//await Future.delayed(const Duration(seconds: 10), () {});
|
||||||
|
|
||||||
|
return channel.channel.subscribeKey;
|
||||||
|
} finally {
|
||||||
|
_incLoadingIndeterminateCounter(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Subscription>> _listSubscriptions(AppAuth auth) async {
|
||||||
|
try {
|
||||||
|
await Future.delayed(const Duration(seconds: 0), () {}); // this is annoyingly important - otherwise we call setLoadingIndeterminate directly in initStat() and get an exception....
|
||||||
|
|
||||||
|
_incLoadingIndeterminateCounter(1);
|
||||||
|
|
||||||
|
var subs = await APIClient.getChannelSubscriptions(auth, widget.channel.channelID);
|
||||||
|
|
||||||
|
//await Future.delayed(const Duration(seconds: 10), () {});
|
||||||
|
|
||||||
|
return subs;
|
||||||
|
} finally {
|
||||||
|
_incLoadingIndeterminateCounter(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<UserPreview> _getOwner(AppAuth auth) async {
|
||||||
|
try {
|
||||||
|
await Future.delayed(const Duration(seconds: 0), () {}); // this is annoyingly important - otherwise we call setLoadingIndeterminate directly in initStat() and get an exception....
|
||||||
|
|
||||||
|
_incLoadingIndeterminateCounter(1);
|
||||||
|
|
||||||
|
final owner = APIClient.getUserPreview(auth, widget.channel.ownerUserID);
|
||||||
|
|
||||||
|
//await Future.delayed(const Duration(seconds: 10), () {});
|
||||||
|
|
||||||
|
return owner;
|
||||||
|
} finally {
|
||||||
|
_incLoadingIndeterminateCounter(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<(IconData, void Function())> _getForignSubActions(Subscription sub) {
|
||||||
|
if (sub.confirmed) {
|
||||||
|
return [(FontAwesomeIcons.solidSquareXmark, () => _cancelForeignSubscription(sub))];
|
||||||
|
} else {
|
||||||
|
return [
|
||||||
|
(FontAwesomeIcons.solidSquareCheck, () => _confirmForeignSubscription(sub)),
|
||||||
|
(FontAwesomeIcons.solidSquareXmark, () => _denyForeignSubscription(sub)),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _incLoadingIndeterminateCounter(int delta) {
|
||||||
|
setState(() {
|
||||||
|
_loadingIndeterminateCounter += delta;
|
||||||
|
AppBarState().setLoadingIndeterminate(_loadingIndeterminateCounter > 0);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,59 +131,54 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
if (message.content != null) ..._buildMessageContent(context, message),
|
if (message.content != null) ..._buildMessageContent(context, message),
|
||||||
SizedBox(height: 8),
|
SizedBox(height: 8),
|
||||||
if (message.senderName != null) _buildMetaCard(context, FontAwesomeIcons.solidSignature, 'Sender', [message.senderName!], () => {/*TODO*/}),
|
if (message.senderName != null)
|
||||||
_buildMetaCard(
|
UI.metaCard(
|
||||||
context,
|
context: context,
|
||||||
FontAwesomeIcons.solidGearCode,
|
icon: FontAwesomeIcons.solidSignature,
|
||||||
'KeyToken',
|
title: 'Sender',
|
||||||
[
|
values: [message.senderName!],
|
||||||
message.usedKeyID,
|
mainAction: () => {/*TODO*/},
|
||||||
token?.name ?? '...',
|
),
|
||||||
],
|
UI.metaCard(
|
||||||
() => {/*TODO*/}),
|
context: context,
|
||||||
_buildMetaCard(
|
icon: FontAwesomeIcons.solidGearCode,
|
||||||
context,
|
title: 'KeyToken',
|
||||||
FontAwesomeIcons.solidIdCardClip,
|
values: [message.usedKeyID, token?.name ?? '...'],
|
||||||
'MessageID',
|
mainAction: () => {/*TODO*/},
|
||||||
[
|
),
|
||||||
message.messageID,
|
UI.metaCard(
|
||||||
message.userMessageID ?? '',
|
context: context,
|
||||||
],
|
icon: FontAwesomeIcons.solidIdCardClip,
|
||||||
null),
|
title: 'MessageID',
|
||||||
_buildMetaCard(
|
values: [message.messageID, message.userMessageID ?? ''],
|
||||||
context,
|
),
|
||||||
FontAwesomeIcons.solidSnake,
|
UI.metaCard(
|
||||||
'Channel',
|
context: context,
|
||||||
[
|
icon: FontAwesomeIcons.solidSnake,
|
||||||
message.channelID,
|
title: 'Channel',
|
||||||
channel?.displayName ?? message.channelInternalName,
|
values: [message.channelID, channel?.displayName ?? message.channelInternalName],
|
||||||
],
|
mainAction: () => {/*TODO*/},
|
||||||
() => {/*TODO*/}),
|
),
|
||||||
_buildMetaCard(
|
UI.metaCard(
|
||||||
context,
|
context: context,
|
||||||
FontAwesomeIcons.solidTimer,
|
icon: FontAwesomeIcons.solidTimer,
|
||||||
'Timestamp',
|
title: 'Timestamp',
|
||||||
[
|
values: [message.timestamp],
|
||||||
message.timestamp,
|
),
|
||||||
],
|
UI.metaCard(
|
||||||
null),
|
context: context,
|
||||||
_buildMetaCard(
|
icon: FontAwesomeIcons.solidUser,
|
||||||
context,
|
title: 'User',
|
||||||
FontAwesomeIcons.solidUser,
|
values: [user?.userID ?? '...', user?.username ?? ''],
|
||||||
'User',
|
mainAction: () => {/*TODO*/},
|
||||||
[
|
),
|
||||||
user?.userID ?? '...',
|
UI.metaCard(
|
||||||
user?.username ?? '',
|
context: context,
|
||||||
],
|
icon: FontAwesomeIcons.solidBolt,
|
||||||
() => {/*TODO*/}), //TODO
|
title: 'Priority',
|
||||||
_buildMetaCard(
|
values: [_prettyPrintPriority(message.priority)],
|
||||||
context,
|
mainAction: () => {/*TODO*/},
|
||||||
FontAwesomeIcons.solidBolt,
|
),
|
||||||
'Priority',
|
|
||||||
[
|
|
||||||
_prettyPrintPriority(message.priority),
|
|
||||||
],
|
|
||||||
() => {/*TODO*/}), //TODO
|
|
||||||
if (message.senderUserID == userAccUserID) UI.button(text: "Delete Message", onPressed: () {/*TODO*/}, color: Colors.red[900]),
|
if (message.senderUserID == userAccUserID) UI.button(text: "Delete Message", onPressed: () {/*TODO*/}, color: Colors.red[900]),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -260,42 +255,6 @@ class _MessageViewPageState extends State<MessageViewPage> {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildMetaCard(BuildContext context, IconData icn, String title, List<String> values, void Function()? action) {
|
|
||||||
final container = UI.box(
|
|
||||||
context: context,
|
|
||||||
padding: EdgeInsets.fromLTRB(16, 2, 4, 2),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
FaIcon(icn, size: 18),
|
|
||||||
SizedBox(width: 16),
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
|
||||||
for (final val in values) Text(val, style: const TextStyle(fontSize: 14)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (action == null) {
|
|
||||||
return Padding(
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 4, horizontal: 0),
|
|
||||||
child: container,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Padding(
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 4, horizontal: 0),
|
|
||||||
child: InkWell(
|
|
||||||
splashColor: Theme.of(context).splashColor,
|
|
||||||
onTap: action,
|
|
||||||
child: container,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _preformatTitle(SCNMessage message) {
|
String _preformatTitle(SCNMessage message) {
|
||||||
return message.title.replaceAll('\n', '').replaceAll('\r', '').replaceAll('\t', ' ');
|
return message.title.replaceAll('\n', '').replaceAll('\r', '').replaceAll('\t', ' ');
|
||||||
}
|
}
|
||||||
|
@ -182,6 +182,10 @@ class AppAuth extends ChangeNotifier implements TokenSource {
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
User? getUserOrNull() {
|
||||||
|
return _user?.$1;
|
||||||
|
}
|
||||||
|
|
||||||
Future<Client?> loadClient({bool force = false, Duration? forceIfOlder = null}) async {
|
Future<Client?> loadClient({bool force = false, Duration? forceIfOlder = null}) async {
|
||||||
if (forceIfOlder != null && _client != null && _client!.$2.difference(DateTime.now()) > forceIfOlder) {
|
if (forceIfOlder != null && _client != null && _client!.$2.difference(DateTime.now()) > forceIfOlder) {
|
||||||
force = true;
|
force = true;
|
||||||
@ -212,6 +216,10 @@ class AppAuth extends ChangeNotifier implements TokenSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Client? getClientOrNull() {
|
||||||
|
return _client?.$1;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String getToken() {
|
String getToken() {
|
||||||
return _tokenAdmin!;
|
return _tokenAdmin!;
|
||||||
|
@ -106,4 +106,49 @@ class UI {
|
|||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Widget metaCard({required BuildContext context, required IconData icon, required String title, required List<String> values, void Function()? mainAction, List<(IconData, void Function())>? iconActions}) {
|
||||||
|
final container = UI.box(
|
||||||
|
context: context,
|
||||||
|
padding: EdgeInsets.fromLTRB(16, 2, 4, 2),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
FaIcon(icon, size: 18),
|
||||||
|
SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(title, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||||
|
for (final val in values) Text(val, style: const TextStyle(fontSize: 14)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (iconActions != null) ...[
|
||||||
|
SizedBox(width: 12),
|
||||||
|
for (final iconAction in iconActions) ...[
|
||||||
|
SizedBox(width: 4),
|
||||||
|
IconButton(icon: FaIcon(iconAction.$1), onPressed: iconAction.$2),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (mainAction == null) {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 4, horizontal: 0),
|
||||||
|
child: container,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.symmetric(vertical: 4, horizontal: 0),
|
||||||
|
child: InkWell(
|
||||||
|
splashColor: Theme.of(context).splashColor,
|
||||||
|
onTap: mainAction,
|
||||||
|
child: container,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user