Fix audio player error handling and add session access level display

This commit is contained in:
Nutcake 2023-05-04 21:54:51 +02:00
parent 4e5ac6a8d4
commit ca198a7cc4
4 changed files with 177 additions and 112 deletions

View file

@ -10,10 +10,11 @@ class Session {
final List<String> tags;
final bool headlessHost;
final String hostUsername;
final SessionAccessLevel accessLevel;
Session({required this.id, required this.name, required this.sessionUsers, required this.thumbnail,
required this.maxUsers, required this.hasEnded, required this.isValid, required this.description,
required this.tags, required this.headlessHost, required this.hostUsername,
required this.tags, required this.headlessHost, required this.hostUsername, required this.accessLevel,
});
factory Session.fromMap(Map map) {
@ -29,6 +30,7 @@ class Session {
tags: ((map["tags"] as List?) ?? []).map((e) => e.toString()).toList(),
headlessHost: map["headlessHost"] ?? false,
hostUsername: map["hostUsername"] ?? "",
accessLevel: SessionAccessLevel.fromName(map["accessLevel"]),
);
}
@ -45,12 +47,39 @@ class Session {
"tags": shallow ? [] : throw UnimplementedError(),
"headlessHost": headlessHost,
"hostUsername": hostUsername,
"accessLevel": accessLevel.name, // This probably wont work, the API usually expects integers.
};
}
bool get isLive => !hasEnded && isValid;
}
enum SessionAccessLevel {
unknown,
private,
friends,
friendsOfFriends,
anyone;
static const _readableNamesMap = {
SessionAccessLevel.unknown: "Unknown",
SessionAccessLevel.private: "Private",
SessionAccessLevel.friends: "Contacts",
SessionAccessLevel.friendsOfFriends: "Contacts+",
SessionAccessLevel.anyone: "Anyone",
};
factory SessionAccessLevel.fromName(String? name) {
return SessionAccessLevel.values.firstWhere((element) => element.name.toLowerCase() == name?.toLowerCase(),
orElse: () => SessionAccessLevel.unknown,
);
}
String toReadableString() {
return SessionAccessLevel._readableNamesMap[this] ?? "Unknown";
}
}
class SessionUser {
final String id;
final String username;
@ -61,10 +90,10 @@ class SessionUser {
factory SessionUser.fromMap(Map map) {
return SessionUser(
id: map["userID"],
username: map["username"],
isPresent: map["isPresent"],
outputDevice: map["outputDevice"],
id: map["userID"] ?? "",
username: map["username"] ?? "Unknown",
isPresent: map["isPresent"] ?? false,
outputDevice: map["outputDevice"] ?? 0,
);
}
}

View file

@ -27,31 +27,47 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer> {
void initState() {
super.initState();
if (Platform.isAndroid) {
_audioPlayer.setAudioSource(AudioSource.uri(Uri.parse(
Aux.neosDbToHttp(AudioClipContent.fromMap(jsonDecode(widget.message.content)).assetUri)
))).whenComplete(() => _audioPlayer.setLoopMode(LoopMode.off));
_audioPlayer.setUrl(
Aux.neosDbToHttp(AudioClipContent
.fromMap(jsonDecode(widget.message.content))
.assetUri),
preload: true).whenComplete(() => _audioPlayer.setLoopMode(LoopMode.off));
}
}
@override
Widget build(BuildContext context) {
if (!Platform.isAndroid) {
Widget _createErrorWidget(String error) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Icon(Icons.error_outline, color: Theme.of(context).colorScheme.error,),
Icon(Icons.error_outline, color: Theme
.of(context)
.colorScheme
.error,),
const SizedBox(height: 4,),
Text("Sorry, audio-messages are not\n supported on this platform.", textAlign: TextAlign.center,
Text(error, textAlign: TextAlign.center,
softWrap: true,
maxLines: 3,
style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Theme.of(context).colorScheme.error),
style: Theme
.of(context)
.textTheme
.bodySmall
?.copyWith(color: Theme
.of(context)
.colorScheme
.error),
),
],
),
);
}
@override
Widget build(BuildContext context) {
if (!Platform.isAndroid) {
return _createErrorWidget("Sorry, audio-messages are not\n supported on this platform.");
}
return IntrinsicWidth(
child: StreamBuilder<PlayerState>(
stream: _audioPlayer.playerStateStream,
@ -77,12 +93,18 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer> {
_audioPlayer.seek(Duration.zero);
_audioPlayer.play();
break;
}},
icon: playerState.processingState == ProcessingState.loading
}
},
icon: SizedBox(
width: 24,
height: 24,
child: playerState.processingState == ProcessingState.loading
? const Center(child: CircularProgressIndicator(),)
: Icon((_audioPlayer.duration! - _audioPlayer.position).inMilliseconds < 10 ? Icons.replay
: Icon(((_audioPlayer.duration ?? Duration.zero) - _audioPlayer.position).inMilliseconds <
10 ? Icons.replay
: (playerState.playing ? Icons.pause : Icons.play_arrow)),
),
),
StreamBuilder(
stream: _audioPlayer.positionStream,
builder: (context, snapshot) {
@ -118,7 +140,8 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer> {
StreamBuilder(
stream: _audioPlayer.positionStream,
builder: (context, snapshot) {
return Text("${snapshot.data?.format() ?? "??"}/${_audioPlayer.duration?.format() ?? "??"}");
return Text("${snapshot.data?.format() ?? "??"}/${_audioPlayer.duration?.format() ??
"??"}");
}
),
const Spacer(),
@ -134,7 +157,10 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer> {
),
),
const SizedBox(width: 4,),
if (widget.message.senderId == ClientHolder.of(context).apiClient.userId) Padding(
if (widget.message.senderId == ClientHolder
.of(context)
.apiClient
.userId) Padding(
padding: const EdgeInsets.only(right: 12.0),
child: MessageStateIndicator(messageState: widget.message.state),
),
@ -142,10 +168,13 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer> {
)
],
);
} else if (snapshot.hasError) {
FlutterError.reportError(FlutterErrorDetails(exception: snapshot.error!, stack: snapshot.stackTrace));
return _createErrorWidget("Failed to load audio-message.");
} else {
return const Center(child: CircularProgressIndicator(),);
}
},
}
),
);
}

View file

@ -26,14 +26,20 @@ class MessageSessionInvite extends StatelessWidget {
padding: const EdgeInsets.only(left: 10),
constraints: const BoxConstraints(maxWidth: 300),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(top: 4),
child: Text(sessionInfo.name, maxLines: null, softWrap: true, style: Theme.of(context).textTheme.titleMedium,),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(

View file

@ -583,6 +583,7 @@ class SessionPopup extends StatelessWidget {
style: Theme.of(context).textTheme.labelMedium,
softWrap: true,
),
Text("Access: ${session.accessLevel.toReadableString()}"),
Text("Users: ${session.sessionUsers.length}", style: Theme.of(context).textTheme.labelMedium),
Text("Maximum users: ${session.maxUsers}", style: Theme.of(context).textTheme.labelMedium),
Text("Headless: ${session.headlessHost ? "Yes" : "No"}", style: Theme.of(context).textTheme.labelMedium),