improve setup

This commit is contained in:
otsmr 2026-04-29 15:48:52 +02:00
parent 836e58ec3a
commit c9b8e32d32
19 changed files with 474 additions and 422 deletions

View file

@ -155,7 +155,7 @@ Future<void> runMigrations() async {
if (u.avatarSvg == null) { if (u.avatarSvg == null) {
u.currentSetupPage = SetupPages.profile.name; u.currentSetupPage = SetupPages.profile.name;
} else { } else {
u.currentSetupPage = SetupPages.userDiscovery.name; u.currentSetupPage = SetupPages.shareYourFriends.name;
} }
}); });
} }

View file

@ -2399,7 +2399,7 @@ abstract class AppLocalizations {
/// No description provided for @onboardingUserDiscoveryShareFriendsDesc. /// No description provided for @onboardingUserDiscoveryShareFriendsDesc.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Share with your friends who you know and who you have verified. Friends can *only see mutual friends* from your friend list.'** /// **'Share with your friends who you know and who you have verified. Friends can *only see mutual friends* from your friend list. You can change your mind at *any time* or *hide specific people*.'**
String get onboardingUserDiscoveryShareFriendsDesc; String get onboardingUserDiscoveryShareFriendsDesc;
/// No description provided for @onboardingUserDiscoveryContactsVerifiedBadge. /// No description provided for @onboardingUserDiscoveryContactsVerifiedBadge.
@ -2429,7 +2429,7 @@ abstract class AppLocalizations {
/// No description provided for @userDiscoverySettingsManualApprovalDesc. /// No description provided for @userDiscoverySettingsManualApprovalDesc.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Before sharing someone, you will be asked every time someone reaches the number of send images.'** /// **'Before someone is shared, you\'ll be asked first.'**
String get userDiscoverySettingsManualApprovalDesc; String get userDiscoverySettingsManualApprovalDesc;
/// No description provided for @onboardingUserDiscoveryLetFriendsFindYou. /// No description provided for @onboardingUserDiscoveryLetFriendsFindYou.
@ -2989,6 +2989,18 @@ abstract class AppLocalizations {
/// In en, this message translates to: /// In en, this message translates to:
/// **'Share contact'** /// **'Share contact'**
String get userDiscoveryManualApprovalShareContact; String get userDiscoveryManualApprovalShareContact;
/// No description provided for @onboardingSetupCompleteTitle.
///
/// In en, this message translates to:
/// **'You\'re all set, {username}!'**
String onboardingSetupCompleteTitle(Object username);
/// No description provided for @onboardingSetupCompleteDesc.
///
/// In en, this message translates to:
/// **'You can now share your moments with your friends securely without distractions like ads.'**
String get onboardingSetupCompleteDesc;
} }
class _AppLocalizationsDelegate class _AppLocalizationsDelegate

View file

@ -1328,7 +1328,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get userDiscoverySettingsManualApprovalDesc => String get userDiscoverySettingsManualApprovalDesc =>
'Bevor du jemanden teilst, wirst du jedes Mal gefragt, sobald jemand die Anzahl der gesendeten Bilder erreicht hat.'; 'Bevor jemand geteilt wird, wirst du zuerst gefragt.';
@override @override
String get onboardingUserDiscoveryLetFriendsFindYou => String get onboardingUserDiscoveryLetFriendsFindYou =>
@ -1683,4 +1683,13 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get userDiscoveryManualApprovalShareContact => 'Kontakt teilen'; String get userDiscoveryManualApprovalShareContact => 'Kontakt teilen';
@override
String onboardingSetupCompleteTitle(Object username) {
return 'Du bist startklar, $username!';
}
@override
String get onboardingSetupCompleteDesc =>
'Du kannst jetzt deine Momente sicher mit deinen Freunden teilen, ohne Ablenkungen wie Werbung.';
} }

View file

