feat: Option to hide redacted and unknown events

This commit is contained in:
Sorunome 2020-11-07 12:00:41 +01:00
parent ee1b128d77
commit 36315a45ec
No known key found for this signature in database
GPG Key ID: B19471D07FC9BE9C
16 changed files with 118 additions and 38 deletions

View File

@ -2,6 +2,7 @@ import 'package:famedlysdk/famedlysdk.dart';
import 'package:flutter_matrix_html/flutter_html.dart'; import 'package:flutter_matrix_html/flutter_html.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils/url_launcher.dart'; import '../utils/url_launcher.dart';
import '../config/setting_keys.dart';
import 'matrix.dart'; import 'matrix.dart';
@ -59,10 +60,10 @@ class HtmlMessage extends StatelessWidget {
); );
}, },
setCodeLanguage: (String key, String value) async { setCodeLanguage: (String key, String value) async {
await matrix.store.setItem('code_language.$key', value); await matrix.store.setItem('${SettingKeys.codeLanguage}.$key', value);
}, },
getCodeLanguage: (String key) async { getCodeLanguage: (String key) async {
return await matrix.store.getItem('code_language.$key'); return await matrix.store.getItem('${SettingKeys.codeLanguage}.$key');
}, },
getPillInfo: (String identifier) async { getPillInfo: (String identifier) async {
if (room == null) { if (room == null) {

View File

@ -72,9 +72,6 @@ class Message extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (event.type == EventTypes.Unknown) {
return Container();
}
if (![EventTypes.Message, EventTypes.Sticker, EventTypes.Encrypted] if (![EventTypes.Message, EventTypes.Sticker, EventTypes.Encrypted]
.contains(event.type)) { .contains(event.type)) {
return StateMessage(event); return StateMessage(event);

View File

@ -23,6 +23,8 @@ import '../utils/beautify_string_extension.dart';
import '../utils/famedlysdk_store.dart'; import '../utils/famedlysdk_store.dart';
import '../views/key_verification.dart'; import '../views/key_verification.dart';
import '../utils/platform_infos.dart'; import '../utils/platform_infos.dart';
import '../config/app_config.dart';
import '../config/setting_keys.dart';
import 'avatar.dart'; import 'avatar.dart';
class Matrix extends StatefulWidget { class Matrix extends StatefulWidget {
@ -72,7 +74,6 @@ class MatrixState extends State<Matrix> {
String activeRoomId; String activeRoomId;
File wallpaper; File wallpaper;
bool renderHtml = false;
String jitsiInstance = 'https://meet.jit.si/'; String jitsiInstance = 'https://meet.jit.si/';
@ -304,18 +305,26 @@ class MatrixState extends State<Matrix> {
} }
if (store != null) { if (store != null) {
store store
.getItem('chat.fluffy.jitsi_instance') .getItem(SettingKeys.jitsiInstance)
.then((final instance) => jitsiInstance = instance ?? jitsiInstance); .then((final instance) => jitsiInstance = instance ?? jitsiInstance);
store.getItem('chat.fluffy.wallpaper').then((final path) async { store.getItem(SettingKeys.wallpaper).then((final path) async {
if (path == null) return; if (path == null) return;
final file = File(path); final file = File(path);
if (await file.exists()) { if (await file.exists()) {
wallpaper = file; wallpaper = file;
} }
}); });
store.getItem('chat.fluffy.renderHtml').then((final render) async { store
renderHtml = render == '1'; .getItemBool(SettingKeys.renderHtml, AppConfig.renderHtml)
}); .then((value) => AppConfig.renderHtml = value);
store
.getItemBool(
SettingKeys.hideRedactedEvents, AppConfig.hideRedactedEvents)
.then((value) => AppConfig.hideRedactedEvents = value);
store
.getItemBool(
SettingKeys.hideUnknownEvents, AppConfig.hideUnknownEvents)
.then((value) => AppConfig.hideUnknownEvents = value);
} }
if (kIsWeb) { if (kIsWeb) {
onFocusSub = html.window.onFocus.listen((_) => webHasFocus = true); onFocusSub = html.window.onFocus.listen((_) => webHasFocus = true);

View File

@ -9,6 +9,7 @@ import 'package:matrix_link_text/link_text.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../utils/url_launcher.dart'; import '../utils/url_launcher.dart';
import '../config/app_config.dart';
import 'html_message.dart'; import 'html_message.dart';
import 'matrix.dart'; import 'matrix.dart';
import 'message_download_content.dart'; import 'message_download_content.dart';
@ -43,7 +44,7 @@ class MessageContent extends StatelessWidget {
case MessageTypes.Text: case MessageTypes.Text:
case MessageTypes.Notice: case MessageTypes.Notice:
case MessageTypes.Emote: case MessageTypes.Emote:
if (Matrix.of(context).renderHtml && if (AppConfig.renderHtml &&
!event.redacted && !event.redacted &&
event.isRichMessage) { event.isRichMessage) {
String html = event.content['formatted_body']; String html = event.content['formatted_body'];

View File

@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'html_message.dart'; import 'html_message.dart';
import 'matrix.dart'; import '../config/app_config.dart';
class ReplyContent extends StatelessWidget { class ReplyContent extends StatelessWidget {
final Event replyEvent; final Event replyEvent;
@ -22,7 +22,7 @@ class ReplyContent extends StatelessWidget {
? replyEvent.getDisplayEvent(timeline) ? replyEvent.getDisplayEvent(timeline)
: replyEvent; : replyEvent;
if (displayEvent != null && if (displayEvent != null &&
Matrix.of(context).renderHtml && AppConfig.renderHtml &&
[EventTypes.Message, EventTypes.Encrypted] [EventTypes.Message, EventTypes.Encrypted]
.contains(displayEvent.type) && .contains(displayEvent.type) &&
[MessageTypes.Text, MessageTypes.Notice, MessageTypes.Emote] [MessageTypes.Text, MessageTypes.Notice, MessageTypes.Emote]

View File

@ -1,6 +1,7 @@
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'matrix.dart'; import 'matrix.dart';
import '../config/setting_keys.dart';
enum Themes { enum Themes {
light, light,
@ -175,14 +176,12 @@ class ThemeSwitcherWidgetState extends State<ThemeSwitcherWidget> {
BuildContext context; BuildContext context;
Future loadSelection(MatrixState matrix) async { Future loadSelection(MatrixState matrix) async {
var item = await matrix.store.getItem('theme') ?? 'system'; var item = await matrix.store.getItem(SettingKeys.theme) ?? 'system';
selectedTheme = Themes.values.firstWhere( selectedTheme = Themes.values.firstWhere(
(e) => e.toString() == 'Themes.' + item, (e) => e.toString() == 'Themes.' + item,
orElse: () => Themes.system); orElse: () => Themes.system);
amoledEnabled = (await matrix.store.getItem('amoled_enabled') ?? 'false') amoledEnabled = await matrix.store.getItemBool(SettingKeys.amoledEnabled);
.toLowerCase() ==
'true';
switchTheme(matrix, selectedTheme, amoledEnabled); switchTheme(matrix, selectedTheme, amoledEnabled);
return; return;
@ -229,11 +228,12 @@ class ThemeSwitcherWidgetState extends State<ThemeSwitcherWidget> {
} }
Future saveThemeValue(MatrixState matrix, Themes value) async { Future saveThemeValue(MatrixState matrix, Themes value) async {
await matrix.store.setItem('theme', value.toString().split('.').last); await matrix.store
.setItem(SettingKeys.theme, value.toString().split('.').last);
} }
Future saveAmoledEnabledValue(MatrixState matrix, bool value) async { Future saveAmoledEnabledValue(MatrixState matrix, bool value) async {
await matrix.store.setItem('amoled_enabled', value.toString()); await matrix.store.setItem(SettingKeys.amoledEnabled, value.toString());
} }
void setup() async { void setup() async {

View File

@ -8,4 +8,7 @@ abstract class AppConfig {
'https://gitlab.com/ChristianPauly/fluffychat-flutter/issues'; 'https://gitlab.com/ChristianPauly/fluffychat-flutter/issues';
static const String sentryDsn = static const String sentryDsn =
'https://8591d0d863b646feb4f3dda7e5dcab38@o256755.ingest.sentry.io/5243143'; 'https://8591d0d863b646feb4f3dda7e5dcab38@o256755.ingest.sentry.io/5243143';
static bool renderHtml = false;
static bool hideRedactedEvents = false;
static bool hideUnknownEvents = false;
} }

View File

@ -0,0 +1,13 @@
abstract class SettingKeys {
static const String jitsiInstance = 'chat.fluffy.jitsi_instance';
static const String wallpaper = 'chat.fluffy.wallpaper';
static const String renderHtml = 'chat.fluffy.renderHtml';
static const String hideRedactedEvents = 'chat.fluffy.hideRedactedEvents';
static const String hideUnknownEvents = 'chat.fluffy.hideUnknownEvents';
static const String sentry = 'sentry';
static const String theme = 'theme';
static const String amoledEnabled = 'amoled_enabled';
static const String codeLanguage = 'code_language';
static const String showNoGoogle = 'chat.fluffy.show_no_google';
static const String databasePassword = 'database-password';
}

View File

@ -681,6 +681,16 @@
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"hideRedactedEvents": "Hide redacted events",
"@hideRedactedEvents": {
"type": "text",
"placeholders": {}
},
"hideUnknownEvents": "Hide unknown events",
"@hideUnknownEvents": {
"type": "text",
"placeholders": {}
},
"homeserverIsNotCompatible": "Homeserver is not compatible", "homeserverIsNotCompatible": "Homeserver is not compatible",
"@homeserverIsNotCompatible": { "@homeserverIsNotCompatible": {
"type": "text", "type": "text",

View File

@ -6,6 +6,7 @@ import 'package:path_provider/path_provider.dart';
import 'dart:async'; import 'dart:async';
import 'dart:core'; import 'dart:core';
import './database/shared.dart'; import './database/shared.dart';
import '../config/setting_keys.dart';
import 'package:random_string/random_string.dart'; import 'package:random_string/random_string.dart';
Future<Database> getDatabase(Client client) async { Future<Database> getDatabase(Client client) async {
@ -16,7 +17,7 @@ Future<Database> getDatabase(Client client) async {
try { try {
if (_db != null) return _db; if (_db != null) return _db;
final store = Store(); final store = Store();
var password = await store.getItem('database-password'); var password = await store.getItem(SettingKeys.databasePassword);
var newPassword = false; var newPassword = false;
if (password == null || password.isEmpty) { if (password == null || password.isEmpty) {
newPassword = true; newPassword = true;
@ -28,7 +29,7 @@ Future<Database> getDatabase(Client client) async {
password: password, password: password,
); );
if (newPassword) { if (newPassword) {
await store.setItem('database-password', password); await store.setItem(SettingKeys.databasePassword, password);
} }
return _db; return _db;
} finally { } finally {
@ -74,6 +75,15 @@ class Store {
} }
} }
Future<bool> getItemBool(String key, [bool defaultValue]) async {
final value = await getItem(key);
if (value == null) {
return defaultValue ?? false;
}
// we also check for '1' for legacy reasons, some booleans were stored that way
return value == '1' || value.toLowerCase() == 'true';
}
Future<void> setItem(String key, String value) async { Future<void> setItem(String key, String value) async {
if (!PlatformInfos.isMobile) { if (!PlatformInfos.isMobile) {
await _setupLocalStorage(); await _setupLocalStorage();

View File

@ -15,6 +15,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import '../components/matrix.dart'; import '../components/matrix.dart';
import '../config/setting_keys.dart';
import 'famedlysdk_store.dart'; import 'famedlysdk_store.dart';
import 'matrix_locals.dart'; import 'matrix_locals.dart';
@ -42,8 +43,7 @@ abstract class FirebaseController {
token = null; token = null;
} }
if (token?.isEmpty ?? true) { if (token?.isEmpty ?? true) {
final storeItem = final storeItem = await matrix.store.getItem(SettingKeys.showNoGoogle);
await matrix.store.getItem('chat.fluffy.show_no_google');
final configOptionMissing = storeItem == null || storeItem.isEmpty; final configOptionMissing = storeItem == null || storeItem.isEmpty;
if (configOptionMissing || (!configOptionMissing && storeItem == '1')) { if (configOptionMissing || (!configOptionMissing && storeItem == '1')) {
BotToast.showText( BotToast.showText(
@ -51,7 +51,7 @@ abstract class FirebaseController {
duration: Duration(seconds: 15), duration: Duration(seconds: 15),
); );
if (configOptionMissing) { if (configOptionMissing) {
await matrix.store.setItem('chat.fluffy.show_no_google', '0'); await matrix.store.setItem(SettingKeys.showNoGoogle, '0');
} }
} }
return; return;

View File

@ -7,6 +7,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:sentry/sentry.dart'; import 'package:sentry/sentry.dart';
import 'famedlysdk_store.dart'; import 'famedlysdk_store.dart';
import '../config/setting_keys.dart';
abstract class SentryController { abstract class SentryController {
static Future<void> toggleSentryAction(BuildContext context) async { static Future<void> toggleSentryAction(BuildContext context) async {
@ -17,14 +18,14 @@ abstract class SentryController {
cancelText: L10n.of(context).no, cancelText: L10n.of(context).no,
); );
final storage = Store(); final storage = Store();
await storage.setItem('sentry', enableSentry.toString()); await storage.setItem(SettingKeys.sentry, enableSentry.toString());
BotToast.showText(text: L10n.of(context).changesHaveBeenSaved); BotToast.showText(text: L10n.of(context).changesHaveBeenSaved);
return; return;
} }
static Future<bool> getSentryStatus() async { static Future<bool> getSentryStatus() async {
final storage = Store(); final storage = Store();
return await storage.getItem('sentry') == 'true'; return await storage.getItemBool(SettingKeys.sentry);
} }
static final sentry = SentryClient(dsn: AppConfig.sentryDsn); static final sentry = SentryClient(dsn: AppConfig.sentryDsn);

View File

@ -34,6 +34,7 @@ import 'package:swipe_to_action/swipe_to_action.dart';
import '../components/dialogs/send_file_dialog.dart'; import '../components/dialogs/send_file_dialog.dart';
import '../components/input_bar.dart'; import '../components/input_bar.dart';
import '../utils/matrix_file_extension.dart'; import '../utils/matrix_file_extension.dart';
import '../config/app_config.dart';
import 'chat_details.dart'; import 'chat_details.dart';
import 'chat_list.dart'; import 'chat_list.dart';
@ -411,9 +412,15 @@ class _ChatState extends State<_Chat> {
List<Event> getFilteredEvents() => timeline.events List<Event> getFilteredEvents() => timeline.events
.where((e) => .where((e) =>
![RelationshipTypes.Edit, RelationshipTypes.Reaction] // always filter out edit and reaction relationships
!{RelationshipTypes.Edit, RelationshipTypes.Reaction}
.contains(e.relationshipType) && .contains(e.relationshipType) &&
e.type != 'm.reaction') // if a reaction has been redacted we also want it to appear in the timeline
e.type != EventTypes.Reaction &&
// if we enabled to hide all redacted events, don't show those
(!AppConfig.hideRedactedEvents || !e.redacted) &&
// if we enabled to hide all unknown events, don't show those
(!AppConfig.hideUnknownEvents || e.isEventTypeKnown))
.toList(); .toList();
@override @override

View File

@ -21,6 +21,8 @@ import '../components/content_banner.dart';
import '../components/dialogs/simple_dialogs.dart'; import '../components/dialogs/simple_dialogs.dart';
import '../components/matrix.dart'; import '../components/matrix.dart';
import '../utils/app_route.dart'; import '../utils/app_route.dart';
import '../config/app_config.dart';
import '../config/setting_keys.dart';
import 'app_info.dart'; import 'app_info.dart';
import 'chat_list.dart'; import 'chat_list.dart';
import 'settings_emotes.dart'; import 'settings_emotes.dart';
@ -115,7 +117,7 @@ class _SettingsState extends State<Settings> {
jitsi += '/'; jitsi += '/';
} }
final matrix = Matrix.of(context); final matrix = Matrix.of(context);
await matrix.store.setItem('chat.fluffy.jitsi_instance', jitsi); await matrix.store.setItem(SettingKeys.jitsiInstance, jitsi);
matrix.jitsiInstance = jitsi; matrix.jitsiInstance = jitsi;
} }
@ -179,13 +181,13 @@ class _SettingsState extends State<Settings> {
Matrix.of(context).wallpaper = File(wallpaper.path); Matrix.of(context).wallpaper = File(wallpaper.path);
await Matrix.of(context) await Matrix.of(context)
.store .store
.setItem('chat.fluffy.wallpaper', wallpaper.path); .setItem(SettingKeys.wallpaper, wallpaper.path);
setState(() => null); setState(() => null);
} }
void deleteWallpaperAction(BuildContext context) async { void deleteWallpaperAction(BuildContext context) async {
Matrix.of(context).wallpaper = null; Matrix.of(context).wallpaper = null;
await Matrix.of(context).store.deleteItem('chat.fluffy.wallpaper'); await Matrix.of(context).store.deleteItem(SettingKeys.wallpaper);
setState(() => null); setState(() => null);
} }
@ -340,13 +342,39 @@ class _SettingsState extends State<Settings> {
ListTile( ListTile(
title: Text(L10n.of(context).renderRichContent), title: Text(L10n.of(context).renderRichContent),
trailing: Switch( trailing: Switch(
value: Matrix.of(context).renderHtml, value: AppConfig.renderHtml,
activeColor: Theme.of(context).primaryColor, activeColor: Theme.of(context).primaryColor,
onChanged: (bool newValue) async { onChanged: (bool newValue) async {
Matrix.of(context).renderHtml = newValue; AppConfig.renderHtml = newValue;
await Matrix.of(context) await Matrix.of(context)
.store .store
.setItem('chat.fluffy.renderHtml', newValue ? '1' : '0'); .setItem(SettingKeys.renderHtml, newValue.toString());
setState(() => null);
},
),
),
ListTile(
title: Text(L10n.of(context).hideRedactedEvents),
trailing: Switch(
value: AppConfig.hideRedactedEvents,
activeColor: Theme.of(context).primaryColor,
onChanged: (bool newValue) async {
AppConfig.hideRedactedEvents = newValue;
await Matrix.of(context).store.setItem(
SettingKeys.hideRedactedEvents, newValue.toString());
setState(() => null);
},
),
),
ListTile(
title: Text(L10n.of(context).hideUnknownEvents),
trailing: Switch(
value: AppConfig.hideUnknownEvents,
activeColor: Theme.of(context).primaryColor,
onChanged: (bool newValue) async {
AppConfig.hideUnknownEvents = newValue;
await Matrix.of(context).store.setItem(
SettingKeys.hideUnknownEvents, newValue.toString());
setState(() => null); setState(() => null);
}, },
), ),

View File

@ -208,8 +208,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: "15d817023d34f813e95eba6ca8c71c575b8c2457" ref: "66572bd03209c1c6488cde53a0c72c11faef341d"
resolved-ref: "15d817023d34f813e95eba6ca8c71c575b8c2457" resolved-ref: "66572bd03209c1c6488cde53a0c72c11faef341d"
url: "https://gitlab.com/famedly/famedlysdk.git" url: "https://gitlab.com/famedly/famedlysdk.git"
source: git source: git
version: "0.0.1" version: "0.0.1"

View File

@ -23,7 +23,7 @@ dependencies:
famedlysdk: famedlysdk:
git: git:
url: https://gitlab.com/famedly/famedlysdk.git url: https://gitlab.com/famedly/famedlysdk.git
ref: 15d817023d34f813e95eba6ca8c71c575b8c2457 ref: 66572bd03209c1c6488cde53a0c72c11faef341d
localstorage: ^3.0.3+6 localstorage: ^3.0.3+6
file_picker_cross: 4.2.2 file_picker_cross: 4.2.2