fluffychat/lib/pages/chat_list/stories_header.dart

293 lines
10 KiB
Dart
Raw Normal View History

2021-12-24 14:18:09 +01:00
import 'package:flutter/material.dart';
import 'package:adaptive_dialog/adaptive_dialog.dart';
2022-04-08 10:12:58 +02:00
import 'package:collection/collection.dart';
2021-12-24 14:18:09 +01:00
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:future_loading_dialog/future_loading_dialog.dart';
import 'package:matrix/matrix.dart';
import 'package:vrouter/vrouter.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions.dart/client_stories_extension.dart';
import 'package:fluffychat/widgets/avatar.dart';
import 'package:fluffychat/widgets/matrix.dart';
2021-12-25 18:47:36 +01:00
enum ContextualRoomAction {
mute,
unmute,
leave,
}
2021-12-24 14:18:09 +01:00
class StoriesHeader extends StatelessWidget {
const StoriesHeader({Key? key}) : super(key: key);
void _addToStoryAction(BuildContext context) =>
VRouter.of(context).to('/stories/create');
void _goToStoryAction(BuildContext context, String roomId) async {
final room = Matrix.of(context).client.getRoomById(roomId);
if (room == null) return;
if (room.membership != Membership.join) {
final result = await showFutureLoadingDialog(
context: context,
future: room.join,
);
if (result.error != null) return;
}
VRouter.of(context).toSegments(['stories', roomId]);
}
2021-12-24 14:18:09 +01:00
void _contextualActions(BuildContext context, Room room) async {
final action = await showModalActionSheet<ContextualRoomAction>(
2021-12-25 14:07:48 +01:00
cancelLabel: L10n.of(context)!.cancel,
2021-12-24 14:18:09 +01:00
context: context,
actions: [
if (room.pushRuleState != PushRuleState.notify)
SheetAction(
label: L10n.of(context)!.unmuteChat,
key: ContextualRoomAction.unmute,
icon: Icons.notifications_outlined,
)
else
SheetAction(
label: L10n.of(context)!.muteChat,
key: ContextualRoomAction.mute,
icon: Icons.notifications_off_outlined,
),
SheetAction(
label: L10n.of(context)!.unsubscribeStories,
key: ContextualRoomAction.leave,
icon: Icons.unsubscribe_outlined,
isDestructiveAction: true,
),
],
);
if (action == null) return;
switch (action) {
case ContextualRoomAction.mute:
await showFutureLoadingDialog(
context: context,
future: () => room.setPushRuleState(PushRuleState.dontNotify),
);
break;
case ContextualRoomAction.unmute:
await showFutureLoadingDialog(
context: context,
future: () => room.setPushRuleState(PushRuleState.notify),
);
break;
case ContextualRoomAction.leave:
await showFutureLoadingDialog(
context: context,
future: () => room.leave(),
);
break;
}
}
@override
Widget build(BuildContext context) {
final client = Matrix.of(context).client;
return StreamBuilder(
stream: Matrix.of(context).onShareContentChanged.stream,
builder: (context, _) => StreamBuilder<Object>(
stream: client.onSync.stream
.where((syncUpdate) => syncUpdate.hasRoomUpdate),
builder: (context, snapshot) {
if (Matrix.of(context).shareContent != null) {
return ListTile(
leading: CircleAvatar(
radius: Avatar.defaultSize / 2,
backgroundColor: Theme.of(context).colorScheme.surface,
foregroundColor: Theme.of(context).textTheme.bodyText1?.color,
2022-02-06 20:59:30 +01:00
child: const Icon(Icons.camera_alt_outlined),
2021-12-24 14:18:09 +01:00
),
title: Text(L10n.of(context)!.addToStory),
onTap: () => _addToStoryAction(context),
);
}
if (client.storiesRooms.isEmpty) {
return Container();
}
2022-04-08 10:12:58 +02:00
final ownStoryRoom = client.storiesRooms
.firstWhereOrNull((r) => r.creatorId == client.userID);
final stories = [
if (ownStoryRoom != null) ownStoryRoom,
...client.storiesRooms..remove(ownStoryRoom),
];
return SizedBox(
2022-02-06 20:59:30 +01:00
height: 106,
child: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 2),
scrollDirection: Axis.horizontal,
itemCount: stories.length,
itemBuilder: (context, i) {
final room = stories[i];
return Opacity(
opacity: room.hasPosts ? 1 : 0.75,
child: FutureBuilder<Profile>(
future: room.getCreatorProfile(),
builder: (context, snapshot) {
2022-04-08 10:12:58 +02:00
final userId = room.creatorId;
final displayname = snapshot.data?.displayName ??
userId?.localpart ??
'Unknown';
final avatarUrl = snapshot.data?.avatarUrl;
return _StoryButton(
profile: Profile(
displayName: displayname,
avatarUrl: avatarUrl,
2022-04-08 10:12:58 +02:00
userId: userId ?? 'Unknown',
),
showEditFab: userId == client.userID,
unread: room.membership == Membership.invite ||
room.hasNewMessages,
onPressed: () => _goToStoryAction(context, room.id),
onLongPressed: () =>
_contextualActions(context, room),
);
}),
);
},
),
);
}),
);
2021-12-24 14:18:09 +01:00
}
}
extension on Room {
2022-02-18 20:25:09 +01:00
Future<Profile> getCreatorProfile() =>
client.getProfileFromUserId(getState(EventTypes.RoomCreate)!.senderId);
2021-12-25 14:07:48 +01:00
bool get hasPosts {
2021-12-26 08:33:57 +01:00
if (membership == Membership.invite) return true;
2021-12-25 14:07:48 +01:00
final lastEvent = this.lastEvent;
if (lastEvent == null) return false;
if (lastEvent.type != EventTypes.Message) return false;
if (DateTime.now().difference(lastEvent.originServerTs).inHours >
ClientStoriesExtension.lifeTimeInHours) {
return false;
}
return true;
}
2021-12-24 14:18:09 +01:00
}
class _StoryButton extends StatelessWidget {
final Profile profile;
final bool showEditFab;
final bool unread;
2021-12-24 14:18:09 +01:00
final void Function() onPressed;
final void Function()? onLongPressed;
const _StoryButton({
required this.profile,
2021-12-24 14:18:09 +01:00
required this.onPressed,
this.showEditFab = false,
2021-12-24 14:18:09 +01:00
this.unread = false,
this.onLongPressed,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
2021-12-25 18:47:36 +01:00
width: 78,
2021-12-24 14:18:09 +01:00
child: InkWell(
2022-02-06 20:59:30 +01:00
borderRadius: BorderRadius.circular(7),
2021-12-24 14:18:09 +01:00
onTap: onPressed,
onLongPress: onLongPressed,
child: Padding(
2021-12-25 18:47:36 +01:00
padding: const EdgeInsets.symmetric(horizontal: 4),
2021-12-24 14:18:09 +01:00
child: Column(
children: [
const SizedBox(height: 8),
2022-02-06 20:59:30 +01:00
Material(
elevation: Theme.of(context).appBarTheme.elevation ?? 7,
shadowColor: Theme.of(context).appBarTheme.shadowColor,
borderRadius: BorderRadius.circular(Avatar.defaultSize),
child: Container(
padding: const EdgeInsets.all(2),
decoration: BoxDecoration(
gradient: unread
? const LinearGradient(
colors: [
Colors.red,
Colors.purple,
Colors.orange,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
)
: null,
color: unread ? null : Theme.of(context).dividerColor,
borderRadius: BorderRadius.circular(Avatar.defaultSize),
),
child: Stack(
children: [
Material(
color: Theme.of(context).colorScheme.surface,
borderRadius: BorderRadius.circular(Avatar.defaultSize),
child: Padding(
padding: const EdgeInsets.all(2.0),
child: CircleAvatar(
radius: 30,
backgroundColor:
Theme.of(context).colorScheme.surface,
foregroundColor:
Theme.of(context).textTheme.bodyText1?.color,
child: Avatar(
mxContent: profile.avatarUrl,
name: profile.displayName,
size: 100,
fontSize: 24,
),
),
),
2022-02-06 20:59:30 +01:00
),
if (showEditFab)
Positioned(
right: 0,
bottom: 0,
child: SizedBox(
width: 24,
height: 24,
child: FloatingActionButton.small(
backgroundColor:
Theme.of(context).backgroundColor,
foregroundColor: Theme.of(context).primaryColor,
onPressed: () =>
VRouter.of(context).to('/stories/create'),
child: const Icon(
Icons.add_outlined,
size: 16,
),
),
),
),
],
2021-12-25 18:47:36 +01:00
),
2021-12-24 14:18:09 +01:00
),
),
const SizedBox(height: 8),
Text(
profile.displayName ?? '',
2021-12-24 14:18:09 +01:00
maxLines: 1,
textAlign: TextAlign.center,
2021-12-25 18:47:36 +01:00
style: TextStyle(
2021-12-24 14:18:09 +01:00
fontSize: 12,
2021-12-25 18:47:36 +01:00
fontWeight: unread ? FontWeight.bold : null,
2021-12-24 14:18:09 +01:00
),
),
2022-02-06 20:59:30 +01:00
const SizedBox(height: 8),
2021-12-24 14:18:09 +01:00
],
),
),
),
);
}
}
2022-04-08 10:12:58 +02:00
extension on Room {
String? get creatorId => getState(EventTypes.RoomCreate)?.senderId;
}