Compare commits

...

2 Commits

10 changed files with 501 additions and 457 deletions

View File

@ -5,8 +5,9 @@ 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/helpers.dart';
import 'package:moxxyv2/ui/widgets/settings/row.dart';
import 'package:moxxyv2/ui/widgets/settings/title.dart';
import 'package:moxxyv2/ui/widgets/topbar.dart';
import 'package:settings_ui/settings_ui.dart';
Widget _buildLanguageOption(BuildContext context, String localeCode, PreferencesState state) {
final selected = state.languageLocaleCode == localeCode;
@ -45,57 +46,51 @@ class AppearanceSettingsPage extends StatelessWidget {
return Scaffold(
appBar: BorderlessTopbar.simple(t.pages.settings.appearance.title),
body: BlocBuilder<PreferencesBloc, PreferencesState>(
builder: (context, state) => SettingsList(
sections: [
SettingsSection(
title: Text(t.pages.settings.appearance.languageSection),
tiles: [
SettingsTile(
title: Text(t.pages.settings.appearance.language),
description: Text(
t.pages.settings.appearance.languageSubtext(
selectedLanguage: localeCodeToLanguageName(state.languageLocaleCode),
),
),
onPressed: (context) async {
final result = await showDialog<String>(
context: context,
builder: (context) {
return SimpleDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(textfieldRadiusRegular),
),
title: Text(t.pages.settings.appearance.language),
children: [
_buildLanguageOption(context, 'default', state),
_buildLanguageOption(context, 'de', state),
_buildLanguageOption(context, 'en', state),
],
);
},
);
if (result == null) {
// Do nothing as the dialog was dismissed
return;
}
// Change preferences and set the app's locale
// ignore: use_build_context_synchronously
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(languageLocaleCode: result),
builder: (context, state) => ListView(
children: [
SectionTitle(t.pages.settings.appearance.languageSection),
SettingsRow(
title: t.pages.settings.appearance.language,
description: t.pages.settings.appearance.languageSubtext(
selectedLanguage: localeCodeToLanguageName(state.languageLocaleCode),
),
onTap: () async {
final result = await showDialog<String>(
context: context,
builder: (context) {
return SimpleDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(textfieldRadiusRegular),
),
title: Text(t.pages.settings.appearance.language),
children: [
_buildLanguageOption(context, 'default', state),
_buildLanguageOption(context, 'de', state),
_buildLanguageOption(context, 'en', state),
],
);
if (result == 'default') {
LocaleSettings.useDeviceLocale();
} else {
LocaleSettings.setLocaleRaw(result);
}
},
),
],
);
if (result == null) {
// Do nothing as the dialog was dismissed
return;
}
// Change preferences and set the app's locale
// ignore: use_build_context_synchronously
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(languageLocaleCode: result),
),
);
if (result == 'default') {
LocaleSettings.useDeviceLocale();
} else {
LocaleSettings.setLocaleRaw(result);
}
},
),
],
),

View File

@ -9,11 +9,12 @@ import 'package:moxxyv2/ui/bloc/cropbackground_bloc.dart';
import 'package:moxxyv2/ui/bloc/preferences_bloc.dart';
import 'package:moxxyv2/ui/constants.dart';
import 'package:moxxyv2/ui/helpers.dart';
import 'package:moxxyv2/ui/widgets/settings/row.dart';
import 'package:moxxyv2/ui/widgets/settings/title.dart';
import 'package:moxxyv2/ui/widgets/topbar.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:settings_ui/settings_ui.dart';
class ConversationSettingsPage extends StatelessWidget {
const ConversationSettingsPage({ super.key });
@ -67,89 +68,94 @@ class ConversationSettingsPage extends StatelessWidget {
return Scaffold(
appBar: BorderlessTopbar.simple(t.pages.settings.conversation.title),
body: BlocBuilder<PreferencesBloc, PreferencesState>(
builder: (context, state) => SettingsList(
sections: [
SettingsSection(
title: Text(t.pages.settings.conversation.appearance),
tiles: [
SettingsTile(
title: Text(t.pages.settings.conversation.selectBackgroundImage),
description: Text(t.pages.settings.conversation.selectBackgroundImageDescription),
onPressed: (context) async {
final backgroundPath = await _pickBackgroundImage();
builder: (context, state) => ListView(
children: [
SectionTitle(t.pages.settings.conversation.appearance),
if (backgroundPath != null) {
// ignore: use_build_context_synchronously
context.read<CropBackgroundBloc>().add(
CropBackgroundRequestedEvent(backgroundPath),
);
}
},
),
SettingsTile(
title: Text(t.pages.settings.conversation.removeBackgroundImage),
onPressed: (context) async {
final result = await showConfirmationDialog(
t.pages.settings.conversation.removeBackgroundImageConfirmTitle,
t.pages.settings.conversation.removeBackgroundImageConfirmBody,
context,
);
SettingsRow(
title: t.pages.settings.conversation.selectBackgroundImage,
description: t.pages.settings.conversation.selectBackgroundImageDescription,
onTap: () async {
final backgroundPath = await _pickBackgroundImage();
if (result) {
// ignore: use_build_context_synchronously
await _removeBackgroundImage(context, state);
}
},
),
],
if (backgroundPath != null) {
// ignore: use_build_context_synchronously
context.read<CropBackgroundBloc>().add(
CropBackgroundRequestedEvent(backgroundPath),
);
}
},
),
SettingsSection(
title: Text(t.pages.settings.conversation.behaviourSection),
tiles: [
SettingsTile.switchTile(
title: Text(t.pages.settings.conversation.contactsIntegration),
description: Text(t.pages.settings.conversation.contactsIntegrationBody),
initialValue: state.enableContactIntegration,
onToggle: (value) async {
// Ensure that we have the permission before changing the value
if (value && await Permission.contacts.status == PermissionStatus.denied) {
if (!(await Permission.contacts.request().isGranted)) {
return;
}
}
// ignore: use_build_context_synchronously
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(enableContactIntegration: value),
),
);
},
),
],
SettingsRow(
title: t.pages.settings.conversation.removeBackgroundImage,
onTap: () async {
final result = await showConfirmationDialog(
t.pages.settings.conversation.removeBackgroundImageConfirmTitle,
t.pages.settings.conversation.removeBackgroundImageConfirmBody,
context,
);
if (result) {
// ignore: use_build_context_synchronously
await _removeBackgroundImage(context, state);
}
},
),
SettingsSection(
title: Text(t.pages.settings.conversation.newChatsSection),
tiles: [
SettingsTile.switchTile(
title: Text(t.pages.settings.conversation.newChatsMuteByDefault),
initialValue: state.defaultMuteState,
onToggle: (value) => context.read<PreferencesBloc>().add(
SectionTitle(t.pages.settings.conversation.behaviourSection),
SettingsRow(
title: t.pages.settings.conversation.contactsIntegration,
description: t.pages.settings.conversation.contactsIntegrationBody,
suffix: Switch(
value: state.enableContactIntegration,
onChanged: (value) async {
// Ensure that we have the permission before changing the value
if (value && await Permission.contacts.status == PermissionStatus.denied) {
if (!(await Permission.contacts.request().isGranted)) {
return;
}
}
// ignore: use_build_context_synchronously
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(enableContactIntegration: value),
),
);
},
),
),
SectionTitle(t.pages.settings.conversation.newChatsSection),
SettingsRow(
title: t.pages.settings.conversation.newChatsMuteByDefault,
suffix: Switch(
value: state.defaultMuteState,
onChanged: (value) {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(defaultMuteState: value),
),
),
),
SettingsTile.switchTile(
title: Text(t.pages.settings.conversation.newChatsE2EE),
initialValue: state.enableOmemoByDefault,
onToggle: (value) => context.read<PreferencesBloc>().add(
);
},
),
),
SettingsRow(
title: t.pages.settings.conversation.newChatsE2EE,
suffix: Switch(
value: state.enableOmemoByDefault,
onChanged: (value) {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(enableOmemoByDefault: value),
),
),
),
],
);
},
),
),
],
),

View File

@ -4,8 +4,9 @@ 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/settings/row.dart';
import 'package:moxxyv2/ui/widgets/settings/title.dart';
import 'package:moxxyv2/ui/widgets/topbar.dart';
import 'package:settings_ui/settings_ui.dart';
class DebuggingPage extends StatelessWidget {
DebuggingPage({ super.key })
@ -28,111 +29,112 @@ class DebuggingPage extends StatelessWidget {
return Scaffold(
appBar: BorderlessTopbar.simple(t.pages.settings.debugging.title),
body: BlocBuilder<PreferencesBloc, PreferencesState>(
builder: (context, state) => SettingsList(
sections: [
SettingsSection(
title: Text(t.pages.settings.debugging.generalSection),
tiles: [
SettingsTile.switchTile(
title: Text(t.pages.settings.debugging.generalEnableDebugging),
onToggle: (value) => context.read<PreferencesBloc>().add(
builder: (context, state) => ListView(
children: [
SectionTitle(t.pages.settings.debugging.generalSection),
SettingsRow(
title: t.pages.settings.debugging.generalEnableDebugging,
suffix: Switch(
value: state.debugEnabled,
onChanged: (value) {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(debugEnabled: value),
),
);
},
),
),
SettingsRow(
title: t.pages.settings.debugging.generalEncryptionPassword,
description: t.pages.settings.debugging.generalEncryptionPasswordSubtext,
onTap: () {
showDialog<void>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(t.pages.settings.debugging.generalEncryptionPassword),
content: TextField(
minLines: 1,
obscureText: true,
controller: _passphraseController,
),
actions: [
TextButton(
child: Text(t.global.dialogAccept),
onPressed: () {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(debugPassphrase: _passphraseController.text),
),
);
Navigator.of(context).pop();
},
)
],
),
initialValue: state.debugEnabled,
),
SettingsTile(
title: Text(t.pages.settings.debugging.generalEncryptionPassword),
description: Text(t.pages.settings.debugging.generalEncryptionPasswordSubtext),
onPressed: (context) {
showDialog<void>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(t.pages.settings.debugging.generalEncryptionPassword),
content: TextField(
minLines: 1,
obscureText: true,
controller: _passphraseController,
),
actions: [
TextButton(
child: Text(t.global.dialogAccept),
onPressed: () {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(debugPassphrase: _passphraseController.text),
),
);
Navigator.of(context).pop();
},
)
],
),
);
},
),
SettingsTile(
title: Text(t.pages.settings.debugging.generalLoggingIp),
description: Text(t.pages.settings.debugging.generalLoggingIpSubtext),
onPressed: (context) {
showDialog<void>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(t.pages.settings.debugging.generalLoggingIp),
content: TextField(
minLines: 1,
controller: _ipController,
),
actions: [
TextButton(
child: Text(t.global.dialogAccept),
onPressed: () {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(debugIp: _ipController.text),
),
);
Navigator.of(context).pop();
},
)
],
),
);
},
),
SettingsTile(
title: Text(t.pages.settings.debugging.generalLoggingPort),
description: Text(t.pages.settings.debugging.generalLoggingPortSubtext),
onPressed: (context) {
showDialog<void>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(t.pages.settings.debugging.generalLoggingPort),
content: TextField(
minLines: 1,
controller: _portController,
keyboardType: TextInputType.number,
),
actions: [
TextButton(
child: Text(t.global.dialogAccept),
onPressed: () {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(debugPort: int.parse(_portController.text)),
),
);
Navigator.of(context).pop();
},
)
],
),
);
},
)
],
)
);
},
),
SettingsRow(
title: t.pages.settings.debugging.generalLoggingIp,
description: t.pages.settings.debugging.generalLoggingIpSubtext,
onTap: () {
showDialog<void>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(t.pages.settings.debugging.generalLoggingIp),
content: TextField(
minLines: 1,
obscureText: true,
controller: _ipController,
),
actions: [
TextButton(
child: Text(t.global.dialogAccept),
onPressed: () {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(debugIp: _ipController.text),
),
);
Navigator.of(context).pop();
},
)
],
),
);
},
),
SettingsRow(
title: t.pages.settings.debugging.generalLoggingPort,
description: t.pages.settings.debugging.generalLoggingPortSubtext,
onTap: () {
showDialog<void>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(t.pages.settings.debugging.generalLoggingPort),
content: TextField(
minLines: 1,
controller: _portController,
keyboardType: TextInputType.number,
),
actions: [
TextButton(
child: Text(t.global.dialogAccept),
onPressed: () {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(debugPort: int.parse(_portController.text)),
),
);
Navigator.of(context).pop();
},
)
],
),
);
},
),
],
),
),

