kopia lustrzana https://gitlab.com/mysocialportal/relatica
Refactor sign in screen to do more account management functions and remove menus/profile screens
rodzic
6b15b2136d
commit
854c142c20
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:relatica/utils/snackbar_builder.dart';
|
||||
|
||||
import '../routes.dart';
|
||||
import '../services/notifications_manager.dart';
|
||||
|
@ -9,7 +10,7 @@ enum NavBarButtons {
|
|||
timelines,
|
||||
notifications,
|
||||
contacts,
|
||||
menu,
|
||||
search,
|
||||
}
|
||||
|
||||
class AppBottomNavBar extends StatelessWidget {
|
||||
|
@ -44,8 +45,8 @@ class AppBottomNavBar extends StatelessWidget {
|
|||
case NavBarButtons.contacts:
|
||||
context.pushNamed(ScreenPaths.contacts);
|
||||
break;
|
||||
case NavBarButtons.menu:
|
||||
context.pushNamed(ScreenPaths.menu);
|
||||
case NavBarButtons.search:
|
||||
buildSnackbar(context, 'Search screen coming soon...');
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -63,7 +64,7 @@ class AppBottomNavBar extends StatelessWidget {
|
|||
return 1;
|
||||
case NavBarButtons.contacts:
|
||||
return 2;
|
||||
case NavBarButtons.menu:
|
||||
case NavBarButtons.search:
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +83,7 @@ class AppBottomNavBar extends StatelessWidget {
|
|||
}
|
||||
|
||||
if (index == 3) {
|
||||
return NavBarButtons.menu;
|
||||
return NavBarButtons.search;
|
||||
}
|
||||
|
||||
throw ArgumentError('$index has no button type');
|
||||
|
@ -111,9 +112,9 @@ class AppBottomNavBar extends StatelessWidget {
|
|||
activeIcon: Icon(Icons.people_sharp),
|
||||
),
|
||||
const BottomNavigationBarItem(
|
||||
label: 'Menu',
|
||||
icon: Icon(Icons.menu),
|
||||
activeIcon: Icon(Icons.menu_open),
|
||||
label: 'Search',
|
||||
icon: Icon(Icons.search),
|
||||
activeIcon: Icon(Icons.search),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -35,7 +35,12 @@ class StandardAppDrawer extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
Divider(),
|
||||
buildMenuButton(
|
||||
context,
|
||||
'Manage Profiles',
|
||||
() => context.pushNamed(ScreenPaths.manageProfiles),
|
||||
),
|
||||
const Divider(),
|
||||
buildMenuButton(
|
||||
context,
|
||||
'Gallery',
|
||||
|
@ -46,11 +51,6 @@ class StandardAppDrawer extends StatelessWidget {
|
|||
'Direct Messages',
|
||||
() => context.pushNamed(ScreenPaths.messages),
|
||||
),
|
||||
buildMenuButton(
|
||||
context,
|
||||
'Profile',
|
||||
() => context.pushNamed(ScreenPaths.profile),
|
||||
),
|
||||
buildMenuButton(
|
||||
context,
|
||||
'Settings',
|
||||
|
|
|
@ -22,7 +22,7 @@ final useVideoPlayer = kIsWeb || Platform.isAndroid || Platform.isIOS;
|
|||
Future<bool?> showConfirmDialog(BuildContext context, String caption) {
|
||||
return showDialog<bool>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
barrierDismissible: true,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(caption),
|
||||
|
|
|
@ -9,13 +9,11 @@ import 'screens/gallery_browsers_screen.dart';
|
|||
import 'screens/gallery_screen.dart';
|
||||
import 'screens/home.dart';
|
||||
import 'screens/interactions_viewer_screen.dart';
|
||||
import 'screens/menus_screen.dart';
|
||||
import 'screens/message_thread_screen.dart';
|
||||
import 'screens/message_threads_browser_screen.dart';
|
||||
import 'screens/messages_new_thread.dart';
|
||||
import 'screens/notifications_screen.dart';
|
||||
import 'screens/post_screen.dart';
|
||||
import 'screens/profile_screen.dart';
|
||||
import 'screens/settings_screen.dart';
|
||||
import 'screens/sign_in.dart';
|
||||
import 'screens/splash.dart';
|
||||
|
@ -28,15 +26,13 @@ class ScreenPaths {
|
|||
static String connectHandle = '/connect';
|
||||
static String contacts = '/contacts';
|
||||
static String splash = '/splash';
|
||||
static String menu = '/menu';
|
||||
static String settings = '/settings';
|
||||
static String messages = '/messages';
|
||||
static String timelines = '/';
|
||||
static String gallery = '/gallery';
|
||||
static String profile = '/profile';
|
||||
static String notifications = '/notifications';
|
||||
static String signin = '/signin';
|
||||
static String switchProfiles = '/switchProfiles';
|
||||
static String manageProfiles = '/switchProfiles';
|
||||
static String signup = '/signup';
|
||||
static String userProfile = '/user_profile';
|
||||
static String userPosts = '/user_posts';
|
||||
|
@ -75,8 +71,8 @@ final appRouter = GoRouter(
|
|||
builder: (context, state) => SignInScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
path: ScreenPaths.switchProfiles,
|
||||
name: ScreenPaths.switchProfiles,
|
||||
path: ScreenPaths.manageProfiles,
|
||||
name: ScreenPaths.manageProfiles,
|
||||
builder: (context, state) => SignInScreen(),
|
||||
),
|
||||
GoRoute(
|
||||
|
@ -101,20 +97,6 @@ final appRouter = GoRouter(
|
|||
child: HomeScreen(),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: ScreenPaths.profile,
|
||||
name: ScreenPaths.profile,
|
||||
pageBuilder: (context, state) => NoTransitionPage(
|
||||
child: ProfileScreen(),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: ScreenPaths.menu,
|
||||
name: ScreenPaths.menu,
|
||||
pageBuilder: (context, state) => NoTransitionPage(
|
||||
child: MenusScreen(),
|
||||
),
|
||||
),
|
||||
GoRoute(
|
||||
path: ScreenPaths.messages,
|
||||
name: ScreenPaths.messages,
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../controls/app_bottom_nav_bar.dart';
|
||||
import '../globals.dart';
|
||||
import '../routes.dart';
|
||||
|
||||
class MenusScreen extends StatelessWidget {
|
||||
static const menuButtonWidth = 350.0;
|
||||
static const menuButtonHeight = 125.0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final menuItems = [
|
||||
buildMenuButton('Gallery', () => context.pushNamed(ScreenPaths.gallery)),
|
||||
buildMenuButton(
|
||||
'Direct Messages', () => context.pushNamed(ScreenPaths.messages)),
|
||||
buildMenuButton('Profile', () => context.pushNamed(ScreenPaths.profile)),
|
||||
buildMenuButton(
|
||||
'Settings', () => context.pushNamed(ScreenPaths.settings)),
|
||||
buildMenuButton('Clear Caches', () async {
|
||||
final confirm = await showYesNoDialog(
|
||||
context, 'You want to clear all memory and disk cache data?');
|
||||
if (confirm == true) {
|
||||
clearCaches();
|
||||
}
|
||||
}),
|
||||
];
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: GridView(
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
mainAxisExtent: menuButtonHeight,
|
||||
maxCrossAxisExtent: menuButtonWidth,
|
||||
),
|
||||
children: menuItems,
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: const AppBottomNavBar(
|
||||
currentButton: NavBarButtons.menu,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildMenuButton(String title, Function() onPressed) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ElevatedButton(
|
||||
onPressed: onPressed,
|
||||
child: Text(title),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:relatica/controls/padding.dart';
|
||||
import 'package:relatica/routes.dart';
|
||||
|
||||
import '../controls/standard_appbar.dart';
|
||||
import '../globals.dart';
|
||||
import '../services/auth_service.dart';
|
||||
|
||||
class ProfileScreen extends StatefulWidget {
|
||||
const ProfileScreen({super.key});
|
||||
|
||||
@override
|
||||
State<ProfileScreen> createState() => _ProfileScreenState();
|
||||
}
|
||||
|
||||
class _ProfileScreenState extends State<ProfileScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final authService = context.watch<AccountsService>();
|
||||
if (!authService.loggedIn) {
|
||||
return Scaffold(
|
||||
appBar: StandardAppBar.build(context, 'Not Logged In'),
|
||||
body: Center(
|
||||
child: Text('Not logged in'),
|
||||
));
|
||||
}
|
||||
final profile = authService.currentProfile;
|
||||
return Scaffold(
|
||||
appBar: StandardAppBar.build(context, 'Profile'),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('Profile: ${profile.handle}'),
|
||||
const VerticalPadding(),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
final confirm =
|
||||
await showYesNoDialog(context, 'Log out account?');
|
||||
if (confirm == true) {
|
||||
await getIt<AccountsService>().signOut(profile);
|
||||
}
|
||||
},
|
||||
child: const Text('Logout')),
|
||||
const VerticalPadding(),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
context.pushNamed(ScreenPaths.switchProfiles);
|
||||
},
|
||||
child: const Text('Switch Profile'))
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import '../controls/padding.dart';
|
|||
import '../globals.dart';
|
||||
import '../models/auth/credentials_intf.dart';
|
||||
import '../models/auth/oauth_credentials.dart';
|
||||
import '../models/auth/profile.dart';
|
||||
import '../services/auth_service.dart';
|
||||
import '../utils/snackbar_builder.dart';
|
||||
|
||||
|
@ -29,16 +30,30 @@ class _SignInScreenState extends State<SignInScreen> {
|
|||
var authType = oauthType;
|
||||
var hidePassword = true;
|
||||
var showUsernameAndPasswordFields = false;
|
||||
var signInButtonEnabled = false;
|
||||
var existingAccount = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
final service = getIt<AccountsService>();
|
||||
if (service.loggedIn) {
|
||||
setCredentials(null, service.currentProfile.credentials);
|
||||
setCredentials(null, service.currentProfile);
|
||||
} else {
|
||||
newProfile();
|
||||
}
|
||||
}
|
||||
|
||||
void newProfile() {
|
||||
usernameController.text = '';
|
||||
passwordController.text = '';
|
||||
serverNameController.text = '';
|
||||
showUsernameAndPasswordFields = false;
|
||||
authType = oauthType;
|
||||
signInButtonEnabled = true;
|
||||
existingAccount = false;
|
||||
}
|
||||
|
||||
void setBasicCredentials(BasicCredentials credentials) {
|
||||
usernameController.text = credentials.username;
|
||||
passwordController.text = credentials.password;
|
||||
|
@ -53,7 +68,10 @@ class _SignInScreenState extends State<SignInScreen> {
|
|||
authType = oauthType;
|
||||
}
|
||||
|
||||
void setCredentials(BuildContext? context, ICredentials credentials) {
|
||||
void setCredentials(BuildContext? context, Profile profile) {
|
||||
final ICredentials credentials = profile.credentials;
|
||||
existingAccount = true;
|
||||
signInButtonEnabled = !profile.loggedIn;
|
||||
if (credentials is BasicCredentials) {
|
||||
setBasicCredentials(credentials);
|
||||
return;
|
||||
|
@ -86,32 +104,39 @@ class _SignInScreenState extends State<SignInScreen> {
|
|||
child: Form(
|
||||
key: formKey,
|
||||
child: Center(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
child: ListView(
|
||||
children: [
|
||||
DropdownButton<String>(
|
||||
value: authType,
|
||||
items: authTypes
|
||||
.map((a) => DropdownMenuItem(value: a, child: Text(a)))
|
||||
.toList(),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
authType = value ?? '';
|
||||
switch (value) {
|
||||
case usernamePasswordType:
|
||||
showUsernameAndPasswordFields = true;
|
||||
break;
|
||||
case oauthType:
|
||||
showUsernameAndPasswordFields = false;
|
||||
break;
|
||||
default:
|
||||
print("Don't know this");
|
||||
Center(
|
||||
child: DropdownButton<String>(
|
||||
value: authType,
|
||||
items: authTypes
|
||||
.map(
|
||||
(a) => DropdownMenuItem(value: a, child: Text(a)))
|
||||
.toList(),
|
||||
onChanged: (value) {
|
||||
if (existingAccount) {
|
||||
buildSnackbar(context,
|
||||
"Can't change the type on an existing account");
|
||||
return;
|
||||
}
|
||||
});
|
||||
}),
|
||||
setState(() {
|
||||
authType = value ?? '';
|
||||
switch (value) {
|
||||
case usernamePasswordType:
|
||||
showUsernameAndPasswordFields = true;
|
||||
break;
|
||||
case oauthType:
|
||||
showUsernameAndPasswordFields = false;
|
||||
break;
|
||||
default:
|
||||
print("Don't know this");
|
||||
}
|
||||
});
|
||||
}),
|
||||
),
|
||||
const VerticalPadding(),
|
||||
TextFormField(
|
||||
readOnly: existingAccount,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
controller: serverNameController,
|
||||
validator: (value) =>
|
||||
|
@ -130,6 +155,7 @@ class _SignInScreenState extends State<SignInScreen> {
|
|||
const VerticalPadding(),
|
||||
if (showUsernameAndPasswordFields) ...[
|
||||
TextFormField(
|
||||
readOnly: existingAccount,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
controller: usernameController,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
|
@ -162,6 +188,7 @@ class _SignInScreenState extends State<SignInScreen> {
|
|||
),
|
||||
const VerticalPadding(),
|
||||
TextFormField(
|
||||
readOnly: existingAccount,
|
||||
obscureText: hidePassword,
|
||||
controller: passwordController,
|
||||
decoration: InputDecoration(
|
||||
|
@ -188,77 +215,129 @@ class _SignInScreenState extends State<SignInScreen> {
|
|||
),
|
||||
const VerticalPadding(),
|
||||
],
|
||||
ElevatedButton(
|
||||
onPressed: () => _signIn(context),
|
||||
child: const Text('Signin'),
|
||||
),
|
||||
const VerticalPadding(),
|
||||
const Text('Logged out:'),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: ListView.separated(
|
||||
itemBuilder: (context, index) {
|
||||
final p = loggedOutProfiles[index];
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
setCredentials(context, p.credentials);
|
||||
setState(() {});
|
||||
signInButtonEnabled
|
||||
? ElevatedButton(
|
||||
onPressed: () => _signIn(context),
|
||||
child: const Text('Signin'),
|
||||
)
|
||||
: ElevatedButton(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
newProfile();
|
||||
});
|
||||
},
|
||||
title: Text(p.handle),
|
||||
subtitle: Text(p.credentials is BasicCredentials
|
||||
? 'Username/Password'
|
||||
: 'OAuth Login'),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, __) => const Divider(),
|
||||
itemCount: loggedOutProfiles.length,
|
||||
),
|
||||
),
|
||||
child: const Text('New'),
|
||||
),
|
||||
const VerticalPadding(),
|
||||
const Text('Logged in:'),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: ListView.separated(
|
||||
itemBuilder: (context, index) {
|
||||
final p = loggedInProfiles[index];
|
||||
final active = service.loggedIn
|
||||
? p.id == service.currentProfile.id
|
||||
: false;
|
||||
return ListTile(
|
||||
onTap: () async {
|
||||
setCredentials(context, p.credentials);
|
||||
setState(() {});
|
||||
await service.setActiveProfile(p);
|
||||
Text(
|
||||
'Logged out:',
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
loggedOutProfiles.isEmpty
|
||||
? const Text(
|
||||
'No logged out profiles',
|
||||
textAlign: TextAlign.center,
|
||||
)
|
||||
: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
width: 0.5,
|
||||
)),
|
||||
child: Column(
|
||||
children: loggedOutProfiles.map((p) {
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
setCredentials(context, p);
|
||||
setState(() {});
|
||||
},
|
||||
title: Text(p.handle),
|
||||
subtitle: Text(p.credentials is BasicCredentials
|
||||
? 'Username/Password'
|
||||
: 'OAuth Login'),
|
||||
trailing: ElevatedButton(
|
||||
onPressed: () async {
|
||||
final confirm = await showYesNoDialog(context,
|
||||
'Remove login information from app?');
|
||||
if (confirm ?? false) {
|
||||
await service.removeProfile(p);
|
||||
}
|
||||
},
|
||||
child: const Text('Remove'),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
const VerticalPadding(),
|
||||
Text(
|
||||
'Logged in:',
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
loggedInProfiles.isEmpty
|
||||
? const Text(
|
||||
'No logged in profiles',
|
||||
textAlign: TextAlign.center,
|
||||
)
|
||||
: Container(
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
width: 0.5,
|
||||
)),
|
||||
child: Column(
|
||||
children: loggedInProfiles.map((p) {
|
||||
final active = service.loggedIn
|
||||
? p.id == service.currentProfile.id
|
||||
: false;
|
||||
return ListTile(
|
||||
onTap: () async {
|
||||
setCredentials(context, p);
|
||||
setState(() {});
|
||||
|
||||
if (mounted) {
|
||||
clearCaches();
|
||||
context.goNamed(ScreenPaths.timelines);
|
||||
}
|
||||
},
|
||||
title: Text(
|
||||
p.handle,
|
||||
style: active
|
||||
? const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.italic)
|
||||
: null,
|
||||
final confirm = await showYesNoDialog(
|
||||
context, 'Switch to account?');
|
||||
|
||||
if (confirm ?? false) {
|
||||
service.setActiveProfile(p);
|
||||
clearCaches();
|
||||
if (mounted) {
|
||||
context.goNamed(ScreenPaths.timelines);
|
||||
}
|
||||
}
|
||||
},
|
||||
title: Text(
|
||||
p.handle,
|
||||
style: active
|
||||
? const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.italic)
|
||||
: null,
|
||||
),
|
||||
subtitle: Text(
|
||||
p.credentials is BasicCredentials
|
||||
? 'Username/Password'
|
||||
: 'OAuth Login',
|
||||
style: active
|
||||
? const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.italic)
|
||||
: null,
|
||||
),
|
||||
trailing: ElevatedButton(
|
||||
onPressed: () async {
|
||||
final confirm = await showYesNoDialog(
|
||||
context, 'Log out account?');
|
||||
if (confirm == true) {
|
||||
await getIt<AccountsService>().signOut(p);
|
||||
}
|
||||
},
|
||||
child: const Text('Sign out'),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
subtitle: Text(
|
||||
p.credentials is BasicCredentials
|
||||
? 'Username/Password'
|
||||
: 'OAuth Login',
|
||||
style: active
|
||||
? const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontStyle: FontStyle.italic)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (_, __) => const Divider(),
|
||||
itemCount: loggedInProfiles.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
@ -295,8 +374,12 @@ class _SignInScreenState extends State<SignInScreen> {
|
|||
}
|
||||
if (result.isFailure) {
|
||||
buildSnackbar(context, 'Error signing in: ${result.error}');
|
||||
return;
|
||||
}
|
||||
await getIt<AccountsService>().setActiveProfile(result.value);
|
||||
if (mounted) {
|
||||
context.goNamed(ScreenPaths.timelines);
|
||||
}
|
||||
context.goNamed(ScreenPaths.timelines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue