-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1657 from nextcloud/feat/neon_talk/room-list
- Loading branch information
Showing
17 changed files
with
580 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
{ | ||
"@@locale": "en" | ||
"@@locale": "en", | ||
"actorSelf": "You" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,70 @@ | ||
import 'dart:async'; | ||
|
||
import 'package:built_collection/built_collection.dart'; | ||
import 'package:meta/meta.dart'; | ||
import 'package:neon_framework/blocs.dart'; | ||
import 'package:neon_framework/models.dart'; | ||
import 'package:neon_framework/utils.dart'; | ||
import 'package:nextcloud/spreed.dart' as spreed; | ||
import 'package:rxdart/rxdart.dart'; | ||
|
||
/// Bloc for fetching Talk rooms | ||
sealed class TalkBloc implements InteractiveBloc { | ||
/// Creates a new Talk Bloc instance. | ||
@internal | ||
factory TalkBloc(Account account) => _TalkBloc(account); | ||
|
||
/// The list of rooms. | ||
BehaviorSubject<Result<BuiltList<spreed.Room>>> get rooms; | ||
|
||
/// The total number of unread messages. | ||
BehaviorSubject<int> get unreadCounter; | ||
} | ||
|
||
class _TalkBloc extends InteractiveBloc implements TalkBloc { | ||
_TalkBloc(this.account); | ||
_TalkBloc(this.account) { | ||
rooms.listen((result) { | ||
if (!result.hasData) { | ||
return; | ||
} | ||
|
||
var unread = 0; | ||
for (final room in result.requireData) { | ||
unread += room.unreadMessages; | ||
} | ||
unreadCounter.add(unread); | ||
}); | ||
|
||
unawaited(refresh()); | ||
} | ||
|
||
final Account account; | ||
|
||
@override | ||
Future<void> refresh() async {} | ||
final rooms = BehaviorSubject(); | ||
|
||
@override | ||
final unreadCounter = BehaviorSubject(); | ||
|
||
@override | ||
void dispose() { | ||
unawaited(rooms.close()); | ||
unawaited(unreadCounter.close()); | ||
super.dispose(); | ||
} | ||
|
||
@override | ||
Future<void> refresh() async { | ||
await RequestManager.instance.wrapNextcloud( | ||
account: account, | ||
cacheKey: 'talk-rooms', | ||
subject: rooms, | ||
rawResponse: account.client.spreed.room.getRoomsRaw(), | ||
unwrap: (response) => BuiltList( | ||
response.body.ocs.data.rebuild( | ||
(b) => b.sort((a, b) => b.lastActivity.compareTo(a.lastActivity)), | ||
), | ||
), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,81 @@ | ||
import 'dart:async'; | ||
|
||
import 'package:flutter/material.dart'; | ||
import 'package:neon_framework/blocs.dart'; | ||
import 'package:neon_framework/utils.dart'; | ||
import 'package:neon_framework/widgets.dart'; | ||
import 'package:neon_talk/src/blocs/talk.dart'; | ||
import 'package:neon_talk/src/widgets/message_preview.dart'; | ||
import 'package:neon_talk/src/widgets/unread_indicator.dart'; | ||
import 'package:nextcloud/spreed.dart' as spreed; | ||
|
||
/// The main page displaying the chat list. | ||
class TalkMainPage extends StatelessWidget { | ||
class TalkMainPage extends StatefulWidget { | ||
/// Creates a new Talk main page. | ||
const TalkMainPage({super.key}); | ||
|
||
@override | ||
Widget build(BuildContext context) => const Placeholder(); | ||
State<TalkMainPage> createState() => _TalkMainPageState(); | ||
} | ||
|
||
class _TalkMainPageState extends State<TalkMainPage> { | ||
late String actorId; | ||
late TalkBloc bloc; | ||
late StreamSubscription<Object> errorsSubscription; | ||
|
||
@override | ||
void initState() { | ||
super.initState(); | ||
|
||
actorId = NeonProvider.of<AccountsBloc>(context).activeAccount.value!.username; | ||
bloc = NeonProvider.of<TalkBloc>(context); | ||
errorsSubscription = bloc.errors.listen((error) { | ||
NeonError.showSnackbar(context, error); | ||
}); | ||
} | ||
|
||
@override | ||
void dispose() { | ||
unawaited(errorsSubscription.cancel()); | ||
super.dispose(); | ||
} | ||
|
||
@override | ||
Widget build(BuildContext context) => ResultBuilder.behaviorSubject( | ||
subject: bloc.rooms, | ||
builder: (context, rooms) => NeonListView( | ||
scrollKey: 'talk-rooms', | ||
isLoading: rooms.isLoading, | ||
error: rooms.error, | ||
onRefresh: bloc.refresh, | ||
itemCount: rooms.data?.length ?? 0, | ||
itemBuilder: (context, index) => buildRoom(rooms.requireData[index]), | ||
), | ||
); | ||
|
||
Widget buildRoom(spreed.Room room) { | ||
Widget? subtitle; | ||
Widget? trailing; | ||
|
||
final lastChatMessage = room.lastMessage.chatMessage; | ||
if (lastChatMessage != null) { | ||
subtitle = TalkMessagePreview( | ||
actorId: actorId, | ||
roomType: spreed.RoomType.fromValue(room.type), | ||
chatMessage: lastChatMessage, | ||
); | ||
} | ||
|
||
if (room.unreadMessages > 0) { | ||
trailing = TalkUnreadIndicator( | ||
room: room, | ||
); | ||
} | ||
|
||
return ListTile( | ||
title: Text(room.displayName), | ||
subtitle: subtitle, | ||
trailing: trailing, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:nextcloud/spreed.dart' as spreed; | ||
|
||
/// Builds a [TextSpan] for the given [chatMessage]. | ||
TextSpan buildChatMessage({ | ||
required spreed.ChatMessage chatMessage, | ||
}) => | ||
TextSpan( | ||
text: chatMessage.message, | ||
); |
56 changes: 56 additions & 0 deletions
56
packages/neon/neon_talk/lib/src/widgets/message_preview.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:neon_talk/l10n/localizations.dart'; | ||
import 'package:neon_talk/src/utils/message.dart'; | ||
import 'package:nextcloud/spreed.dart' as spreed; | ||
|
||
/// Displays a preview of the [chatMessage] including the display name of the sender. | ||
class TalkMessagePreview extends StatelessWidget { | ||
/// Creates a new Talk message preview. | ||
const TalkMessagePreview({ | ||
required this.actorId, | ||
required this.roomType, | ||
required this.chatMessage, | ||
super.key, | ||
}); | ||
|
||
/// ID of the current actor. | ||
final String actorId; | ||
|
||
/// Type of the room | ||
final spreed.RoomType roomType; | ||
|
||
/// The chat message to preview. | ||
final spreed.ChatMessage chatMessage; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
String? actorName; | ||
if (chatMessage.actorId == actorId) { | ||
actorName = TalkLocalizations.of(context).actorSelf; | ||
} else if (!roomType.isSingleUser) { | ||
actorName = chatMessage.actorDisplayName; | ||
} | ||
|
||
return RichText( | ||
maxLines: 1, | ||
overflow: TextOverflow.ellipsis, | ||
text: TextSpan( | ||
style: TextStyle( | ||
color: Theme.of(context).colorScheme.onBackground, | ||
), | ||
children: [ | ||
if (actorName != null) | ||
TextSpan( | ||
text: '$actorName: ', | ||
style: const TextStyle( | ||
fontWeight: FontWeight.bold, | ||
), | ||
), | ||
buildChatMessage( | ||
chatMessage: chatMessage, | ||
), | ||
], | ||
), | ||
); | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
packages/neon/neon_talk/lib/src/widgets/unread_indicator.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:nextcloud/spreed.dart' as spreed; | ||
|
||
/// Displays the number of unread messages and whether the user was mentioned for a given [room]. | ||
class TalkUnreadIndicator extends StatelessWidget { | ||
/// Creates a new Talk unread indicator. | ||
const TalkUnreadIndicator({ | ||
required this.room, | ||
super.key, | ||
}); | ||
|
||
/// The room that the indicator will display unread messages and mentions for. | ||
final spreed.Room room; | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
assert(room.unreadMessages > 0, 'Need at least on unread message'); | ||
|
||
final colorScheme = Theme.of(context).colorScheme; | ||
|
||
final highlight = room.unreadMention || spreed.RoomType.fromValue(room.type).isSingleUser; | ||
final backgroundColor = highlight ? colorScheme.primaryContainer : colorScheme.background; | ||
final textColor = highlight ? colorScheme.onPrimaryContainer : colorScheme.onBackground; | ||
|
||
Widget? avatar; | ||
if (room.unreadMentionDirect) { | ||
avatar = Icon( | ||
Icons.alternate_email, | ||
size: 20, | ||
color: textColor, | ||
); | ||
} | ||
|
||
return Chip( | ||
shape: RoundedRectangleBorder( | ||
borderRadius: const BorderRadius.all(Radius.circular(50)), | ||
side: BorderSide( | ||
color: colorScheme.primaryContainer, | ||
), | ||
), | ||
padding: const EdgeInsets.all(2), | ||
backgroundColor: backgroundColor, | ||
avatar: avatar, | ||
label: Text( | ||
room.unreadMessages.toString(), | ||
style: TextStyle( | ||
fontWeight: FontWeight.bold, | ||
fontFamily: 'monospace', | ||
color: textColor, | ||
), | ||
), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.