Compare commits

...

2 Commits

9 changed files with 233 additions and 123 deletions

View File

@ -250,7 +250,10 @@
"title": "About",
"licensed": "Licensed under GPL3",
"version": "Version ${version}",
"viewSourceCode": "View source code"
"viewSourceCode": "View source code",
"nMoreToGo": "${n} more to go...",
"debugMenuShown": "You are now a developer!",
"debugMenuAlreadyShown": "You are already a developer!"
},
"appearance": {
"title": "Appearance",

View File

@ -250,7 +250,10 @@
"title": "Über",
"licensed": "Lizensiert unter GPL3",
"version": "Version ${version}",
"viewSourceCode": "Quellcode anschauen"
"viewSourceCode": "Quellcode anschauen",
"nMoreToGo": "Noch ${n}...",
"debugMenuShown": "Du bist jetzt ein Entwickler!",
"debugMenuAlreadyShown": "Du bist bereits ein Entwickler!"
},
"appearance": {
"title": "Aussehen",

View File

@ -1,4 +1,5 @@
import 'package:moxxyv2/service/database/constants.dart';
import 'package:moxxyv2/service/database/helpers.dart';
import 'package:moxxyv2/shared/models/preference.dart';
import 'package:sqflite_sqlcipher/sqflite.dart';
@ -427,4 +428,12 @@ Future<void> createDatabase(Database db, int version) async {
'true',
).toDatabaseJson(),
);
await db.insert(
preferenceTable,
Preference(
'showDebugMenu',
typeBool,
boolToString(false),
).toDatabaseJson(),
);
}

View File

@ -32,6 +32,7 @@ import 'package:moxxyv2/service/database/migrations/0000_stickers_missing_attrib
import 'package:moxxyv2/service/database/migrations/0000_stickers_missing_attributes3.dart';
import 'package:moxxyv2/service/database/migrations/0000_stickers_privacy.dart';
import 'package:moxxyv2/service/database/migrations/0000_xmpp_state.dart';
import 'package:moxxyv2/service/database/migrations/0001_debug_menu.dart';
import 'package:moxxyv2/service/helpers.dart';
import 'package:moxxyv2/service/not_specified.dart';
import 'package:moxxyv2/service/omemo/omemo.dart';
@ -82,7 +83,7 @@ class DatabaseService {
_db = await openDatabase(
dbPath,
password: key,
version: 25,
version: 26,
onCreate: createDatabase,
onConfigure: (db) async {
// In order to do schema changes during database upgrades, we disable foreign
@ -191,6 +192,10 @@ class DatabaseService {
_log.finest('Running migration for database version 25');
await upgradeFromV24ToV25(db);
}
if (oldVersion < 26) {
_log.finest('Running migration for database version 26');
await upgradeFromV25ToV26(db);
}
},
);

View File

@ -0,0 +1,15 @@
import 'package:moxxyv2/service/database/constants.dart';
import 'package:moxxyv2/service/database/helpers.dart';
import 'package:moxxyv2/shared/models/preference.dart';
import 'package:sqflite_sqlcipher/sqflite.dart';
Future<void> upgradeFromV25ToV26(Database db) async {
await db.insert(
preferenceTable,
Preference(
'showDebugMenu',
typeBool,
boolToString(false),
).toDatabaseJson(),
);
}

View File

@ -32,6 +32,7 @@ class PreferencesState with _$PreferencesState {
@Default(true) bool enableStickers,
@Default(true) bool autoDownloadStickersFromContacts,
@Default(true) bool isStickersNodePublic,
@Default(false) bool showDebugMenu,
}) = _PreferencesState;
// JSON serialization

View File

