feat(privacy): add personalized ads toggle in settings

- Add PrivacySettings schema with can_sell field (default: false)
- Move privacy toggle to GeneralSettingsScreen with clear labeling
- Update l10n for zh/en/zh_hant with user-friendly copy
- Clean up redundant PrivacyNotificationSettingsScreen
- Update profile-protocol.md to reflect new privacy schema
- Fix basedpyright warnings in user.py
This commit is contained in:
qzl
2026-04-17 13:11:09 +08:00
parent be30eb6eab
commit 913ed26f8d
17 changed files with 417 additions and 163 deletions
@@ -104,6 +104,21 @@ class _GeneralSettingsScreenState extends State<GeneralSettingsScreen> {
],
),
const SizedBox(height: AppSpacing.lg),
SectionLabel(text: l10n.settingsSectionPrivacy),
SettingsGroupCard(
children: [
SettingsSwitchTile(
icon: Icons.security_rounded,
title: l10n.settingsDoNotSellTitle,
value: _settings.privacy.canSell,
tint: colors.primary,
background: colors.surfaceContainerHighest,
showDivider: false,
onChanged: (value) => _updatePrivacy(canSell: value),
),
],
),
const SizedBox(height: AppSpacing.lg),
SectionLabel(text: l10n.settingsSectionNotification),
SettingsGroupCard(
children: [
@@ -151,6 +166,15 @@ class _GeneralSettingsScreenState extends State<GeneralSettingsScreen> {
await widget.onSettingsChanged(_settings);
}
void _updatePrivacy({bool? canSell}) {
final newPrivacy = _settings.privacy.copyWith(canSell: canSell);
final newSettings = _settings.copyWith(privacy: newPrivacy);
setState(() {
_settings = newSettings;
});
widget.onSettingsChanged(newSettings);
}
void _updateNotification({bool? allowNotifications, bool? allowVibration}) {
final newNotification = _settings.notification.copyWith(
allowNotifications: allowNotifications,
@@ -1,144 +0,0 @@
import 'package:flutter/material.dart';
import '../../../../l10n/app_localizations.dart';
import '../../../../shared/theme/design_tokens.dart';
import '../../data/models/profile_settings.dart';
import 'settings_placeholder_screen.dart';
import '../widgets/settings_section_widgets.dart';
class PrivacyNotificationSettingsScreen extends StatelessWidget {
const PrivacyNotificationSettingsScreen({super.key, required this.settings});
final ProfileSettingsV1 settings;
@override
Widget build(BuildContext context) {
final l10n = AppLocalizations.of(context)!;
final colors = Theme.of(context).colorScheme;
return Scaffold(
backgroundColor: colors.surfaceContainerLow,
appBar: AppBar(
title: Text(l10n.settingsPrivacyAndNotificationTitle),
centerTitle: true,
backgroundColor: colors.surfaceContainerLow,
surfaceTintColor: colors.surfaceContainerLow,
),
body: ListView(
padding: const EdgeInsets.all(AppSpacing.lg),
children: [
SectionLabel(text: l10n.settingsSectionPrivacy),
SettingsGroupCard(
children: [
SettingsMenuTile(
icon: Icons.visibility_outlined,
title: l10n.settingsPrivacyProfileVisibility,
subtitle: l10n.settingsPlaceholderState(
settings.privacy.length,
),
tint: colors.primary,
background: colors.surfaceContainerHighest,
onTap: () => _openPlaceholder(
context,
title: l10n.settingsPrivacyProfileVisibility,
value: l10n.settingsComingSoon,
description: l10n.settingsPrivacyHint,
),
),
SettingsMenuTile(
icon: Icons.psychology_alt_outlined,
title: l10n.settingsPrivacyPersonalization,
subtitle: l10n.settingsComingSoon,
tint: colors.primary,
background: colors.surfaceContainerHighest,
onTap: () => _openPlaceholder(
context,
title: l10n.settingsPrivacyPersonalization,
value: l10n.settingsComingSoon,
description: l10n.settingsPrivacyHint,
),
),
SettingsMenuTile(
icon: Icons.history_toggle_off_rounded,
title: l10n.settingsPrivacyHistoryVisibility,
subtitle: l10n.settingsComingSoon,
tint: colors.primary,
background: colors.surfaceContainerHighest,
showDivider: false,
onTap: () => _openPlaceholder(
context,
title: l10n.settingsPrivacyHistoryVisibility,
value: l10n.settingsComingSoon,
description: l10n.settingsPrivacyHint,
),
),
],
),
const SizedBox(height: AppSpacing.xl),
SectionLabel(text: l10n.settingsSectionNotification),
SettingsGroupCard(
children: [
SettingsMenuTile(
icon: Icons.notifications_outlined,
title: l10n.settingsNotificationSystem,
subtitle: l10n.settingsPlaceholderState(1),
tint: colors.secondary,
background: colors.surfaceContainerHighest,
onTap: () => _openPlaceholder(
context,
title: l10n.settingsNotificationSystem,
value: l10n.settingsComingSoon,
description: l10n.settingsNotificationHint,
),
),
SettingsMenuTile(
icon: Icons.campaign_outlined,
title: l10n.settingsNotificationActivity,
subtitle: l10n.settingsComingSoon,
tint: colors.secondary,
background: colors.surfaceContainerHighest,
onTap: () => _openPlaceholder(
context,
title: l10n.settingsNotificationActivity,
value: l10n.settingsComingSoon,
description: l10n.settingsNotificationHint,
),
),
SettingsMenuTile(
icon: Icons.auto_graph_outlined,
title: l10n.settingsNotificationResult,
subtitle: l10n.settingsComingSoon,
tint: colors.secondary,
background: colors.surfaceContainerHighest,
showDivider: false,
onTap: () => _openPlaceholder(
context,
title: l10n.settingsNotificationResult,
value: l10n.settingsComingSoon,
description: l10n.settingsNotificationHint,
),
),
],
),
],
),
);
}
Future<void> _openPlaceholder(
BuildContext context, {
required String title,
required String value,
required String description,
}) async {
await Navigator.of(context).push<void>(
MaterialPageRoute<void>(
builder: (_) => SettingsPlaceholderScreen(
title: title,
value: value,
description: description,
),
),
);
}
}