Add session invites to message view
This commit is contained in:
parent
158fccbf3f
commit
38205fe8e1
5 changed files with 241 additions and 115 deletions
|
@ -4,6 +4,7 @@ import 'dart:developer';
|
||||||
import 'package:contacts_plus_plus/api_client.dart';
|
import 'package:contacts_plus_plus/api_client.dart';
|
||||||
import 'package:contacts_plus_plus/apis/message_api.dart';
|
import 'package:contacts_plus_plus/apis/message_api.dart';
|
||||||
import 'package:contacts_plus_plus/auxiliary.dart';
|
import 'package:contacts_plus_plus/auxiliary.dart';
|
||||||
|
import 'package:contacts_plus_plus/models/session.dart';
|
||||||
import 'package:uuid/uuid.dart';
|
import 'package:uuid/uuid.dart';
|
||||||
|
|
||||||
enum MessageType {
|
enum MessageType {
|
||||||
|
|
|
@ -9,10 +9,11 @@ class Session {
|
||||||
final String description;
|
final String description;
|
||||||
final List<String> tags;
|
final List<String> tags;
|
||||||
final bool headlessHost;
|
final bool headlessHost;
|
||||||
|
final String hostUsername;
|
||||||
|
|
||||||
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.tags, required this.headlessHost, required this.hostUsername,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Session.fromMap(Map map) {
|
factory Session.fromMap(Map map) {
|
||||||
|
@ -21,12 +22,13 @@ class Session {
|
||||||
name: map["name"],
|
name: map["name"],
|
||||||
sessionUsers: (map["sessionUsers"] as List? ?? []).map((entry) => SessionUser.fromMap(entry)).toList(),
|
sessionUsers: (map["sessionUsers"] as List? ?? []).map((entry) => SessionUser.fromMap(entry)).toList(),
|
||||||
thumbnail: map["thumbnail"] ?? "",
|
thumbnail: map["thumbnail"] ?? "",
|
||||||
maxUsers: map["maxUsers"],
|
maxUsers: map["maxUsers"] ?? 0,
|
||||||
hasEnded: map["hasEnded"],
|
hasEnded: map["hasEnded"] ?? false,
|
||||||
isValid: map["isValid"],
|
isValid: map["isValid"] ?? true,
|
||||||
description: map["description"] ?? "",
|
description: map["description"] ?? "",
|
||||||
tags: ((map["tags"] as List?) ?? []).map((e) => e.toString()).toList(),
|
tags: ((map["tags"] as List?) ?? []).map((e) => e.toString()).toList(),
|
||||||
headlessHost: map["headlessHost"],
|
headlessHost: map["headlessHost"] ?? false,
|
||||||
|
hostUsername: map["hostUsername"] ?? "",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
83
lib/widgets/message_session_invite.dart
Normal file
83
lib/widgets/message_session_invite.dart
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:contacts_plus_plus/api_client.dart';
|
||||||
|
import 'package:contacts_plus_plus/auxiliary.dart';
|
||||||
|
import 'package:contacts_plus_plus/models/message.dart';
|
||||||
|
import 'package:contacts_plus_plus/models/session.dart';
|
||||||
|
import 'package:contacts_plus_plus/widgets/generic_avatar.dart';
|
||||||
|
import 'package:contacts_plus_plus/widgets/messages.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
class MessageSessionInvite extends StatelessWidget {
|
||||||
|
MessageSessionInvite({required this.message, super.key});
|
||||||
|
final DateFormat _dateFormat = DateFormat.Hm();
|
||||||
|
final Message message;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final sessionInfo = Session.fromMap(jsonDecode(message.content));
|
||||||
|
return TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(context: context, builder: (context) => SessionPopup(session: sessionInfo));
|
||||||
|
},
|
||||||
|
style: TextButton.styleFrom(padding: EdgeInsets.zero),
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.only(left: 10),
|
||||||
|
constraints: const BoxConstraints(maxWidth: 300),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(sessionInfo.name, maxLines: null, softWrap: true, style: Theme.of(context).textTheme.titleMedium,),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
GenericAvatar(
|
||||||
|
imageUri: Aux.neosDbToHttp(Aux.neosDbToHttp(sessionInfo.thumbnail)),
|
||||||
|
placeholderIcon: Icons.no_photography,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4,),
|
||||||
|
Text("${sessionInfo.sessionUsers.length}/${sessionInfo.maxUsers}")
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8,),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(child: Text("Hosted by ${sessionInfo.hostUsername}", overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.white60),)),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||||
|
child: Text(
|
||||||
|
_dateFormat.format(message.sendTime),
|
||||||
|
style: Theme
|
||||||
|
.of(context)
|
||||||
|
.textTheme
|
||||||
|
.labelMedium
|
||||||
|
?.copyWith(color: Colors.white54),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4,),
|
||||||
|
if (message.senderId == ClientHolder.of(context).client.userId) Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 12.0),
|
||||||
|
child: MessageStateIndicator(messageState: message.state),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
@ -7,8 +6,9 @@ import 'package:contacts_plus_plus/auxiliary.dart';
|
||||||
import 'package:contacts_plus_plus/models/friend.dart';
|
import 'package:contacts_plus_plus/models/friend.dart';
|
||||||
import 'package:contacts_plus_plus/models/message.dart';
|
import 'package:contacts_plus_plus/models/message.dart';
|
||||||
import 'package:contacts_plus_plus/models/session.dart';
|
import 'package:contacts_plus_plus/models/session.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/audio_clip_player.dart';
|
import 'package:contacts_plus_plus/widgets/message_audio_player.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/generic_avatar.dart';
|
import 'package:contacts_plus_plus/widgets/generic_avatar.dart';
|
||||||
|
import 'package:contacts_plus_plus/widgets/message_session_invite.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
@ -338,8 +338,26 @@ class MyMessageBubble extends StatelessWidget {
|
||||||
var content = message.content;
|
var content = message.content;
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case MessageType.sessionInvite:
|
case MessageType.sessionInvite:
|
||||||
content = "[Session Invite]";
|
return Row(
|
||||||
continue rawText;
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
color: Theme
|
||||||
|
.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primaryContainer,
|
||||||
|
margin: const EdgeInsets.only(left: 32, bottom: 16, right: 12),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 12),
|
||||||
|
child: MessageSessionInvite(message: message,),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
case MessageType.object:
|
case MessageType.object:
|
||||||
content = "[Asset]";
|
content = "[Asset]";
|
||||||
continue rawText;
|
continue rawText;
|
||||||
|
@ -435,17 +453,28 @@ class OtherMessageBubble extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var content = message.content;
|
var content = message.content;
|
||||||
if (message.type == MessageType.sessionInvite) {
|
|
||||||
content = "[Session Invite]";
|
|
||||||
} else if (message.type == MessageType.sound) {
|
|
||||||
content = "[Voice Message]";
|
|
||||||
} else if (message.type == MessageType.object) {
|
|
||||||
content = "[Asset]";
|
|
||||||
}
|
|
||||||
switch (message.type) {
|
switch (message.type) {
|
||||||
case MessageType.sessionInvite:
|
case MessageType.sessionInvite:
|
||||||
content = "[Session Invite]";
|
return Row(
|
||||||
continue rawText;
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Card(
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
color: Theme
|
||||||
|
.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.secondaryContainer,
|
||||||
|
margin: const EdgeInsets.only(right: 32, bottom: 16, left: 12),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||||
|
child: MessageSessionInvite(message: message,),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
case MessageType.object:
|
case MessageType.object:
|
||||||
content = "[Asset]";
|
content = "[Asset]";
|
||||||
continue rawText;
|
continue rawText;
|
||||||
|
@ -500,7 +529,8 @@ class OtherMessageBubble extends StatelessWidget {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [Card(
|
children: [
|
||||||
|
Card(
|
||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(16),
|
borderRadius: BorderRadius.circular(16),
|
||||||
),
|
),
|
||||||
|
@ -547,15 +577,13 @@ class MessageStateIndicator extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SessionTile extends StatelessWidget {
|
class SessionPopup extends StatelessWidget {
|
||||||
const SessionTile({required this.session, super.key});
|
const SessionPopup({required this.session, super.key});
|
||||||
|
|
||||||
final Session session;
|
final Session session;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
showDialog(context: context, builder: (context) {
|
|
||||||
final ScrollController userListScrollController = ScrollController();
|
final ScrollController userListScrollController = ScrollController();
|
||||||
final thumbnailUri = Aux.neosDbToHttp(session.thumbnail);
|
final thumbnailUri = Aux.neosDbToHttp(session.thumbnail);
|
||||||
return Dialog(
|
return Dialog(
|
||||||
|
@ -625,7 +653,7 @@ class SessionTile extends StatelessWidget {
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Center(
|
child: Center(
|
||||||
child: thumbnailUri.isEmpty ? const Text("No Image") : CachedNetworkImage(
|
child: CachedNetworkImage(
|
||||||
imageUrl: thumbnailUri,
|
imageUrl: thumbnailUri,
|
||||||
placeholder: (context, url) {
|
placeholder: (context, url) {
|
||||||
return const CircularProgressIndicator();
|
return const CircularProgressIndicator();
|
||||||
|
@ -649,7 +677,19 @@ class SessionTile extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class SessionTile extends StatelessWidget {
|
||||||
|
const SessionTile({required this.session, super.key});
|
||||||
|
final Session session;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(context: context, builder: (context) => SessionPopup(session: session));
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|
Loading…
Reference in a new issue