diff --git a/lib/constants.dart b/lib/constants.dart index 425b110c..c5b44410 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -6,6 +6,7 @@ const optionHomeInitialTab = 'home.initial_tab'; const optionMediaSize = 'media.size'; +const optionRegexFilter = 'regex_filter'; const optionSubscriptionGroupsOrderByAscending = 'subscription_groups.order_by.ascending'; const optionSubscriptionGroupsOrderByField = 'subscription_groups.order_by.field'; const optionSubscriptionOrderByAscending = 'subscription.order_by.ascending'; diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index 1b5e2077..a65af15f 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -234,6 +234,7 @@ class MessageLookup extends MessageLookupByLibrary { "prefix": MessageLookupByLibrary.simpleMessage("prefix"), "private_profile": MessageLookupByLibrary.simpleMessage("Private profile"), + "regex_filter": MessageLookupByLibrary.simpleMessage("Filter tweets via regex"), "released_under_the_mit_license": MessageLookupByLibrary.simpleMessage( "Released under the MIT License"), "replying_to": MessageLookupByLibrary.simpleMessage("Replying to"), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 8afe020e..8e27fc73 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -1741,6 +1741,16 @@ class L10n { args: [], ); } + + /// `This tweet was filtered` + String get this_tweet_was_filtered { + return Intl.message( + 'This tweet was filtered', + name: 'this_tweet_was_filtered', + desc: '', + args: [], + ); + } /// `This tweet is unavailable` String get this_tweet_is_unavailable { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 3b803cb7..02870456 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -193,5 +193,6 @@ "fritter_blue": "Fritter blue", "blue_theme_based_on_the_twitter_color_scheme": "Blue theme based on the Twitter color scheme", "something_broke_in_fritter": "Something broke in Fritter.", - "unable_to_run_the_database_migrations": "Unable to run the database migrations" -} \ No newline at end of file + "unable_to_run_the_database_migrations": "Unable to run the database migrations", + "regex_filter": "Filter tweets via regex" +} diff --git a/lib/settings/settings.dart b/lib/settings/settings.dart index 8e1857f7..efc51ff5 100644 --- a/lib/settings/settings.dart +++ b/lib/settings/settings.dart @@ -408,6 +408,12 @@ class _SettingsScreenState extends State { subtitle: Text(L10n.of(context).export_your_data), onTap: () => Navigator.pushNamed(context, routeSettingsExport), ), + PrefTitle(title: Text(L10n.of(context).filters)), + PrefText( + label: L10n.of(context).regex_filter, + pref: optionRegexFilter, + // TODO: Add regex validator + hintText: "ExampleFilter1|ExampleFilter[2-3]"), PrefTitle(title: Text(L10n.of(context).logging)), PrefCheckbox( title: Text(L10n.of(context).enable_sentry), diff --git a/lib/tweet/tweet.dart b/lib/tweet/tweet.dart index e40b8abe..a9c267db 100644 --- a/lib/tweet/tweet.dart +++ b/lib/tweet/tweet.dart @@ -25,6 +25,7 @@ import 'package:provider/provider.dart'; import 'package:share/share.dart'; import 'package:timeago/timeago.dart' as timeago; import 'package:url_launcher/url_launcher.dart'; +import 'package:pref/pref.dart'; class TweetTile extends StatefulWidget { final bool clickable; @@ -276,6 +277,25 @@ class TweetTileState extends State with SingleTickerProviderStateMixi var numberFormat = NumberFormat.compact(); TweetWithCard tweet = this.tweet.retweetedStatusWithCard == null ? this.tweet : this.tweet.retweetedStatusWithCard!; + + // Get filter string from options: + var prefService = PrefService.of(context); + var regexFilterString = prefService.get(optionRegexFilter); + + if (regexFilterString != null && regexFilterString.isNotEmpty){ + RegExp regexFilter = RegExp('$regexFilterString', caseSensitive: false, multiLine: true); + + // Check if regex filter matches tweet text: + if (regexFilter.hasMatch( tweet.fullText.toString() )) { + + // TODO: completely remove the card instead of showing "This tweet was filtered" + tweet.text = L10n.of(context).this_tweet_was_filtered; + tweet.fullText = L10n.of(context).this_tweet_was_filtered; + tweet.idStr = ''; + tweet.isTombstone = true; + return const SizedBox.shrink(); // Empty widget + } + } if (tweet.isTombstone ?? false) { return SizedBox(