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 List<String> tags;
final bool headlessHost; final bool headlessHost;
final String hostUsername; final String hostUsername;
final SessionAccessLevel accessLevel;
Session({required this.id, required this.name, required this.sessionUsers, required this.thumbnail, 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.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) { factory Session.fromMap(Map map) {
@ -29,6 +30,7 @@ class Session {
tags: ((map["tags"] as List?) ?? []).map((e) => e.toString()).toList(), tags: ((map["tags"] as List?) ?? []).map((e) => e.toString()).toList(),
headlessHost: map["headlessHost"] ?? false, headlessHost: map["headlessHost"] ?? false,
hostUsername: map["hostUsername"] ?? "", hostUsername: map["hostUsername"] ?? "",
accessLevel: SessionAccessLevel.fromName(map["accessLevel"]),
); );
} }
@ -45,12 +47,39 @@ class Session {
"tags": shallow ? [] : throw UnimplementedError(), "tags": shallow ? [] : throw UnimplementedError(),
"headlessHost": headlessHost, "headlessHost": headlessHost,
"hostUsername": hostUsername, "hostUsername": hostUsername,
"accessLevel": accessLevel.name, // This probably wont work, the API usually expects integers.
}; };
} }
bool get isLive => !hasEnded && isValid; 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 { class SessionUser {
final String id; final String id;
final String username; final String username;
@ -61,10 +90,10 @@ class SessionUser {
factory SessionUser.fromMap(Map map) { factory SessionUser.fromMap(Map map) {
return SessionUser( return SessionUser(
id: map["userID"], id: map["userID"] ?? "",
username: map["username"], username: map["username"] ?? "Unknown",
isPresent: map["isPresent"], isPresent: map["isPresent"] ?? false,
outputDevice: map["outputDevice"], outputDevice: map["outputDevice"] ?? 0,
); );
} }
} }

View file

@ -27,31 +27,47 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer> {
void initState() { void initState() {
super.initState(); super.initState();
if (Platform.isAndroid) { if (Platform.isAndroid) {
_audioPlayer.setAudioSource(AudioSource.uri(Uri.parse( _audioPlayer.setUrl(
Aux.neosDbToHttp(AudioClipContent.fromMap(jsonDecode(widget.message.content)).assetUri) Aux.neosDbToHttp(AudioClipContent
))).whenComplete(() => _audioPlayer.setLoopMode(LoopMode.off)); .fromMap(jsonDecode(widget.message.content))
.assetUri),
preload: true).whenComplete(() => _audioPlayer.setLoopMode(LoopMode.off));
} }
} }
@override Widget _createErrorWidget(String error) {
Widget build(BuildContext context) {
if (!Platform.isAndroid) {
return Padding( return Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ 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,), 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, softWrap: true,
maxLines: 3, 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( return IntrinsicWidth(
child: StreamBuilder<PlayerState>( child: StreamBuilder<PlayerState>(
stream: _audioPlayer.playerStateStream, stream: _audioPlayer.playerStateStream,
@ -77,12 +93,18 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer> {
_audioPlayer.seek(Duration.zero); _audioPlayer.seek(Duration.zero);
_audioPlayer.play(); _audioPlayer.play();
break; break;
}}, }
icon: playerState.processingState == ProcessingState.loading },
icon: SizedBox(
width: 24,
height: 24,
child: playerState.processingState == ProcessingState.loading
? const Center(child: CircularProgressIndicator(),) ? 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)), : (playerState.playing ? Icons.pause : Icons.play_arrow)),
), ),
),
StreamBuilder( StreamBuilder(
stream: _audioPlayer.positionStream, stream: _audioPlayer.positionStream,
builder: (context, snapshot) { builder: (context, snapshot) {
@ -118,7 +140,8 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer> {
StreamBuilder( StreamBuilder(
stream: _audioPlayer.positionStream, stream: _audioPlayer.positionStream,
builder: (context, snapshot) { builder: (context, snapshot) {
return Text("${snapshot.data?.format() ?? "??"}/${_audioPlayer.duration?.format() ?? "??"}"); return Text("${snapshot.data?.format() ?? "??"}/${_audioPlayer.duration?.format() ??
"??"}");
} }
), ),
const Spacer(), const Spacer(),
@ -134,7 +157,10 @@ class _MessageAudioPlayerState extends State<MessageAudioPlayer> {
), ),
), ),
const SizedBox(width: 4,), 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), padding: const EdgeInsets.only(right: 12.0),
child: MessageStateIndicator(messageState: widget.message.state), 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 { } else {
return const Center(child: CircularProgressIndicator(),); return const Center(child: CircularProgressIndicator(),);
} }
}, }
), ),
); );
} }

View file

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

View file

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