From aee96db977bac36c660348731aaa4bf86b6b48bc Mon Sep 17 00:00:00 2001 From: Craftplacer <22963120+Craftplacer@users.noreply.github.com> Date: Sun, 29 Oct 2023 21:21:18 +0100 Subject: [PATCH] fix: state not updated on post change --- .../lib/ui/shared/posts/post_content.dart | 69 ++++++++------- src/kaiteki/test/post_state_test.dart | 83 +++++++++++++++++++ 2 files changed, 124 insertions(+), 28 deletions(-) create mode 100644 src/kaiteki/test/post_state_test.dart diff --git a/src/kaiteki/lib/ui/shared/posts/post_content.dart b/src/kaiteki/lib/ui/shared/posts/post_content.dart index 71c435e8..6591cf89 100644 --- a/src/kaiteki/lib/ui/shared/posts/post_content.dart +++ b/src/kaiteki/lib/ui/shared/posts/post_content.dart @@ -103,44 +103,57 @@ class _PostContentWidgetState extends ConsumerState { ); } + @override + void didUpdateWidget(covariant PostContent oldWidget) { + super.didUpdateWidget(oldWidget); + + if (oldWidget.post != widget.post) { + _renderContent(); + _updateCollapsed(); + } + } + @override void didChangeDependencies() { super.didChangeDependencies(); - _renderContent(); + _updateCollapsed(); + } + void _updateCollapsed() { final cwBehavior = ref.watch(preferences.cwBehavior).value; final subject = widget.post.subject; final hasSubject = subject != null && subject.isNotEmpty; - if (hasSubject) { - switch (cwBehavior) { - case ContentWarningBehavior.collapse: - collapsed = true; - break; - case ContentWarningBehavior.expanded: - collapsed = false; - break; - case ContentWarningBehavior.automatic: - final plainText = renderedContent?.toPlainText( - includeSemanticsLabels: false, - includePlaceholders: false, - ); - - final strings = [if (plainText != null) plainText, subject]; - - if (strings.isEmpty) break; - - final words = strings - .map((e) => e.toLowerCase()) - .map((e) => e.split(RegExp(r"([\s:])+"))) - .flattened; - - collapsed = sensitiveWords.any(words.contains); - break; - } - } + collapsed = switch (cwBehavior) { + ContentWarningBehavior.collapse => hasSubject, + ContentWarningBehavior.expanded => false, + ContentWarningBehavior.automatic => _containsSensitiveWords(), + }; + } + + bool _containsSensitiveWords() { + final plainText = renderedContent?.toPlainText( + includeSemanticsLabels: false, + includePlaceholders: false, + ); + + final subject = widget.post.subject; + + final strings = [ + if (plainText != null) plainText, + if (subject != null) subject, + ]; + + if (strings.isEmpty) return false; + + final words = strings + .map((e) => e.toLowerCase()) + .map((e) => e.split(RegExp(r"([\s:])+"))) + .flattened; + + return sensitiveWords.any(words.contains); } void _renderContent() { diff --git a/src/kaiteki/test/post_state_test.dart b/src/kaiteki/test/post_state_test.dart new file mode 100644 index 00000000..2cc15563 --- /dev/null +++ b/src/kaiteki/test/post_state_test.dart @@ -0,0 +1,83 @@ +import "package:flutter/material.dart"; +import "package:flutter_test/flutter_test.dart"; +import "package:kaiteki/model/auth/account.dart"; +import "package:kaiteki/model/auth/account_key.dart"; +import "package:kaiteki/ui/shared/posts/post_widget.dart"; +import "package:kaiteki_core/kaiteki_core.dart"; + +import "utils/bootstrap.dart"; +import "utils/dummy_adapter.dart"; + +void main() { + testWidgets("Test post widget state updating", (tester) async { + final boot = await Bootstrapper.getInstance( + initialAccounts: [ + Account( + key: const AccountKey(ApiType.mastodon, "example.com", "alice"), + adapter: DummyAdapter(), + accountSecret: null, + clientSecret: null, + user: exampleUser, + ), + ], + ); + + final post = Post( + id: "1", + content: "Hello world", + postedAt: DateTime.now(), + author: exampleUser, + ); + + final key = GlobalKey<_TestPostWidgetState>(); + + await tester.pumpWidget( + boot.wrap(_TestPostWidget(post: post, key: key)), + ); + + final contentFinder = find.text(post.content!); + + expect(contentFinder, findsOneWidget); + + final newPost = post.copyWith(content: "Hello world 2"); + + key.currentState!.post = newPost; + + await tester.pumpAndSettle(); + + expect(contentFinder, findsNothing); + }); +} + +class _TestPostWidget extends StatefulWidget { + final Post post; + + const _TestPostWidget({required this.post, super.key}); + + @override + State<_TestPostWidget> createState() => _TestPostWidgetState(); +} + +class _TestPostWidgetState extends State<_TestPostWidget> { + late Post _post; + + set post(Post post) => setState(() => _post = post); + + @override + void initState() { + super.initState(); + _post = widget.post; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 600), + child: PostWidget(_post), + ), + ), + ); + } +}