1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-12-26 04:48:51 +00:00

Display the list of friends of the user

This commit is contained in:
Pierre HUBERT 2019-05-01 17:52:41 +02:00
parent 3109f7c482
commit 5021ded039
9 changed files with 343 additions and 2 deletions

View File

@ -0,0 +1,38 @@
import 'package:comunic/lists/friends_list.dart';
import 'package:comunic/models/api_request.dart';
import 'package:comunic/models/friend.dart';
/// Friends helper
///
/// @author Pierre HUBERT
class FriendsHelper {
/// Get the list of friends of the user
///
/// Returns the list of friends in case of success, or null if an error
/// occurred
Future<FriendsList> downloadList() async {
final response = await APIRequest(
uri: "friends/getList",
needLogin: true,
args: {
"complete": "true",
},
).exec();
if (response.code != 200) return null;
// Parse and return the list of friends
FriendsList list = FriendsList();
response.getArray().forEach((f) =>
list.add(Friend(
id: f["ID_friend"],
accepted: f["accepted"] == 1,
lastActive: f["time_last_activity"],
following: f["following"] == 1,
canPostTexts: f["canPostTexts"],),),
);
return list;
}
}

View File

@ -0,0 +1,24 @@
import 'dart:collection';
import 'package:comunic/models/friend.dart';
/// List of friends of the user
///
/// @author Pierre HUBERT
class FriendsList extends ListBase<Friend> {
List<Friend> _list = List();
int get length => _list.length;
set length(int length) => _list.length = length;
@override
Friend operator [](int index) => _list[index];
@override
void operator []=(int index, Friend value) => _list[index] = value;
/// Get the ID of all the friends of the current user
List<int> get usersId => map((f) => f.id).toList();
}

29
lib/models/friend.dart Normal file
View File

@ -0,0 +1,29 @@
import 'package:comunic/utils/date_utils.dart';
import 'package:meta/meta.dart';
/// Single user Friend information
///
/// @author Pierre HUBERT
class Friend {
final int id;
final bool accepted;
final int lastActive;
final bool following;
final bool canPostTexts;
Friend({
@required this.id,
@required this.accepted,
@required this.lastActive,
@required this.following,
@required this.canPostTexts,
}) : assert(id != null),
assert(accepted != null),
assert(lastActive != null),
assert(following != null),
assert(canPostTexts != null);
/// Check out whether friend is connected or not
bool get isConnected => time() - 30 < lastActive;
}

View File

