Skip to content

Commit b8445b9

Browse files
committed
✨ Add user suggestion page
1 parent d78758e commit b8445b9

15 files changed

+479
-67
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
* New comment will be displayed on top.
1414
* Added link preview in tweet.
1515
* Bug fixed and performance improvements.
16+
* Display suggested users list to follow
1617
## [1.0.6] - 17 Jul 2020
1718

1819
* In user profile page three tabs are added to filter tweet and comment tweet and tweet with media.

lib/main.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:firebase_core/firebase_core.dart';
22
import 'package:flutter/material.dart';
3+
import 'package:flutter_twitter_clone/state/chats/suggestionUserState.dart';
34
import 'package:google_fonts/google_fonts.dart';
45
import 'package:provider/provider.dart';
56

@@ -35,6 +36,8 @@ class MyApp extends StatelessWidget {
3536
ChangeNotifierProvider<SearchState>(create: (_) => SearchState()),
3637
ChangeNotifierProvider<NotificationState>(
3738
create: (_) => NotificationState()),
39+
ChangeNotifierProvider<SuggestionsState>(
40+
create: (_) => SuggestionsState()),
3841
],
3942
child: MaterialApp(
4043
title: 'Fwitter',

lib/model/user.dart

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
class UserModel {
1+
import 'package:equatable/equatable.dart';
2+
3+
// ignore: must_be_immutable
4+
class UserModel extends Equatable {
25
String? key;
36
String? email;
47
String? userId;
@@ -152,4 +155,27 @@ class UserModel {
152155
String get getFollowing {
153156
return '${following ?? 0}';
154157
}
158+
159+
@override
160+
List<Object?> get props => [
161+
key,
162+
email,
163+
userId,
164+
displayName,
165+
userName,
166+
webSite,
167+
profilePic,
168+
bannerImage,
169+
contact,
170+
bio,
171+
location,
172+
dob,
173+
createdAt,
174+
isVerified,
175+
followers,
176+
following,
177+
fcmToken,
178+
followersList,
179+
followingList
180+
];
155181
}

lib/state/appState.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import 'package:flutter/material.dart';
33
class AppState extends ChangeNotifier {
44
bool _isBusy = false;
55
bool get isbusy => _isBusy;
6-
set loading(bool value) {
7-
_isBusy = value;
8-
notifyListeners();
6+
set isBusy(bool value) {
7+
if (value != _isBusy) {
8+
_isBusy = value;
9+
notifyListeners();
10+
}
911
}
1012

1113
int _pageIndex = 0;

lib/state/authState.dart

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,29 @@ class AuthState extends AppState {
7575
Future<String?> signIn(String email, String password,
7676
{required GlobalKey<ScaffoldState> scaffoldKey}) async {
7777
try {
78-
loading = true;
78+
isBusy = true;
7979
var result = await _firebaseAuth.signInWithEmailAndPassword(
8080
email: email, password: password);
8181
user = result.user;
8282
userId = user!.uid;
8383
return user!.uid;
84-
} catch (error) {
85-
loading = false;
84+
} on FirebaseException catch (error) {
85+
if (error.code == 'firebase_auth/user-not-found') {
86+
Utility.customSnackBar(scaffoldKey, 'User not found');
87+
} else {
88+
Utility.customSnackBar(
89+
scaffoldKey,
90+
error.message ?? 'Somethimg went wrong',
91+
);
92+
}
8693
cprint(error, errorIn: 'signIn');
87-
kAnalytics.logLogin(loginMethod: 'email_login');
94+
} catch (error) {
8895
Utility.customSnackBar(scaffoldKey, error.toString());
89-
// logoutCallback();
96+
cprint(error, errorIn: 'signIn');
97+
9098
return null;
99+
} finally {
100+
isBusy = false;
91101
}
92102
}
93103

@@ -164,7 +174,7 @@ class AuthState extends AppState {
164174
{required GlobalKey<ScaffoldState> scaffoldKey,
165175
required String password}) async {
166176
try {
167-
loading = true;
177+
isBusy = true;
168178
var result = await _firebaseAuth.createUserWithEmailAndPassword(
169179
email: userModel.email!,
170180
password: password,
@@ -183,7 +193,7 @@ class AuthState extends AppState {
183193
createUser(_userModel!, newUser: true);
184194
return user!.uid;
185195
} catch (error) {
186-
loading = false;
196+
isBusy = false;
187197
cprint(error, errorIn: 'signUp');
188198
Utility.customSnackBar(scaffoldKey, error.toString());
189199
return null;
@@ -206,26 +216,26 @@ class AuthState extends AppState {
206216

207217
kDatabase.child('profile').child(user.userId!).set(user.toJson());
208218
_userModel = user;
209-
loading = false;
219+
isBusy = false;
210220
}
211221

212222
/// Fetch current user profile
213223
Future<User?> getCurrentUser() async {
214224
try {
215-
loading = true;
225+
isBusy = true;
216226
Utility.logEvent('get_currentUSer', parameter: {});
217227
user = _firebaseAuth.currentUser;
218228
if (user != null) {
229+
await getProfileUser();
219230
authStatus = AuthStatus.LOGGED_IN;
220231
userId = user!.uid;
221-
getProfileUser();
222232
} else {
223233
authStatus = AuthStatus.NOT_LOGGED_IN;
224234
}
225-
loading = false;
235+
isBusy = false;
226236
return user;
227237
} catch (error) {
228-
loading = false;
238+
isBusy = false;
229239
cprint(error, errorIn: 'getCurrentUser');
230240
authStatus = AuthStatus.NOT_LOGGED_IN;
231241
return null;
@@ -360,8 +370,6 @@ class AuthState extends AppState {
360370
/// If `userProfileId` is null then logged in user's profile will fetched
361371
FutureOr<void> getProfileUser({String? userProfileId}) {
362372
try {
363-
loading = true;
364-
365373
userProfileId = userProfileId ?? user!.uid;
366374
kDatabase
367375
.child("profile")
@@ -377,7 +385,7 @@ class AuthState extends AppState {
377385
_userModel!.isVerified = user!.emailVerified;
378386
if (!user!.emailVerified) {
379387
// Check if logged in user verified his email address or not
380-
reloadUser();
388+
// reloadUser();
381389
}
382390
if (_userModel!.fcmToken == null) {
383391
updateFCMToken();
@@ -389,10 +397,10 @@ class AuthState extends AppState {
389397
Utility.logEvent('get_profile', parameter: {});
390398
}
391399
}
392-
loading = false;
400+
isBusy = false;
393401
});
394402
} catch (error) {
395-
loading = false;
403+
isBusy = false;
396404
cprint(error, errorIn: 'getProfileUser');
397405
}
398406
}

lib/state/bookmarkState.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class BookmarkState extends AppState {
3434
if (_tweetList != null) {
3535
return;
3636
}
37-
loading = true;
37+
isBusy = true;
3838
kDatabase
3939
.child('bookmark')
4040
.child(userId)
@@ -62,10 +62,10 @@ class BookmarkState extends AppState {
6262
}
6363
}
6464
}
65-
loading = false;
65+
isBusy = false;
6666
});
6767
} catch (error) {
68-
loading = false;
68+
isBusy = false;
6969
cprint(error, errorIn: 'getDataFromDatabase');
7070
}
7171
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import 'package:flutter_twitter_clone/helper/utility.dart';
2+
import 'package:flutter_twitter_clone/model/user.dart';
3+
import 'package:flutter_twitter_clone/state/appState.dart';
4+
5+
class SuggestionsState extends AppState {
6+
List<UserModel>? _userlist;
7+
8+
UserModel? currentUser;
9+
void initUser(UserModel? model) {
10+
if (model != null && currentUser != model) {
11+
currentUser = model;
12+
_displaySuggestions = int.tryParse(currentUser!.getFollowing)! < 5;
13+
}
14+
}
15+
16+
bool _displaySuggestions = false;
17+
bool get displaySuggestions => _displaySuggestions;
18+
set displaySuggestions(bool value) {
19+
if (value != _displaySuggestions) {
20+
_displaySuggestions = value;
21+
notifyListeners();
22+
}
23+
}
24+
25+
List<UserModel>? get userlist => _userlist;
26+
void setUserlist(List<UserModel>? list) {
27+
if (list != null && _userlist == null) {
28+
list.sort((a, b) {
29+
if (a.followersList != null && b.followersList != null) {
30+
return b.followersList!.length.compareTo(a.followersList!.length);
31+
} else if (a.followersList != null) {
32+
return 0;
33+
}
34+
return 1;
35+
});
36+
37+
_userlist = list.take(20).toList();
38+
_userlist!.removeWhere((element) => isFollowing(element));
39+
_selectedusers = List.from(_userlist!);
40+
}
41+
}
42+
43+
/// Check if user followerlist contain your or not
44+
/// If your id exist in follower list it mean you are following him
45+
bool isFollowing(UserModel user) {
46+
if (user.followersList != null &&
47+
user.followersList!.any((x) => x == currentUser!.userId)) {
48+
return true;
49+
} else {
50+
return false;
51+
}
52+
}
53+
54+
List<UserModel> _selectedusers = [];
55+
int get selectedusersCount => _selectedusers.length;
56+
57+
bool isSelected(UserModel user) {
58+
return _selectedusers.contains(user);
59+
}
60+
61+
void toggleAllSelections() {
62+
if (_selectedusers.length == _userlist!.length) {
63+
_selectedusers = [];
64+
} else {
65+
_selectedusers = List.from(_userlist!);
66+
}
67+
notifyListeners();
68+
}
69+
70+
void toggleUserSelection(UserModel user) {
71+
if (isSelected(user)) {
72+
_selectedusers.remove(user);
73+
} else {
74+
_selectedusers.add(user);
75+
}
76+
77+
notifyListeners();
78+
}
79+
80+
/// get [UserModel list] from firebase realtime Database
81+
void getDataFromDatabase() {
82+
try {} catch (error) {
83+
isBusy = false;
84+
cprint(error, errorIn: 'getDataFromDatabase');
85+
}
86+
}
87+
88+
Future<void> followUsers() async {
89+
try {
90+
if (_selectedusers.length > 0) {
91+
/// Add current user id to the following list of all selected users
92+
for (final user in _selectedusers) {
93+
user.followersList ??= [];
94+
user.followersList!.add(currentUser!.userId!);
95+
user.followers = user.followersList!.length;
96+
kDatabase
97+
.child('profile')
98+
.child(user.userId!)
99+
.child('followerList')
100+
.set(user.followersList);
101+
102+
cprint('user added to following list');
103+
}
104+
105+
/// Add all selected users to current user following list
106+
currentUser!.followingList ??= [];
107+
currentUser!.followingList!
108+
.addAll(_selectedusers.map((e) => e.userId!));
109+
currentUser!.following = currentUser!.followingList!.length;
110+
kDatabase
111+
.child('profile')
112+
.child(currentUser!.userId!)
113+
.child('followingList')
114+
.set(currentUser!.followingList);
115+
116+
displaySuggestions = false;
117+
}
118+
} catch (error) {
119+
isBusy = false;
120+
cprint(error, errorIn: 'followUsers');
121+
}
122+
}
123+
}

lib/state/notificationState.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class NotificationState extends AppState {
6161
if (_notificationList != null) {
6262
return;
6363
}
64-
loading = true;
64+
isBusy = true;
6565
kDatabase
6666
.child('notification')
6767
.child(userId)
@@ -80,10 +80,10 @@ class NotificationState extends AppState {
8080
.sort((x, y) => y.timeStamp!.compareTo(x.timeStamp!));
8181
}
8282
}
83-
loading = false;
83+
isBusy = false;
8484
});
8585
} catch (error) {
86-
loading = false;
86+
isBusy = false;
8787
cprint(error, errorIn: 'getDataFromDatabase');
8888
}
8989
}

lib/state/searchState.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class SearchState extends AppState {
3838
});
3939
_userFilterlist!
4040
.sort((x, y) => y.followers!.compareTo(x.followers!));
41+
notifyListeners();
4142
}
4243
} else {
4344
_userlist = null;

lib/ui/page/Auth/verifyEmail.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class _VerifyEmailPageState extends State<VerifyEmailPage> {
2727
crossAxisAlignment: CrossAxisAlignment.center,
2828
children: state.user!.emailVerified
2929
? <Widget>[
30-
const NotifyText(
30+
NotifyText(
3131
title: 'Your email address is verified',
3232
subTitle:
3333
'You have got your blue tick on your name. Cheers !!',

0 commit comments

Comments
 (0)