@ -1300,7 +1300,7 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get onboardingUserDiscoveryShareFriendsDesc => String get onboardingUserDiscoveryShareFriendsDesc =>
'Share with your friends who you know and who you have verified. Friends can *only see mutual friends* from your friend list.'; 'Share with your friends who you know and who you have verified. Friends can *only see mutual friends* from your friend list. You can change your mind at *any time* or *hide specific people*.';
@override @override
String get onboardingUserDiscoveryContactsVerifiedBadge => String get onboardingUserDiscoveryContactsVerifiedBadge =>
@ -1319,7 +1319,7 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get userDiscoverySettingsManualApprovalDesc => String get userDiscoverySettingsManualApprovalDesc =>
'Before sharing someone, you will be asked every time someone reaches the number of send images.'; 'Before someone is shared, you\'ll be asked first.';
@override @override
String get onboardingUserDiscoveryLetFriendsFindYou => String get onboardingUserDiscoveryLetFriendsFindYou =>
@ -1667,4 +1667,13 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get userDiscoveryManualApprovalShareContact => 'Share contact'; String get userDiscoveryManualApprovalShareContact => 'Share contact';
@override
String onboardingSetupCompleteTitle(Object username) {
return 'You\'re all set, $username!';
}
@override
String get onboardingSetupCompleteDesc =>
'You can now share your moments with your friends securely without distractions like ads.';
} }

@ -1 +1 @@
Subproject commit 492bef11cf6bd472a71bd1c1b3007d731adea433 Subproject commit 96fc996e06f96a7a26438bba0421cb26eb721427

View file