View File

@ -4,8 +4,9 @@ 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/settings/row.dart';
import 'package:moxxyv2/ui/widgets/settings/title.dart';
import 'package:moxxyv2/ui/widgets/topbar.dart';
import 'package:settings_ui/settings_ui.dart';
class _AutoDownloadSizes {
const _AutoDownloadSizes(this.text, this.value);
@ -45,6 +46,9 @@ class AutoDownloadSizeDialogState extends State<AutoDownloadSizeDialog> {
@override
Widget build(BuildContext context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(textfieldRadiusRegular),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 32,
vertical: 12,
@ -101,56 +105,66 @@ class NetworkPage extends StatelessWidget {
return Scaffold(
appBar: BorderlessTopbar.simple(t.pages.settings.network.title),
body: BlocBuilder<PreferencesBloc, PreferencesState>(
builder: (context, state) => SettingsList(
sections: [
SettingsSection(
title: Text(t.pages.settings.network.automaticDownloadsSection),
tiles: [
SettingsTile(
title: Text(t.pages.settings.network.automaticDownloadsText),
),
SettingsTile.switchTile(
title: Text(t.pages.settings.network.wifi),
initialValue: state.autoDownloadWifi,
onToggle: (value) => context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(autoDownloadWifi: value),
),
),
),
SettingsTile.switchTile(
title: Text(t.pages.settings.network.mobileData),
initialValue: state.autoDownloadMobile,
onToggle: (value) => context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(autoDownloadMobile: value),
),
),
),
SettingsTile(
title: Text(t.pages.settings.network.automaticDownloadsMaximumSize),
description: Text(t.pages.settings.network.automaticDownloadsMaximumSizeSubtext),
onPressed: (context) async {
final result = await showDialog<int>(
context: context,
barrierDismissible: false,
builder: (context) => AutoDownloadSizeDialog(
selectedValueInitial: state.maximumAutoDownloadSize,
),
);
if (result == null) return;
if (state.maximumAutoDownloadSize == result) return;
builder: (context, state) => ListView(
children: [
SectionTitle(t.pages.settings.network.automaticDownloadsSection),
SettingsRow(
title: t.pages.settings.network.automaticDownloadsText,
),
// ignore: use_build_context_synchronously
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(maximumAutoDownloadSize: result),
),
);
},
SettingsRow(
title: t.pages.settings.network.wifi,
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 16,
),
suffix: Switch(
value: state.autoDownloadWifi,
onChanged: (value) => context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(autoDownloadWifi: value),
),
),
],
)
),
),
SettingsRow(
title: t.pages.settings.network.mobileData,
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 16,
),
suffix: Switch(
value: state.autoDownloadMobile,
onChanged: (value) => context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(autoDownloadMobile: value),
),
),
),
),
SettingsRow(
title: t.pages.settings.network.automaticDownloadsMaximumSize,
description: t.pages.settings.network.automaticDownloadsMaximumSizeSubtext,
onTap: () async {
final result = await showDialog<int>(
context: context,
builder: (context) => AutoDownloadSizeDialog(
selectedValueInitial: state.maximumAutoDownloadSize,
),
);
if (result == null) return;
if (state.maximumAutoDownloadSize == result) return;
// ignore: use_build_context_synchronously
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(maximumAutoDownloadSize: result),
),
);
},
),
],
),
),

View File

@ -5,8 +5,9 @@ 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/pages/settings/privacy/tile.dart';
import 'package:moxxyv2/ui/widgets/settings/row.dart';
import 'package:moxxyv2/ui/widgets/settings/title.dart';
import 'package:moxxyv2/ui/widgets/topbar.dart';
import 'package:settings_ui/settings_ui.dart';
class PrivacyPage extends StatelessWidget {
const PrivacyPage({ super.key });
@ -23,88 +24,99 @@ class PrivacyPage extends StatelessWidget {
return Scaffold(
appBar: BorderlessTopbar.simple(t.pages.settings.privacy.title),
body: BlocBuilder<PreferencesBloc, PreferencesState>(
builder: (context, state) => SettingsList(
sections: [
SettingsSection(
title: Text(t.pages.settings.privacy.generalSection),
tiles: [
SettingsTile.switchTile(
title: Text(t.pages.settings.privacy.showContactRequests),
description: Text(t.pages.settings.privacy.showContactRequestsSubtext),
initialValue: state.showSubscriptionRequests,
onToggle: (value) => context.read<PreferencesBloc>().add(
builder: (context, state) => ListView(
children: [
SectionTitle(t.pages.settings.privacy.generalSection),
SettingsRow(
title: t.pages.settings.privacy.showContactRequests,
description: t.pages.settings.privacy.showContactRequestsSubtext,
suffix: Switch(
value: state.showSubscriptionRequests,
onChanged: (value) {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(showSubscriptionRequests: value),
),
),
),
SettingsTile.switchTile(
title: Text(t.pages.settings.privacy.profilePictureVisibility),
description: Text(t.pages.settings.privacy.profilePictureVisibilitSubtext),
initialValue: state.isAvatarPublic,
onToggle: (value) => context.read<PreferencesBloc>().add(
);
},
),
),
SettingsRow(
title: t.pages.settings.privacy.profilePictureVisibility,
description: t.pages.settings.privacy.profilePictureVisibilitSubtext,
suffix: Switch(
value: state.isAvatarPublic,
onChanged: (value) {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(isAvatarPublic: value),
),
),
),
SettingsTile.switchTile(
title: Text(t.pages.settings.privacy.autoAcceptSubscriptionRequests),
description: Text(t.pages.settings.privacy.autoAcceptSubscriptionRequestsSubtext),
initialValue: state.autoAcceptSubscriptionRequests,
onToggle: (value) => context.read<PreferencesBloc>().add(
);
},
),
),
SettingsRow(
title: t.pages.settings.privacy.autoAcceptSubscriptionRequests,
description: t.pages.settings.privacy.autoAcceptSubscriptionRequestsSubtext,
suffix: Switch(
value: state.autoAcceptSubscriptionRequests,
onChanged: (value) {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(autoAcceptSubscriptionRequests: value),
),
),
)
],
);
},
),
),
SettingsSection(
title: Text(t.pages.settings.privacy.conversationsSection),
tiles: [
SettingsTile.switchTile(
title: Text(t.pages.settings.privacy.sendChatMarkers),
description: Text(t.pages.settings.privacy.sendChatMarkersSubtext),
initialValue: state.sendChatMarkers,
onToggle: (value) => context.read<PreferencesBloc>().add(
SectionTitle(t.pages.settings.privacy.conversationsSection),
SettingsRow(
title: t.pages.settings.privacy.sendChatMarkers,
description: t.pages.settings.privacy.sendChatMarkersSubtext,
suffix: Switch(
value: state.sendChatMarkers,
onChanged: (value) {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(sendChatMarkers: value),
),
),
),
SettingsTile.switchTile(
title: Text(t.pages.settings.privacy.sendChatStates),
description: Text(t.pages.settings.privacy.sendChatStatesSubtext),
initialValue: state.sendChatStates,
onToggle: (value) => context.read<PreferencesBloc>().add(
);
},
),
),
SettingsRow(
title: t.pages.settings.privacy.sendChatStates,
description: t.pages.settings.privacy.sendChatStatesSubtext,
suffix: Switch(
value: state.sendChatStates,
onChanged: (value) {
context.read<PreferencesBloc>().add(
PreferencesChangedEvent(
state.copyWith(sendChatStates: value),
),
),
)
],
);
},
),
),
SettingsSection(
title: Text(t.pages.settings.privacy.redirectsSection),
tiles: [
RedirectSettingsTile(
'Youtube',
'Invidious',
(state) => state.youtubeRedirect,
(state, value) => state.copyWith(youtubeRedirect: value),
(state) => state.enableYoutubeRedirect,
(state, value) => state.copyWith(enableYoutubeRedirect: value),
),
RedirectSettingsTile(
'Twitter',
'Nitter',
(state) => state.twitterRedirect,
(state, value) => state.copyWith(twitterRedirect: value),
(state) => state.enableTwitterRedirect,
(state, value) => state.copyWith(enableTwitterRedirect: value),
),
],
SectionTitle(t.pages.settings.privacy.redirectsSection),
RedirectSettingsTile(
'Youtube',
'Invidious',
(state) => state.youtubeRedirect,
(state, value) => state.copyWith(youtubeRedirect: value),
(state) => state.enableYoutubeRedirect,
(state, value) => state.copyWith(enableYoutubeRedirect: value),
),
RedirectSettingsTile(
'Twitter',
'Nitter',
(state) => state.twitterRedirect,
(state, value) => state.copyWith(twitterRedirect: value),
(state) => state.enableTwitterRedirect,
(state, value) => state.copyWith(enableTwitterRedirect: value),
),
],
),

View File

@ -5,9 +5,9 @@ import 'package:moxxyv2/shared/models/preferences.dart';
import 'package:moxxyv2/ui/bloc/preferences_bloc.dart';
import 'package:moxxyv2/ui/helpers.dart';
import 'package:moxxyv2/ui/pages/settings/privacy/redirect_dialog.dart';
import 'package:settings_ui/settings_ui.dart';
import 'package:moxxyv2/ui/widgets/settings/row.dart';
class RedirectSettingsTile extends AbstractSettingsTile {
class RedirectSettingsTile extends StatelessWidget {
const RedirectSettingsTile(
this.serviceName,
this.exampleProxy,
@ -27,28 +27,13 @@ class RedirectSettingsTile extends AbstractSettingsTile {
@override
Widget build(BuildContext context) {
return BlocBuilder<PreferencesBloc, PreferencesState>(
builder: (context, state) => SettingsTile(
title: Text(t.pages.settings.privacy.redirectsTitle(serviceName: serviceName)),
description: Column(
children: [
Align(
alignment: Alignment.centerLeft,
child: Text(
t.pages.settings.privacy.redirectText(
serviceName: serviceName,
exampleProxy: exampleProxy,
),
),
),
Align(
alignment: Alignment.centerLeft,
child: Text(
t.pages.settings.privacy.currentlySelected(proxy: getProxy(state)),
),
),
],
builder: (context, state) => SettingsRow(
title: t.pages.settings.privacy.redirectsTitle(serviceName: serviceName),
description: t.pages.settings.privacy.redirectText(
serviceName: serviceName,
exampleProxy: exampleProxy,
),
onPressed: (context) {
onTap: () {
showDialog<void>(
context: context,
builder: (BuildContext context) => RedirectDialog(
@ -60,7 +45,7 @@ class RedirectSettingsTile extends AbstractSettingsTile {
),
);
},
trailing: Switch(
suffix: Switch(
value: getEnabled(state),
onChanged: (value) {
if (getProxy(state).isEmpty) {

View File

@ -5,9 +5,10 @@ import 'package:moxxyv2/i18n/strings.g.dart';
import 'package:moxxyv2/ui/bloc/preferences_bloc.dart';
import 'package:moxxyv2/ui/constants.dart';
import 'package:moxxyv2/ui/helpers.dart';
import 'package:moxxyv2/ui/widgets/settings/row.dart';
import 'package:moxxyv2/ui/widgets/settings/title.dart';
import 'package:moxxyv2/ui/widgets/topbar.dart';
import 'package:phosphor_flutter/phosphor_flutter.dart';
import 'package:settings_ui/settings_ui.dart';
class SettingsPage extends StatelessWidget {
const SettingsPage({ super.key });
@ -23,91 +24,126 @@ class SettingsPage extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: BorderlessTopbar.simple(t.pages.settings.settings.title),
body: SettingsList(
sections: [
SettingsSection(
title: Text(t.pages.settings.settings.conversationsSection),
tiles: [
SettingsTile(
title: Text(t.pages.settings.conversation.title),
leading: const Icon(Icons.chat_bubble),
onPressed: (context) => Navigator.pushNamed(context, conversationSettingsRoute),
),
SettingsTile(
title: Text(t.pages.settings.stickers.title),
leading: const Icon(PhosphorIcons.stickerBold),
onPressed: (context) => Navigator.pushNamed(context, stickersRoute),
),
SettingsTile(
title: Text(t.pages.settings.network.title),
leading: const Icon(Icons.network_wifi),
onPressed: (context) => Navigator.pushNamed(context, networkRoute),
),
SettingsTile(
title: Text(t.pages.settings.privacy.title),
leading: const Icon(Icons.shield),
onPressed: (context) => Navigator.pushNamed(context, privacyRoute),
)
],
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);
},
),
SettingsSection(
title: Text(t.pages.settings.settings.accountSection),
tiles: [
SettingsTile(
title: Text(t.pages.blocklist.title),
leading: const Icon(Icons.block),
onPressed: (context) => Navigator.pushNamed(context, blocklistRoute),
),
SettingsTile(
title: Text(t.pages.settings.settings.signOut),
leading: const Icon(Icons.logout),
onPressed: (context) 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.accountSection),
SettingsRow(
title: t.pages.blocklist.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.block),
),
onTap: () {
Navigator.pushNamed(context, blocklistRoute);
},
),
SettingsSection(
title: Text(t.pages.settings.settings.miscellaneousSection),
tiles: [
SettingsTile(
title: Text(t.pages.settings.appearance.title),
leading: const Icon(Icons.brush),
onPressed: (context) => Navigator.pushNamed(context, appearanceRoute),
),
SettingsTile(
title: Text(t.pages.settings.about.title),
leading: const Icon(Icons.info),
onPressed: (context) => Navigator.pushNamed(context, aboutRoute),
),
SettingsTile(
title: Text(t.pages.settings.licenses.title),
leading: const Icon(Icons.description),
onPressed: (context) => Navigator.pushNamed(context, licensesRoute),
)
],
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());
}
},
),
// TODO(Unknown): Maybe also have a switch somewhere
...kDebugMode ? [
SettingsSection(
title: Text(t.pages.settings.settings.debuggingSection),
tiles: [
SettingsTile(
title: Text(t.pages.settings.debugging.title),
leading: const Icon(Icons.info),
onPressed: (context) => Navigator.pushNamed(context, debuggingRoute),
)
],
)
] : []
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)
SettingsRow(
title: t.pages.settings.debugging.title,
prefix: const Padding(
padding: EdgeInsets.only(right: 16),
child: Icon(Icons.info),
),
onTap: () {
Navigator.pushNamed(context, debuggingRoute);
},
),
],
),
);

View File

@ -12,6 +12,10 @@ class SettingsRow extends StatelessWidget {
this.prefix,
this.onTap,
this.crossAxisAlignment = CrossAxisAlignment.center,
this.padding = const EdgeInsets.symmetric(
vertical: 16,
horizontal: 16,
),
super.key,
});
final String title;
@ -21,16 +25,14 @@ class SettingsRow extends StatelessWidget {
final Widget? prefix;
final void Function()? onTap;
final CrossAxisAlignment crossAxisAlignment;
final EdgeInsets padding;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 16,
horizontal: 16,
),
padding: padding,
child: Row(
crossAxisAlignment: crossAxisAlignment,
children: [

View File

@ -1150,13 +1150,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
settings_ui:
dependency: "direct main"
description:
name: settings_ui
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
share_handler:
dependency: "direct main"
description:

View File

@ -84,7 +84,6 @@ dependencies:
qr_flutter: 4.0.0
random_string: 2.3.1
record: 4.4.3
settings_ui: 2.0.2
share_handler: 0.0.16
slang: 3.4.0
slang_flutter: 3.4.0