@ -1,11 +1,15 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:moxxyv2/i18n/strings.g.dart';
import 'package:moxxyv2/shared/models/preferences.dart';
import 'package:moxxyv2/ui/bloc/preferences_bloc.dart';
import 'package:moxxyv2/ui/constants.dart';
import 'package:moxxyv2/ui/widgets/topbar.dart';
import 'package:url_launcher/url_launcher.dart';
// TODO(PapaTutuWawa): Include license text
class SettingsAboutPage extends StatelessWidget {
class SettingsAboutPage extends StatefulWidget {
const SettingsAboutPage({ super.key });
static MaterialPageRoute<dynamic> get route => MaterialPageRoute<dynamic>(
@ -14,7 +18,18 @@ class SettingsAboutPage extends StatelessWidget {
name: aboutRoute,
),
);
@override
SettingsAboutPageState createState() => SettingsAboutPageState();
}
class SettingsAboutPageState extends State<SettingsAboutPage> {
/// The amount of taps on the Moxxy logo, if showDebugMenu is false
int _counter = 0;
/// True, if the toast ("You're already a developer") has already been shown once.
bool _alreadyShownNotificationShown = false;
Future<void> _openUrl(String url) async {
if (!await launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication)) {
// TODO(Unknown): Show a popup to copy the url
@ -29,9 +44,45 @@ class SettingsAboutPage extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: paddingVeryLarge),
child: Column(
children: [
Image.asset(
'assets/images/logo.png',
width: 200, height: 200,
BlocBuilder<PreferencesBloc, PreferencesState>(
buildWhen: (prev, next) => prev.showDebugMenu != next.showDebugMenu,
builder: (context, state) => InkWell(
onTap: () async {
if (state.showDebugMenu) {
if (_counter == 0 && !_alreadyShownNotificationShown) {
_alreadyShownNotificationShown = true;
await Fluttertoast.showToast(
msg: t.pages.settings.about.debugMenuAlreadyShown,
gravity: ToastGravity.SNACKBAR,
toastLength: Toast.LENGTH_SHORT,
);
}
return;
}
_counter++;
if (_counter == 10) {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(
showDebugMenu: true,
),
),
);
await Fluttertoast.showToast(
msg: t.pages.settings.about.debugMenuShown,
gravity: ToastGravity.SNACKBAR,
toastLength: Toast.LENGTH_SHORT,
);
}
},
child:Image.asset(
'assets/images/logo.png',
width: 200, height: 200,
),
),
),
Text(
t.global.title,

View File

@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:moxxyv2/i18n/strings.g.dart';
@ -135,6 +136,23 @@ class DebuggingPage extends StatelessWidget {
);
},
),
// Hide the testing commands outside of debug mode
...kDebugMode ? [
const SectionTitle('Testing'),
SettingsRow(
title: 'Reset showDebugMenu state',
onTap: () {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(
showDebugMenu: false,
),
),
);
},
),
] : [],
],
),
),

View File