@ -48,10 +48,6 @@ class MainCameraPreview extends StatelessWidget {
mainCameraController.cameraController!, mainCameraController.cameraController!,
child: Stack( child: Stack(
children: [ children: [
if (mainCameraController.customPaint != null)
Positioned.fill(
child: mainCameraController.customPaint!,
),
if (mainCameraController.facePaint != null) if (mainCameraController.facePaint != null)
Positioned.fill( Positioned.fill(
child: mainCameraController.facePaint!, child: mainCameraController.facePaint!,
@ -64,8 +60,7 @@ class MainCameraPreview extends StatelessWidget {
), ),
), ),
), ),
if (mainCameraController.focusPointOffset != null && if (!mainCameraController.isSharePreviewIsShown)
!mainCameraController.isSharePreviewIsShown)
AspectRatio( AspectRatio(
aspectRatio: 9 / 16, aspectRatio: 9 / 16,
child: ClipRect( child: ClipRect(
@ -84,22 +79,30 @@ class MainCameraPreview extends StatelessWidget {
.width, .width,
child: Stack( child: Stack(
children: [ children: [
Positioned( if (mainCameraController.qrCodePain != null)
top: mainCameraController.focusPointOffset!.dy - 40, Positioned.fill(
left: child: mainCameraController.qrCodePain!,
mainCameraController.focusPointOffset!.dx - 40, ),
child: Container( if (mainCameraController.focusPointOffset != null)
height: 80, Positioned(
width: 80, top:
clipBehavior: Clip.antiAliasWithSaveLayer, mainCameraController.focusPointOffset!.dy -
decoration: BoxDecoration( 40,
shape: BoxShape.circle, left:
border: Border.all( mainCameraController.focusPointOffset!.dx -
color: Colors.white.withAlpha(150), 40,
child: Container(
height: 80,
width: 80,
clipBehavior: Clip.antiAliasWithSaveLayer,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.white.withAlpha(150),
),
), ),
), ),
), ),
),
], ],
), ),
), ),

View file

@ -76,7 +76,7 @@ class MainCameraController {
); );
bool _isBusy = false; bool _isBusy = false;
bool _isBusyFaces = false; bool _isBusyFaces = false;
CustomPaint? customPaint; CustomPaint? qrCodePain;
CustomPaint? facePaint; CustomPaint? facePaint;
Offset? focusPointOffset; Offset? focusPointOffset;
@ -183,7 +183,7 @@ class MainCameraController {
..cameraId = cameraId; ..cameraId = cameraId;
facePaint = null; facePaint = null;
customPaint = null; qrCodePain = null;
isSelectingFaceFilters = false; isSelectingFaceFilters = false;
setFilter(FaceFilterType.none); setFilter(FaceFilterType.none);
zoomButtonKey = GlobalKey(); zoomButtonKey = GlobalKey();
@ -334,7 +334,7 @@ class MainCameraController {
inputImage.metadata!.rotation, inputImage.metadata!.rotation,
cameraController!.description.lensDirection, cameraController!.description.lensDirection,
); );
customPaint = CustomPaint(painter: painter); qrCodePain = CustomPaint(painter: painter);
if (barcodes.isEmpty && timeSharedLinkWasSetWithQr != null) { if (barcodes.isEmpty && timeSharedLinkWasSetWithQr != null) {
if (timeSharedLinkWasSetWithQr!.isAfter( if (timeSharedLinkWasSetWithQr!.isAfter(

View file

@ -286,10 +286,17 @@ class _ContactViewState extends State<ContactView> {
..._transferredTrust.map( ..._transferredTrust.map(
(tt) => ListTile( (tt) => ListTile(
dense: true, dense: true,
title: Text( title: Row(
context.lang.contactVerifiedBy( children: [
getContactDisplayName(tt.$1), Text(
), context.lang.contactVerifiedBy(
getContactDisplayName(tt.$1),
),
),
VerificationBadgeComp(
contact: tt.$1,
),
],
), ),
trailing: Text( trailing: Text(
DateFormat.yMd( DateFormat.yMd(

View file

@ -4,18 +4,21 @@ import 'package:flutter/material.dart';
import 'package:twonly/locator.dart'; import 'package:twonly/locator.dart';
import 'package:twonly/src/services/user.service.dart'; import 'package:twonly/src/services/user.service.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/visual/views/onboarding/setup/add_new_contacts_setup.view.dart'; import 'package:twonly/src/visual/views/onboarding/setup/add_new_contacts.setup.dart';
import 'package:twonly/src/visual/views/onboarding/setup/backup_setup.view.dart'; import 'package:twonly/src/visual/views/onboarding/setup/backup.setup.dart';
import 'package:twonly/src/visual/views/onboarding/setup/profile_setup.view.dart'; import 'package:twonly/src/visual/views/onboarding/setup/let_your_friends_find_you.setup.dart';
import 'package:twonly/src/visual/views/onboarding/setup/user_discovery_setup.view.dart'; import 'package:twonly/src/visual/views/onboarding/setup/profile.setup.dart';
import 'package:twonly/src/visual/views/onboarding/setup/verification_badge_setup.view.dart'; import 'package:twonly/src/visual/views/onboarding/setup/share_your_friends.setup.dart';
import 'package:twonly/src/visual/views/onboarding/setup/verification_badge.setup.dart';
import 'package:twonly/src/visual/views/settings/privacy/user_discovery/components/user_discovery_setup.comp.dart';
enum SetupPages { enum SetupPages {
profile, profile,
backup, backup,
addNewContact, addNewContact,
verificationBadge, verificationBadge,
userDiscovery, shareYourFriends,
letYourFriendsFindYou,
} }
extension SetupPagesExtension on SetupPages { extension SetupPagesExtension on SetupPages {
@ -28,7 +31,7 @@ extension SetupPagesExtension on SetupPages {
int get pageNumber => index + 1; int get pageNumber => index + 1;
int get totalPages => SetupPages.values.length; int get totalPages => SetupPages.values.length;
int get progressPercentage => (pageNumber / totalPages * 100).round(); int get progressPercentage => ((pageNumber - 1) / totalPages * 100).round();
String get progressText => '$pageNumber / $totalPages'; String get progressText => '$pageNumber / $totalPages';
bool get isLast => index == SetupPages.values.length - 1; bool get isLast => index == SetupPages.values.length - 1;
@ -53,10 +56,13 @@ class SetupView extends StatefulWidget {
class _SetupViewState extends State<SetupView> { class _SetupViewState extends State<SetupView> {
StreamSubscription<void>? _userUpdateStream; StreamSubscription<void>? _userUpdateStream;
late UserDiscoverySetupState state;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
state = UserDiscoverySetupState(setState: setState);
if (widget.onUpdate != null) { if (widget.onUpdate != null) {
_userUpdateStream = userService.onUserUpdated.listen((u) { _userUpdateStream = userService.onUserUpdated.listen((u) {
if (userService.currentUser.currentSetupPage == null) { if (userService.currentUser.currentSetupPage == null) {
@ -115,7 +121,7 @@ class _SetupViewState extends State<SetupView> {
key: ValueKey(currentPage.name), key: ValueKey(currentPage.name),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 30),
children: [ children: [
_buildPage(currentPage), _buildPage(currentPage, state),
if (!currentPage.isLast) if (!currentPage.isLast)
SizedBox( SizedBox(
height: 50, height: 50,
@ -145,7 +151,7 @@ class _SetupViewState extends State<SetupView> {
); );
} }
Widget _buildPage(SetupPages page) { Widget _buildPage(SetupPages page, UserDiscoverySetupState state) {
switch (page) { switch (page) {
case SetupPages.profile: case SetupPages.profile:
return const ProfileSetupPage(); return const ProfileSetupPage();
@ -155,8 +161,10 @@ class _SetupViewState extends State<SetupView> {
return const AddNewContactsPage(); return const AddNewContactsPage();
case SetupPages.verificationBadge: case SetupPages.verificationBadge:
return const VerificationBadgeSetupPage(); return const VerificationBadgeSetupPage();
case SetupPages.userDiscovery: case SetupPages.shareYourFriends:
return const UserDiscoverySetupPage(); return ShareYourFriendsSetupPage(state: state);
case SetupPages.letYourFriendsFindYou:
return LetYourFriendsFindYou(state: state);
} }
} }
} }

View file

@ -15,7 +15,9 @@ class _FinishSetupCompState extends State<FinishSetupComp> {
await context.navPush( await context.navPush(
SetupView( SetupView(
onUpdate: () { onUpdate: () {
Navigator.pop(context); if (mounted) {
Navigator.pop(context);
}
}, },
), ),
); );

View file

@ -0,0 +1,88 @@
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
import 'package:twonly/locator.dart';
import 'package:twonly/src/services/user.service.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/visual/views/onboarding/setup.view.dart';
import 'package:twonly/src/visual/views/onboarding/setup/components/next_button.comp.dart';
import 'package:twonly/src/visual/views/settings/privacy/user_discovery/components/user_discovery_setup.comp.dart';
class LetYourFriendsFindYou extends StatefulWidget {
const LetYourFriendsFindYou({required this.state, super.key});
final UserDiscoverySetupState state;
@override
State<LetYourFriendsFindYou> createState() => _LetYourFriendsFindYouState();
}
class _LetYourFriendsFindYouState extends State<LetYourFriendsFindYou> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (userService.currentUser.isUserDiscoveryEnabled &&
userService.currentUser.userDiscoverySharePromotion) {
// feature is already configured...
UserService.update((user) {
user.currentSetupPage = SetupPages.letYourFriendsFindYou.next()?.name;
});
}
});
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (widget.state.isUserDiscoveryEnabled)
UserDiscoverySetupComp(
state: widget.state,
showOnlySpecificPage: UserDiscoveryPages.letYourFriendsFindYou,
)
else
Container(
padding: const EdgeInsets.all(32),
child: Column(
children: [
Lottie.asset(
'assets/animations/takephoto.lottie',
repeat: true,
height: 150,
),
const SizedBox(height: 60),
Text(
context.lang.onboardingSetupCompleteTitle(
userService.currentUser.username,
),
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 12),
Text(
context.lang.onboardingSetupCompleteDesc,
style: const TextStyle(
fontSize: 16,
),
textAlign: TextAlign.center,
),
],
),
),
const SizedBox(height: 50),
NextButtonComp(
onPressed: () async {
return !(await widget.state.initializeOrUpdate());
},
),
],
),
);
}
}

View file

@ -1,30 +1,30 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:twonly/locator.dart'; import 'package:twonly/locator.dart';
import 'package:twonly/src/services/user.service.dart'; import 'package:twonly/src/services/user.service.dart';
import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/visual/views/onboarding/setup.view.dart'; import 'package:twonly/src/visual/views/onboarding/setup.view.dart';
import 'package:twonly/src/visual/views/onboarding/setup/components/next_button.comp.dart'; import 'package:twonly/src/visual/views/onboarding/setup/components/next_button.comp.dart';
import 'package:twonly/src/visual/views/settings/privacy/user_discovery/components/user_discovery_setup.comp.dart'; import 'package:twonly/src/visual/views/settings/privacy/user_discovery/components/user_discovery_setup.comp.dart';
class UserDiscoverySetupPage extends StatefulWidget { class ShareYourFriendsSetupPage extends StatefulWidget {
const UserDiscoverySetupPage({super.key}); const ShareYourFriendsSetupPage({required this.state, super.key});
final UserDiscoverySetupState state;
@override @override
State<UserDiscoverySetupPage> createState() => _UserDiscoverySetupPageState(); State<ShareYourFriendsSetupPage> createState() =>
_ShareYourFriendsSetupPageState();
} }
class _UserDiscoverySetupPageState extends State<UserDiscoverySetupPage> { class _ShareYourFriendsSetupPageState extends State<ShareYourFriendsSetupPage> {
late UserDiscoverySetupState state;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
state = UserDiscoverySetupState(setState: setState);
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (userService.currentUser.isUserDiscoveryEnabled) { if (userService.currentUser.isUserDiscoveryEnabled) {
// feature is already configured... // feature is already configured...
UserService.update((user) { UserService.update((user) {
user.currentSetupPage = SetupPages.userDiscovery.next()?.name; user.currentSetupPage = SetupPages.shareYourFriends.next()?.name;
}); });
} }
}); });
@ -36,17 +36,15 @@ class _UserDiscoverySetupPageState extends State<UserDiscoverySetupPage> {
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Text( UserDiscoverySetupComp(
context.lang.onboardingUserDiscoveryShareFriends, state: widget.state,
style: Theme.of(context).textTheme.headlineSmall, showOnlySpecificPage: UserDiscoveryPages.shareYourFriends,
), ),
const SizedBox(height: 32),
UserDiscoverySetupComp(state: state),
const SizedBox(height: 60), const SizedBox(height: 60),
NextButtonComp( const NextButtonComp(
onPressed: () async { // onPressed: () async {
return !(await state.initializeOrUpdate()); // return !(await widget.state.initializeOrUpdate());
}, // },
), ),
], ],
), ),

View file

@ -14,6 +14,7 @@ import 'package:twonly/src/services/user.service.dart';
import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/utils/misc.dart';
import 'package:twonly/src/utils/storage.dart'; import 'package:twonly/src/utils/storage.dart';
import 'package:twonly/src/visual/components/alert.dialog.dart'; import 'package:twonly/src/visual/components/alert.dialog.dart';
import 'package:twonly/src/visual/views/onboarding/setup.view.dart';
import 'package:twonly/src/visual/views/settings/developer/user_discovery_developer.view.dart'; import 'package:twonly/src/visual/views/settings/developer/user_discovery_developer.view.dart';
class DeveloperSettingsView extends StatefulWidget { class DeveloperSettingsView extends StatefulWidget {
@ -131,6 +132,16 @@ class _DeveloperSettingsViewState extends State<DeveloperSettingsView> {
onTap: () => onTap: () =>
context.push(Routes.settingsDeveloperAutomatedTesting), context.push(Routes.settingsDeveloperAutomatedTesting),
), ),
ListTile(
title: const Text('Reopen Setup'),
onTap: () async {
await UserService.update((u) {
u
..currentSetupPage = SetupPages.profile.name
..isUserDiscoveryEnabled = false;
});
},
),
], ],
); );
}, },

View file

@ -11,15 +11,16 @@ import 'package:twonly/src/visual/views/onboarding/setup/components/setup_switch
const exampleUsers = [ const exampleUsers = [
'james', 'james',
'john',
'robert',
'michael',
'william',
'david',
'mary', 'mary',
'john',
'patricia', 'patricia',
'robert',
'jennifer', 'jennifer',
'michael',
'linda', 'linda',
'william',
'lena',
'david',
]; ];
class UserDiscoverySetupState { class UserDiscoverySetupState {
@ -27,7 +28,6 @@ class UserDiscoverySetupState {
required this.setState, required this.setState,
this.isUserDiscoveryEnabled = true, this.isUserDiscoveryEnabled = true,
this.sharePromotion = true, this.sharePromotion = true,
this.isShareAllContacts = false,
this.isManualApprovalEnabled = false, this.isManualApprovalEnabled = false,
this.threshold = 2, this.threshold = 2,
this.requiredSendImages = 4, this.requiredSendImages = 4,
@ -39,7 +39,6 @@ class UserDiscoverySetupState {
int threshold; int threshold;
bool sharePromotion; bool sharePromotion;
bool isShareAllContacts;
bool isManualApprovalEnabled; bool isManualApprovalEnabled;
int requiredSendImages; int requiredSendImages;
@ -53,11 +52,6 @@ class UserDiscoverySetupState {
} }
Future<bool> initializeOrUpdate() async { Future<bool> initializeOrUpdate() async {
if (isShareAllContacts) {
requiredSendImages = 0;
isManualApprovalEnabled = false;
}
if (isUserDiscoveryEnabled) { if (isUserDiscoveryEnabled) {
await UserDiscoveryService.initializeOrUpdate( await UserDiscoveryService.initializeOrUpdate(
threshold: threshold, threshold: threshold,
@ -76,13 +70,17 @@ class UserDiscoverySetupState {
} }
} }
enum UserDiscoveryPages { all, shareYourFriends, letYourFriendsFindYou }
class UserDiscoverySetupComp extends StatelessWidget { class UserDiscoverySetupComp extends StatelessWidget {
const UserDiscoverySetupComp({ const UserDiscoverySetupComp({
required this.state, required this.state,
this.showOnlySpecificPage = UserDiscoveryPages.all,
super.key, super.key,
}); });
final UserDiscoverySetupState state; final UserDiscoverySetupState state;
final UserDiscoveryPages showOnlySpecificPage;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -90,330 +88,65 @@ class UserDiscoverySetupComp extends StatelessWidget {
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
RichText( if (showOnlySpecificPage == UserDiscoveryPages.all ||
text: TextSpan( showOnlySpecificPage == UserDiscoveryPages.shareYourFriends) ...[
children: formattedText( Text(
context, context.lang.onboardingUserDiscoveryShareFriends,
context.lang.userDiscoveryDisabledIntro, style: Theme.of(context).textTheme.headlineSmall,
),
), ),
textAlign: TextAlign.center, const SizedBox(height: 32),
),
const SizedBox(height: 80), RichText(
text: TextSpan(
Text( children: formattedText(
context.lang.onboardingUserDiscoveryIncreaseTrust, context,
style: const TextStyle( context.lang.onboardingUserDiscoveryShareFriendsDesc,
fontSize: 17,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
RichText(
text: TextSpan(
children: formattedText(
context,
context.lang.onboardingUserDiscoveryShareFriendsDesc,
),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
SetupSwitchCard(
value: state.isUserDiscoveryEnabled,
onChanged: (val) => state.update(() {
state.isUserDiscoveryEnabled = val;
if (!val) {
state.sharePromotion = false;
}
}),
title: context.lang.onboardingUserDiscoveryShareFriends,
expandedChild: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
context.lang.onboardingUserDiscoveryContactsVerifiedBadge,
style: TextStyle(
color: context.color.onSurfaceVariant,
fontSize: 12,
),
textAlign: TextAlign.center,
), ),
const SizedBox(height: 16),
Center(
child: Container(
width: 100,
height: 40,
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 0.5),
borderRadius: BorderRadius.circular(12),
),
child: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
AvatarIcon(fontSize: 12),
SizedBox(width: 5),
Text(
'jane',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
SizedBox(width: 5),
VerificationBadgeComp(
isVerifiedByTransferredTrust: true,
size: 14,
clickable: false,
),
],
),
),
),
const SizedBox(height: 24),
Text(
context.lang.onboardingUserDiscoveryWhoIsRequesting,
style: TextStyle(
color: context.color.onSurfaceVariant,
fontSize: 12,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 3,
),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 0.5),
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const AvatarIcon(fontSize: 14),
const SizedBox(width: 5),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'jane',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 13,
),
),
RichText(
text: TextSpan(
children: buildFriendsListTextString(
context,
[
'mary',
'james',
],
),
style: const TextStyle(fontSize: 10),
),
),
],
),
),
const MockContactRequestActionsComp(),
],
),
),
),
const SizedBox(height: 16),
],
),
),
const SizedBox(height: 80),
Text(
context.lang.userDiscoveryDisabledYouHaveControl,
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
RichText(
text: TextSpan(
children: formattedText(
context,
context.lang.userDiscoveryDisabledDecide,
), ),
textAlign: TextAlign.center,
), ),
textAlign: TextAlign.center,
),
const SizedBox(height: 24), const SizedBox(height: 32),
if (state.isUserDiscoveryEnabled) SetupSwitchCard(
Container( value: state.isUserDiscoveryEnabled,
decoration: BoxDecoration( onChanged: (val) => state.update(() {
color: context.color.surfaceContainerLow, state.isUserDiscoveryEnabled = val;
borderRadius: BorderRadius.circular(12), if (!val) {
), state.sharePromotion = false;
child: Column( }
mainAxisSize: MainAxisSize.min, }),
title: context.lang.onboardingUserDiscoveryShareFriends,
expandedChild: Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
SwitchListTile( SwitchListTile(
value: state.isShareAllContacts, value: state.isManualApprovalEnabled,
onChanged: (val) => state.update(() { onChanged: (val) => state.update(
state.isShareAllContacts = val; () => state.isManualApprovalEnabled = val,
}), ),
title: Text( title: Text(
context.lang.userDiscoverySettingsEnableAllContacts, context.lang.userDiscoverySettingsManualApproval,
style: const TextStyle(fontSize: 13), style: const TextStyle(fontSize: 13),
), ),
subtitle: Text(
context.lang.userDiscoverySettingsManualApprovalDesc,
style: const TextStyle(fontSize: 10),
),
tileColor: context.color.surfaceContainerLow, tileColor: context.color.surfaceContainerLow,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
contentPadding: const EdgeInsets.symmetric(horizontal: 16), contentPadding: const EdgeInsets.symmetric(
), horizontal: 16,
if (!state.isShareAllContacts)
ListTile(
title: Text(
context.lang.userDiscoverySettingsMinImagesTitle,
style: const TextStyle(fontSize: 13),
),
trailing: DropdownButtonHideUnderline(
child: DropdownButton<int>(
value: state.requiredSendImages,
items: List.generate(
9,
(index) {
final value = index + 2;
return DropdownMenuItem<int>(
value: value,
child: Text('$value'),
);
},
),
onChanged: (newValue) {
if (newValue != null) {
state.update(
() => state.requiredSendImages = newValue,
);
}
},
),
),
), ),
if (!state.isShareAllContacts)
SwitchListTile(
value: state.isManualApprovalEnabled,
onChanged: (val) => state.update(
() => state.isManualApprovalEnabled = val,
),
title: Text(
context.lang.userDiscoverySettingsManualApproval,
style: const TextStyle(fontSize: 13),
),
subtitle: Text(
context.lang.userDiscoverySettingsManualApprovalDesc,
style: const TextStyle(fontSize: 10),
),
tileColor: context.color.surfaceContainerLow,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
),
),
],
),
),
const SizedBox(height: 80),
Text(
context.lang.onboardingUserDiscoveryLetFriendsFindYou,
style: const TextStyle(
fontSize: 17,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
RichText(
text: TextSpan(
children: formattedText(
context,
context.lang.onboardingUserDiscoveryLetFriendsFindYouDesc,
),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
SetupSwitchCard(
value: state.sharePromotion,
onChanged: (val) => state.update(() {
if (val) {
state.isUserDiscoveryEnabled = true;
}
state.sharePromotion = val;
}),
title: context.lang.onboardingUserDiscoveryBeRecommended,
expandedChild: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Expanded(
child: Text(
context.lang.userDiscoverySettingsMutualFriends,
style: const TextStyle(fontSize: 12),
),
),
const SizedBox(width: 6),
DropdownButtonHideUnderline(
child: DropdownButton<int>(
value: state.threshold,
items: List.generate(
9,
(index) {
final value = index + 2;
return DropdownMenuItem<int>(
value: value,
child: Text('$value'),
);
},
),
onChanged: (newValue) {
if (newValue != null) {
state.update(() {
state.threshold = newValue;
});
}
},
),
),
],
), ),
const SizedBox(height: 16), const Padding(
padding: EdgeInsets.symmetric(vertical: 8),
child: Divider(),
),
Text( Text(
context.lang.onboardingUserDiscoveryWhatOthersSee, context.lang.onboardingUserDiscoveryContactsVerifiedBadge,
style: TextStyle( style: TextStyle(
color: context.color.onSurfaceVariant, color: context.color.onSurfaceVariant,
fontSize: 12, fontSize: 12,
@ -421,55 +154,38 @@ class UserDiscoverySetupComp extends StatelessWidget {
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Padding( Center(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container( child: Container(
padding: const EdgeInsets.symmetric( width: 100,
horizontal: 6, height: 40,
vertical: 3,
),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 0.5), border: Border.all(color: Colors.grey, width: 0.5),
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
), ),
child: Row( child: const Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const AvatarIcon(fontSize: 14), AvatarIcon(fontSize: 12),
const SizedBox(width: 5), SizedBox(width: 5),
Expanded( Text(
child: Column( 'jane',
crossAxisAlignment: CrossAxisAlignment.start, style: TextStyle(
children: [ fontWeight: FontWeight.bold,
Text(
userService.currentUser.username,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
RichText(
text: TextSpan(
children: buildFriendsListTextString(
context,
exampleUsers.sublist(
0,
state.threshold,
),
),
style: const TextStyle(fontSize: 11),
),
),
],
), ),
), ),
const MockContactSuggestedActionsComp(), SizedBox(width: 5),
VerificationBadgeComp(
isVerifiedByTransferredTrust: true,
size: 14,
clickable: false,
),
], ],
), ),
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 24),
Text( Text(
context.lang.onboardingUserDiscoveryWhatYouSee, context.lang.onboardingUserDiscoveryWhoIsRequesting,
style: TextStyle( style: TextStyle(
color: context.color.onSurfaceVariant, color: context.color.onSurfaceVariant,
fontSize: 12, fontSize: 12,
@ -478,7 +194,7 @@ class UserDiscoverySetupComp extends StatelessWidget {
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.symmetric(horizontal: 24),
child: Container( child: Container(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 6, horizontal: 6,
@ -508,10 +224,10 @@ class UserDiscoverySetupComp extends StatelessWidget {
text: TextSpan( text: TextSpan(
children: buildFriendsListTextString( children: buildFriendsListTextString(
context, context,
exampleUsers.sublist( [
0, 'mary',
state.threshold, 'james',
), ],
), ),
style: const TextStyle(fontSize: 10), style: const TextStyle(fontSize: 10),
), ),
@ -524,10 +240,201 @@ class UserDiscoverySetupComp extends StatelessWidget {
), ),
), ),
), ),
const SizedBox(height: 16),
], ],
), ),
), ),
),
if (showOnlySpecificPage == UserDiscoveryPages.all)
const SizedBox(height: 80),
],
if (showOnlySpecificPage == UserDiscoveryPages.all ||
showOnlySpecificPage ==
UserDiscoveryPages.letYourFriendsFindYou) ...[
Text(
context.lang.onboardingUserDiscoveryLetFriendsFindYou,
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 32),
RichText(
text: TextSpan(
children: formattedText(
context,
context.lang.userDiscoveryDisabledIntro,
),
),
textAlign: TextAlign.center,
),
const SizedBox(height: 32),
SetupSwitchCard(
value: state.sharePromotion,
onChanged: (val) => state.update(() {
if (val) {
state.isUserDiscoveryEnabled = true;
}
state.sharePromotion = val;
}),
title: context.lang.onboardingUserDiscoveryBeRecommended,
expandedChild: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Expanded(
child: Text(
context.lang.userDiscoverySettingsMutualFriends,
style: const TextStyle(fontSize: 12),
),
),
const SizedBox(width: 12),
DropdownButtonHideUnderline(
child: DropdownButton<int>(
value: state.threshold,
items: List.generate(
9,
(index) {
final value = index + 2;
return DropdownMenuItem<int>(
value: value,
child: Text('$value'),
);
},
),
onChanged: (newValue) {
if (newValue != null) {
state.update(() {
state.threshold = newValue;
});
}
},
),
),
],
),
const SizedBox(height: 16),
Text(
context.lang.onboardingUserDiscoveryWhatOthersSee,
style: TextStyle(
color: context.color.onSurfaceVariant,
fontSize: 12,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 3,
),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 0.5),
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const AvatarIcon(fontSize: 14),
const SizedBox(width: 5),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
userService.currentUser.username,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
RichText(
text: TextSpan(
children: buildFriendsListTextString(
context,
exampleUsers.sublist(
0,
state.threshold,
),
),
style: const TextStyle(fontSize: 11),
),
),
],
),
),
const MockContactSuggestedActionsComp(),
],
),
),
),
const SizedBox(height: 16),
Text(
context.lang.onboardingUserDiscoveryWhatYouSee,
style: TextStyle(
color: context.color.onSurfaceVariant,
fontSize: 12,
),
textAlign: TextAlign.center,
),
const SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 3,
),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey, width: 0.5),
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const AvatarIcon(fontSize: 14),
const SizedBox(width: 5),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'jane',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 13,
),
),
RichText(
text: TextSpan(
children: buildFriendsListTextString(
context,
exampleUsers.sublist(
0,
state.threshold,
),
),
style: const TextStyle(fontSize: 10),
),
),
],
),
),
const MockContactRequestActionsComp(),
],
),
),
),
],
),
),
),
],
], ],
), ),
); );

View file

@ -25,8 +25,6 @@ class _UserDiscoverySettingsViewState extends State<UserDiscoverySettingsView> {
requiredSendImages: u.requiredSendImages, requiredSendImages: u.requiredSendImages,
isUserDiscoveryEnabled: u.isUserDiscoveryEnabled, isUserDiscoveryEnabled: u.isUserDiscoveryEnabled,
sharePromotion: u.userDiscoverySharePromotion, sharePromotion: u.userDiscoverySharePromotion,
isShareAllContacts:
u.requiredSendImages == 0 && !u.userDiscoveryRequiresManualApproval,
isManualApprovalEnabled: u.userDiscoveryRequiresManualApproval, isManualApprovalEnabled: u.userDiscoveryRequiresManualApproval,
threshold: u.userDiscoveryThreshold, threshold: u.userDiscoveryThreshold,
); );