diff --git a/lib/controllers/power_level_controller.dart b/lib/controllers/power_level_controller.dart index 93e4ba3..cd7075c 100644 --- a/lib/controllers/power_level_controller.dart +++ b/lib/controllers/power_level_controller.dart @@ -24,10 +24,9 @@ class PowerLevelController extends Notifier { final event = room?.events.firstWhereOrNull( (event) => event.rowId == room.state[EventType.powerLevels.type]?[""], ); + final content = event?.content ?? PowerLevelsContent(); final user = ref.watch(ClientStateController.provider)?.userId; - if (user == null || event?.content is! PowerLevelsContent) return false; - - final content = event?.content as PowerLevelsContent; + if (user == null || content is! PowerLevelsContent) return false; int powerLevelOf(String userId) => content.users[userId] ?? content.usersDefault; @@ -36,7 +35,8 @@ class PowerLevelController extends Notifier { return switch (config) { EventPowerLevelConfig(:final eventType) => - userLevel > (content.events[eventType.type] ?? content.eventsDefault), + userLevel >= (content.events[eventType.type] ?? content.eventsDefault), + MembershipActionPowerLevelConfig(:final action, :final targetUser) => switch (action) { MembershipAction.invite => userLevel >= content.invite, @@ -51,7 +51,8 @@ class PowerLevelController extends Notifier { }, StatePowerLevelConfig(:final eventType) => - userLevel > (content.events[eventType.type] ?? content.stateDefault), + userLevel >= (content.events[eventType.type] ?? content.stateDefault), + RedactionPowerLevelConfig(:final targetUser) => userLevel >= (targetUser == user diff --git a/lib/models/content/membership.dart b/lib/models/content/membership.dart index aa5a36d..7e00811 100644 --- a/lib/models/content/membership.dart +++ b/lib/models/content/membership.dart @@ -7,6 +7,7 @@ part "membership.g.dart"; @freezed abstract class MembershipContent extends Content with _$MembershipContent { MembershipContent._(); + factory MembershipContent({ @JsonKey(name: "displayname") required String? displayName, @JsonKey(name: "membership") required MembershipStatus status, diff --git a/lib/models/content/power_levels.dart b/lib/models/content/power_levels.dart index 594dac0..bff41b0 100644 --- a/lib/models/content/power_levels.dart +++ b/lib/models/content/power_levels.dart @@ -1,7 +1,6 @@ import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:freezed_annotation/freezed_annotation.dart"; import "package:nexus/models/content/content.dart"; - part "power_levels.freezed.dart"; part "power_levels.g.dart"; diff --git a/lib/widgets/chat_page/composer/relation_preview.dart b/lib/widgets/chat_page/composer/relation_preview.dart index e8dabb1..f795d6d 100644 --- a/lib/widgets/chat_page/composer/relation_preview.dart +++ b/lib/widgets/chat_page/composer/relation_preview.dart @@ -54,7 +54,13 @@ class RelationPreview extends ConsumerWidget { ), ), Expanded( - child: EventText(relatedEvent!, textOnly: true, maxLines: 1), + child: IgnorePointer( + child: EventText( + relatedEvent!, + textOnly: true, + maxLines: 1, + ), + ), ), ], ), diff --git a/lib/widgets/chat_page/event_text.dart b/lib/widgets/chat_page/event_text.dart index 924dd79..e35b06f 100644 --- a/lib/widgets/chat_page/event_text.dart +++ b/lib/widgets/chat_page/event_text.dart @@ -1,11 +1,16 @@ -import "dart:convert"; import "package:fast_immutable_collections/fast_immutable_collections.dart"; import "package:flutter/material.dart"; +import "package:nexus/models/content/avatar.dart"; +import "package:nexus/models/content/message.dart"; import "package:nexus/models/event.dart"; +import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart"; +import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart"; +import "package:timeago/timeago.dart"; class EventText extends StatelessWidget { final Event event; final bool textOnly; + final bool isGrouped; final int? maxLines; final VoidCallback? onTapReply; final IList Function(Event event)? getEventOptions; @@ -13,6 +18,7 @@ class EventText extends StatelessWidget { this.event, { this.onTapReply, this.textOnly = false, + this.isGrouped = false, this.maxLines, this.getEventOptions, super.key, @@ -20,6 +26,81 @@ class EventText extends StatelessWidget { @override Widget build(BuildContext context) { - return Text(json.encode(event.toJson())); // NEXT TODO + final theme = Theme.of(context); + final timestamp = Tooltip( + message: event.timestamp.toString(), + child: Text( + format(event.timestamp), + style: theme.textTheme.labelSmall?.copyWith(color: Colors.grey), + ), + ); + + return switch (event.content) { + MessageContent() => Row( + spacing: 8, + children: [ + isGrouped ? SizedBox(width: 40) : MessageAvatar(event, height: 40), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (!isGrouped) + Row( + spacing: 4, + children: [ + MessageDisplayname( + event, + style: TextStyle(fontWeight: FontWeight.bold), + ), + Expanded(child: timestamp), + ], + ), + Text("data"), + ], + ), + ), + ], + ), + AvatarContent() => Row( + spacing: 4, + children: [ + SizedBox(width: 4), + Icon(Icons.numbers), + MessageDisplayname( + event, + style: TextStyle( + color: theme.colorScheme.primary, + fontWeight: FontWeight.bold, + ), + ), + Text("changed the room avatar"), + ], + ), + _ => Text("AAAAA"), + }; } } + +// ExpandableImage( +// url, +// child: ClipRRect( +// borderRadius: BorderRadius.all(Radius.circular(8)), +// child: Image( +// image: CachedNetworkImage( +// url, +// ref.watch(CrossCacheController.provider), +// headers: ref.headers, +// ), +// width: width, +// height: height, +// loadingBuilder: (context, child, loadingProgress) => +// blurHash == null ? Loading() : BlurHash(hash: blurHash!), +// errorBuilder: (context, error, stackTrace) => Center( +// child: Text( +// "Image Failed to Load", +// style: TextStyle(color: Theme.of(context).colorScheme.error), +// ), +// ), +// ), +// ), +// ) diff --git a/lib/widgets/chat_page/expandable_image_message.dart b/lib/widgets/chat_page/expandable_image_message.dart deleted file mode 100644 index 5bc2c20..0000000 --- a/lib/widgets/chat_page/expandable_image_message.dart +++ /dev/null @@ -1,48 +0,0 @@ -import "package:cross_cache/cross_cache.dart"; -import "package:flutter/material.dart"; -import "package:flutter_blurhash/flutter_blurhash.dart"; -import "package:flutter_riverpod/flutter_riverpod.dart"; -import "package:nexus/controllers/cross_cache_controller.dart"; -import "package:nexus/helpers/extensions/get_headers.dart"; -import "package:nexus/widgets/chat_page/expandable_image.dart"; -import "package:nexus/widgets/loading.dart"; - -class ExpandableImageMessage extends ConsumerWidget { - final String url; - final double? width; - final double? height; - final String? blurHash; - - const ExpandableImageMessage( - this.url, { - this.width, - this.height, - this.blurHash, - super.key, - }); - - @override - Widget build(BuildContext context, WidgetRef ref) => ExpandableImage( - url, - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(8)), - child: Image( - image: CachedNetworkImage( - url, - ref.watch(CrossCacheController.provider), - headers: ref.headers, - ), - width: width, - height: height, - loadingBuilder: (context, child, loadingProgress) => - blurHash == null ? Loading() : BlurHash(hash: blurHash!), - errorBuilder: (context, error, stackTrace) => Center( - child: Text( - "Image Failed to Load", - style: TextStyle(color: Theme.of(context).colorScheme.error), - ), - ), - ), - ), - ); -} diff --git a/lib/widgets/chat_page/room_chat.dart b/lib/widgets/chat_page/room_chat.dart index dbd6f44..41a5d08 100644 --- a/lib/widgets/chat_page/room_chat.dart +++ b/lib/widgets/chat_page/room_chat.dart @@ -21,7 +21,7 @@ import "package:nexus/widgets/chat_page/emoji_picker_button.dart"; import "package:nexus/widgets/chat_page/event_text.dart"; import "package:nexus/widgets/chat_page/member_list.dart"; import "package:nexus/widgets/chat_page/room_appbar.dart"; -import "package:nexus/widgets/chat_page/wrappers/message_wrapper.dart"; +import "package:nexus/widgets/chat_page/wrappers/event_wrapper.dart"; import "package:nexus/widgets/error_dialog.dart"; import "package:nexus/widgets/form_text_input.dart"; import "package:nexus/main.dart"; @@ -320,25 +320,28 @@ class RoomChat extends HookConsumerWidget { SuperSliverList.builder( listController: listController.value, itemCount: value.length, - itemBuilder: (_, index) => MessageWrapper( - value[index], - EventText( + itemBuilder: (_, index) => Padding( + padding: EdgeInsets.symmetric(vertical: 8), + child: EventWrapper( value[index], - onTapReply: () => - listController.value.animateToItem( - index: index, - scrollController: scrollController, - alignment: 0.5, - duration: (_) => - Duration(milliseconds: 250), - curve: (_) => Curves.easeInOut, - ), - getEventOptions: getEventOptions, + EventText( + value[index], + onTapReply: () => + listController.value.animateToItem( + index: index, + scrollController: scrollController, + alignment: 0.5, + duration: (_) => + Duration(milliseconds: 250), + curve: (_) => Curves.easeInOut, + ), + getEventOptions: getEventOptions, + // TODO: Reimplement grouping + isGrouped: false, + ), + // TODO: Reimplement flashing + isFlashing: false, ), - // TODO: Reimplement grouping - isGrouped: false, - // TODO: Reimplement flashing - isFlashing: false, ), ), ], diff --git a/lib/widgets/chat_page/wrappers/event_wrapper.dart b/lib/widgets/chat_page/wrappers/event_wrapper.dart new file mode 100644 index 0000000..fb765da --- /dev/null +++ b/lib/widgets/chat_page/wrappers/event_wrapper.dart @@ -0,0 +1,46 @@ +import "package:flutter/material.dart"; +import "package:nexus/models/event.dart"; +import "package:nexus/widgets/chat_page/wrappers/reaction_row.dart"; + +class EventWrapper extends StatelessWidget { + final Event event; + final Widget child; + final bool isFlashing; + const EventWrapper( + this.event, + this.child, { + this.isFlashing = false, + super.key, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(12)), + child: AnimatedContainer( + padding: isFlashing ? EdgeInsets.all(8) : EdgeInsets.all(0), + color: isFlashing + ? Theme.of(context).colorScheme.onSurface.withAlpha(50) + : Colors.transparent, + duration: Duration(milliseconds: 250), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 4, + children: [ + child, + if (event.sendError != null && event.sendError != "not sent") + Text( + event.sendError!, + style: theme.textTheme.labelSmall?.copyWith( + color: theme.colorScheme.error, + ), + ), + ReactionRow(event), + ], + ), + ), + ); + } +} diff --git a/lib/widgets/chat_page/wrappers/message_wrapper.dart b/lib/widgets/chat_page/wrappers/message_wrapper.dart deleted file mode 100644 index 620d859..0000000 --- a/lib/widgets/chat_page/wrappers/message_wrapper.dart +++ /dev/null @@ -1,83 +0,0 @@ -import "package:flutter/material.dart"; -import "package:nexus/models/event.dart"; -import "package:nexus/widgets/chat_page/lazy_loading/message_avatar.dart"; -import "package:nexus/widgets/chat_page/lazy_loading/message_displayname.dart"; -import "package:nexus/widgets/chat_page/wrappers/reaction_row.dart"; -import "package:timeago/timeago.dart"; - -class MessageWrapper extends StatelessWidget { - final Event event; - final Widget child; - final bool isGrouped; - final bool isFlashing; - const MessageWrapper( - this.event, - this.child, { - this.isGrouped = false, - this.isFlashing = false, - super.key, - }); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - - return ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(12)), - child: AnimatedContainer( - padding: isFlashing ? EdgeInsets.all(8) : EdgeInsets.all(0), - color: isFlashing - ? Theme.of(context).colorScheme.onSurface.withAlpha(50) - : Colors.transparent, - duration: Duration(milliseconds: 250), - child: Row( - spacing: 8, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - isGrouped ? SizedBox(width: 40) : MessageAvatar(event, height: 40), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 4, - children: [ - if (!isGrouped) - Row( - spacing: 4, - children: [ - Flexible( - child: MessageDisplayname( - event, - style: theme.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.bold, - ), - ), - ), - Tooltip( - message: event.timestamp.toString(), - child: Text( - format(event.timestamp), - style: theme.textTheme.labelSmall?.copyWith( - color: Colors.grey, - ), - ), - ), - ], - ), - child, - if (event.sendError != null && event.sendError != "not sent") - Text( - event.sendError!, - style: theme.textTheme.labelSmall?.copyWith( - color: theme.colorScheme.error, - ), - ), - ReactionRow(event), - ], - ), - ), - ], - ), - ), - ); - } -}