@ -31,6 +31,9 @@ class User extends CacheModel {
/// Get user full name
String get fullName => firstName + " " + lastName;
/// Get user display name
String get displayName => fullName; //TODO : support HTML characters (eg: &Eacute; => é)
Map<String, dynamic> toMap() {
return {
UserTableContract.C_ID: id,

View File

@ -1,6 +1,8 @@
import 'package:comunic/ui/screens/conversations_list_screen.dart';
import 'package:comunic/ui/screens/friends_list_screen.dart';
import 'package:comunic/ui/screens/menus_screen.dart';
import 'package:comunic/ui/tiles/custom_bottom_navigation_bar_item.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:flutter/material.dart';
/// Main route of the application
@ -52,6 +54,9 @@ class _HomeRouteState extends State<HomeRoute> {
return ConversationsListScreen();
case 1:
return FriendsListScreen();
case 2:
return MenuScreen();
default:
@ -64,11 +69,15 @@ class _HomeRouteState extends State<HomeRoute> {
return <BottomNavigationBarItem>[
CustomNavigationBarItem(
icon: Icon(Icons.chat),
title: Text("Conversations"),
title: Text(tr("Conversations")),
),
CustomNavigationBarItem(
icon: Icon(Icons.group),
title: Text(tr("Friends")),
),
CustomNavigationBarItem(
icon: Icon(Icons.menu),
title: Text("Menu"),
title: Text(tr("Menu")),
),
];
}

View File

@ -0,0 +1,122 @@
import 'package:comunic/helpers/friends_helper.dart';
import 'package:comunic/helpers/users_helper.dart';
import 'package:comunic/lists/friends_list.dart';
import 'package:comunic/lists/users_list.dart';
import 'package:comunic/ui/tiles/accepted_friend_tile.dart';
import 'package:comunic/ui/tiles/pending_friend_tile.dart';
import 'package:comunic/ui/widgets/safe_state.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/ui_utils.dart';
import 'package:flutter/material.dart';
/// Friends list screen
///
/// Display the list of friends of the current user
///
/// @author Pierre HUBERT
enum _ErrorsLevel { NONE, MINOR, MAJOR }
class FriendsListScreen extends StatefulWidget {
@override
State<StatefulWidget> createState() => _FriendsListScreenState();
}
class _FriendsListScreenState extends SafeState<FriendsListScreen> {
/// Helpers
final _friendsHelper = FriendsHelper();
final _usersHelper = UsersHelper();
/// Widget members
_ErrorsLevel _error = _ErrorsLevel.NONE;
FriendsList _friendsList;
UsersList _usersInfo;
bool _loading = true;
/// Useful setters
set error(_ErrorsLevel err) => setState(() => _error = err);
set loading(bool loading) => setState(() => _loading = loading);
void _gotError() =>
error = _friendsList == null ? _ErrorsLevel.MAJOR : _ErrorsLevel.MINOR;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_loadList();
}
/// Load the list of friends
Future<void> _loadList() async {
error = _ErrorsLevel.NONE;
loading = true;
// Get the list of friends
final list = await _friendsHelper.downloadList();
// Check for errors
if (list == null) return _gotError();
// Get information about related users
final users = await _usersHelper.getUsersInfo(list.usersId);
// Check for errors
if (users == null) return _gotError();
// Apply new information
setState(() {
_friendsList = list;
_usersInfo = users;
});
loading = false;
error = _ErrorsLevel.NONE;
}
/// Build and return loading error
Widget _buildError() =>
buildErrorCard(
tr("Could not load your list of friends!"),
actions: [
FlatButton(
onPressed: _loadList,
child: Text(
tr("Retry").toUpperCase(),
style: TextStyle(color: Colors.white),
),
),
],
);
@override
Widget build(BuildContext context) {
if (_error == _ErrorsLevel.MAJOR) return _buildError();
if (_friendsList == null) return buildCenteredProgressBar();
return Column(
children: <Widget>[
// Check for errors
Container(child: _error != _ErrorsLevel.NONE ? _buildError() : null),
// Check if loading
Container(child: _loading ? CircularProgressIndicator() : null),
// List of friends
Expanded(
child: ListView.builder(itemCount: _friendsList.length, itemBuilder: (c, i) =>
_friendsList[i].accepted ? AcceptedFriendTile(
friend: _friendsList[i],
user: _usersInfo.getUser(_friendsList[i].id),
) : PendingFriendTile(
friend: _friendsList[i],
user: _usersInfo.getUser(_friendsList[i].id),
onRespond: (friend, accept){},
)),
),
],
);
}
}

View File

@ -0,0 +1,39 @@
import 'package:comunic/models/friend.dart';
import 'package:comunic/models/user.dart';
import 'package:comunic/ui/widgets/account_image_widget.dart';
import 'package:comunic/utils/date_utils.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:flutter/material.dart';
/// Accepted friend tile
///
/// @author Pierre HUBERT
class AcceptedFriendTile extends StatelessWidget {
final Friend friend;
final User user;
const AcceptedFriendTile(
{Key key, @required this.friend, @required this.user})
: assert(friend != null),
assert(user != null),
super(key: key);
@override
Widget build(BuildContext context) {
return ListTile(
leading: AccountImageWidget(user: user),
title: Text(user.displayName),
subtitle: friend.isConnected
? Text(
tr(
"Online",
),
style: TextStyle(color: Colors.green),
)
: Text(
diffTimeFromNowToStr(friend.lastActive),
),
);
}
}

View File

@ -0,0 +1,64 @@
import 'package:comunic/models/friend.dart';
import 'package:comunic/models/user.dart';
import 'package:comunic/ui/widgets/account_image_widget.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:flutter/material.dart';
/// Pending friend tile
///
/// @author Pierre HUBERT
typedef RespondFriendshipRequestCallback = void Function(Friend, bool);
class PendingFriendTile extends StatelessWidget {
final Friend friend;
final User user;
final RespondFriendshipRequestCallback onRespond;
const PendingFriendTile(
{Key key,
@required this.friend,
@required this.user,
@required this.onRespond})
: assert(friend != null),
assert(user != null),
assert(onRespond != null),
super(key: key);
@override
Widget build(BuildContext context) {
return ListTile(
leading: AccountImageWidget(
user: user,
),
isThreeLine: true,
title: Text(user.fullName),
subtitle: Container(
height: 30.0,
margin: EdgeInsets.only(top: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FlatButton(
child: Text(
tr("Accept").toUpperCase(),
style: TextStyle(color: Colors.white),
),
color: Colors.green,
onPressed: () => onRespond(friend, true),
),
Container(width: 8.0,),
FlatButton(
child: Text(
tr("Reject").toUpperCase(),
style: TextStyle(color: Colors.white),
),
color: Colors.red,
onPressed: () => onRespond(friend, false),
)
],
),
),
);
}
}

View File

@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
/// Little State hack to avoid issues
///
/// @author Pierre HUBERT
abstract class SafeState<T extends StatefulWidget> extends State<T> {
@override
void setState(fn) {
if(mounted)
super.setState(fn);
}
}