@ -1,7 +1,9 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:get_it/get_it.dart';
import 'package:moxxyv2/i18n/strings.g.dart';
import 'package:moxxyv2/shared/models/preferences.dart';
import 'package:moxxyv2/ui/bloc/blocklist_bloc.dart';
import 'package:moxxyv2/ui/bloc/preferences_bloc.dart';
import 'package:moxxyv2/ui/constants.dart';
@ -25,129 +27,132 @@ class SettingsPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: BorderlessTopbar.simple(t.pages.settings.settings.title),
body: ListView(
children: [
SectionTitle(t.pages.settings.settings.conversationsSection),
SettingsRow(
title: t.pages.settings.settings.conversationsSection,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.chat_bubble),
),
onTap: () {
Navigator.pushNamed(context, conversationSettingsRoute);
},
),
SettingsRow(
title: t.pages.settings.stickers.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(PhosphorIcons.stickerBold),
),
onTap: () {
Navigator.pushNamed(context, stickersRoute);
},
),
SettingsRow(
title: t.pages.settings.network.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.network_wifi),
),
onTap: () {
Navigator.pushNamed(context, networkRoute);
},
),
SettingsRow(
title: t.pages.settings.privacy.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.shield),
),
onTap: () {
Navigator.pushNamed(context, privacyRoute);
},
),
SectionTitle(t.pages.settings.settings.accountSection),
SettingsRow(
title: t.pages.blocklist.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.block),
),
onTap: () {
GetIt.I.get<BlocklistBloc>().add(
BlocklistRequestedEvent(),
);
},
),
SettingsRow(
title: t.pages.settings.settings.signOut,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.logout),
),
onTap: () async {
final result = await showConfirmationDialog(
t.pages.settings.settings.signOutConfirmTitle,
t.pages.settings.settings.signOutConfirmBody,
context,
);
if (result) {
GetIt.I.get<PreferencesBloc>().add(SignedOutEvent());
}
},
),
SectionTitle(t.pages.settings.settings.miscellaneousSection),
SettingsRow(
title: t.pages.settings.appearance.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.logout),
),
onTap: () {
Navigator.pushNamed(context, appearanceRoute);
},
),
SettingsRow(
title: t.pages.settings.about.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.info),
),
onTap: () {
Navigator.pushNamed(context, aboutRoute);
},
),
SettingsRow(
title: t.pages.settings.licenses.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.info),
),
onTap: () {
Navigator.pushNamed(context, licensesRoute);
},
),
if (kDebugMode)
SectionTitle(t.pages.settings.settings.debuggingSection),
if (kDebugMode)
body: BlocBuilder<PreferencesBloc, PreferencesState>(
buildWhen: (prev, next) => prev.showDebugMenu != next.showDebugMenu,
builder: (context, state) => ListView(
children: [
SectionTitle(t.pages.settings.settings.conversationsSection),
SettingsRow(
title: t.pages.settings.debugging.title,
title: t.pages.settings.settings.conversationsSection,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.chat_bubble),
),
onTap: () {
Navigator.pushNamed(context, conversationSettingsRoute);
},
),
SettingsRow(
title: t.pages.settings.stickers.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(PhosphorIcons.stickerBold),
),
onTap: () {
Navigator.pushNamed(context, stickersRoute);
},
),
SettingsRow(
title: t.pages.settings.network.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.network_wifi),
),
onTap: () {
Navigator.pushNamed(context, networkRoute);
},
),
SettingsRow(
title: t.pages.settings.privacy.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.shield),
),
onTap: () {
Navigator.pushNamed(context, privacyRoute);
},
),
SectionTitle(t.pages.settings.settings.accountSection),
SettingsRow(
title: t.pages.blocklist.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.block),
),
onTap: () {
GetIt.I.get<BlocklistBloc>().add(
BlocklistRequestedEvent(),
);
},
),
SettingsRow(
title: t.pages.settings.settings.signOut,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.logout),
),
onTap: () async {
final result = await showConfirmationDialog(
t.pages.settings.settings.signOutConfirmTitle,
t.pages.settings.settings.signOutConfirmBody,
context,
);
if (result) {
GetIt.I.get<PreferencesBloc>().add(SignedOutEvent());
}
},
),
SectionTitle(t.pages.settings.settings.miscellaneousSection),
SettingsRow(
title: t.pages.settings.appearance.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.logout),
),
onTap: () {
Navigator.pushNamed(context, appearanceRoute);
},
),
SettingsRow(
title: t.pages.settings.about.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.info),
),
onTap: () {
Navigator.pushNamed(context, debuggingRoute);
Navigator.pushNamed(context, aboutRoute);
},
),
],
SettingsRow(
title: t.pages.settings.licenses.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.info),
),
onTap: () {
Navigator.pushNamed(context, licensesRoute);
},
),
if (kDebugMode || state.showDebugMenu)
SectionTitle(t.pages.settings.settings.debuggingSection),
if (kDebugMode || state.showDebugMenu)
SettingsRow(
title: t.pages.settings.debugging.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.info),
),
onTap: () {
Navigator.pushNamed(context, debuggingRoute);
},
),
],
),
),
);
}