1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-11-22 21:09:21 +00:00

Can update a conversation

This commit is contained in:
Pierre HUBERT 2019-05-01 09:24:50 +02:00
parent 92054d6e29
commit e7c34e6e71
7 changed files with 212 additions and 57 deletions

View File

@ -8,7 +8,6 @@ import 'package:comunic/models/api_request.dart';
import 'package:comunic/models/api_response.dart'; import 'package:comunic/models/api_response.dart';
import 'package:comunic/models/conversation.dart'; import 'package:comunic/models/conversation.dart';
import 'package:comunic/models/conversation_message.dart'; import 'package:comunic/models/conversation_message.dart';
import 'package:comunic/models/conversation_settings.dart';
import 'package:comunic/models/new_conversation_message.dart'; import 'package:comunic/models/new_conversation_message.dart';
import 'package:comunic/utils/account_utils.dart'; import 'package:comunic/utils/account_utils.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
@ -28,7 +27,7 @@ class ConversationsHelper {
/// Create a new conversation /// Create a new conversation
/// ///
/// Return the ID of the newly created conversation or -1 in case of failure /// Return the ID of the newly created conversation or -1 in case of failure
Future<int> createConversation(ConversationSettings settings) async { Future<int> createConversation(Conversation settings) async {
final response = final response =
await APIRequest(uri: "conversations/create", needLogin: true, args: { await APIRequest(uri: "conversations/create", needLogin: true, args: {
"name": settings.hasName ? settings.name : "false", "name": settings.hasName ? settings.name : "false",
@ -41,6 +40,27 @@ class ConversationsHelper {
return response.getObject()["conversationID"]; return response.getObject()["conversationID"];
} }
/// Update an existing conversation
///
/// Returns a boolean depending of the success of the operation
Future<bool> updateConversation(Conversation settings) async {
final request =
APIRequest(uri: "conversations/updateSettings", needLogin: true, args: {
"conversationID": settings.id.toString(),
"following": settings.following ? "true" : "false"
});
// Update all conversation settings, if possible
if(settings.isOwner) {
request.addString("name", settings.hasName ? settings.name : "false");
request.addString("members", settings.members.join(","));
}
final response = await request.exec();
return response.code == 200;
}
/// Download the list of conversations from the server /// Download the list of conversations from the server
Future<ConversationsList> downloadList() async { Future<ConversationsList> downloadList() async {
final response = final response =

View File

@ -1,5 +1,6 @@
import 'package:comunic/helpers/database/database_contract.dart'; import 'package:comunic/helpers/database/database_contract.dart';
import 'package:comunic/models/cache_model.dart'; import 'package:comunic/models/cache_model.dart';
import 'package:comunic/utils/account_utils.dart';
import 'package:comunic/utils/list_utils.dart'; import 'package:comunic/utils/list_utils.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
@ -34,6 +35,10 @@ class Conversation extends CacheModel implements Comparable {
/// Check out whether a conversation has a fixed name or not /// Check out whether a conversation has a fixed name or not
bool get hasName => this.name != null; bool get hasName => this.name != null;
/// Check out whether current user of the application is the owner of it or
/// not
bool get isOwner => this.ownerID == userID();
Conversation.fromMap(Map<String, dynamic> map) Conversation.fromMap(Map<String, dynamic> map)
: ownerID = map[ConversationTableContract.C_OWNER_ID], : ownerID = map[ConversationTableContract.C_OWNER_ID],
lastActive = map[ConversationTableContract.C_LAST_ACTIVE], lastActive = map[ConversationTableContract.C_LAST_ACTIVE],

View File

@ -1,27 +0,0 @@
import 'package:meta/meta.dart';
/// Conversation settings model
///
/// Use this model to create / update a conversation
///
/// @author Pierre HUBERT
class ConversationSettings {
/// Set the ID to 0 if not required
final int id;
final String name;
final bool following;
final List<int> members;
ConversationSettings({
@required this.id,
@required this.name,
@required this.following,
@required this.members,
}) : assert(members != null && members.length > 0),
assert(following != null);
bool get hasName => name != null && name.length > 0;
bool get hasId => id != null && id > 0;
}

View File

@ -1,5 +1,6 @@
import 'package:comunic/helpers/conversations_helper.dart'; import 'package:comunic/helpers/conversations_helper.dart';
import 'package:comunic/models/conversation.dart'; import 'package:comunic/models/conversation.dart';
import 'package:comunic/ui/routes/update_conversation_route.dart';
import 'package:comunic/ui/screens/conversation_screen.dart'; import 'package:comunic/ui/screens/conversation_screen.dart';
import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/ui_utils.dart'; import 'package:comunic/utils/ui_utils.dart';
@ -53,6 +54,14 @@ class _ConversationRouteState extends State<ConversationRoute> {
}); });
} }
void _openSettings() {
Navigator.of(context).push(MaterialPageRoute(builder: (b) {
return UpdateConversationRoute(
conversationID: widget.conversationID,
);
}));
}
Widget _buildRouteBody() { Widget _buildRouteBody() {
//Handle errors //Handle errors
if (_error != null && _error) if (_error != null && _error)
@ -86,6 +95,12 @@ class _ConversationRouteState extends State<ConversationRoute> {
title: Text( title: Text(
_conversationName == null ? tr("Loading") : _conversationName, _conversationName == null ? tr("Loading") : _conversationName,
), ),
actions: <Widget>[
IconButton(
icon: Icon(Icons.settings),
onPressed: _openSettings,
)
],
), ),
body: _buildRouteBody(), body: _buildRouteBody(),
); );

View File

@ -0,0 +1,91 @@
import 'package:comunic/helpers/conversations_helper.dart';
import 'package:comunic/helpers/users_helper.dart';
import 'package:comunic/lists/users_list.dart';
import 'package:comunic/models/conversation.dart';
import 'package:comunic/ui/screens/update_conversation_screen.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/ui_utils.dart';
import 'package:flutter/material.dart';
/// Update a conversation route
///
/// @author Pierre HUBERT
class UpdateConversationRoute extends StatefulWidget {
final int conversationID;
const UpdateConversationRoute({Key key, this.conversationID})
: assert(conversationID != null),
super(key: key);
@override
State<StatefulWidget> createState() => _UpdateConversationRoute();
}
class _UpdateConversationRoute extends State<UpdateConversationRoute> {
Conversation _conversation;
UsersList _membersInfo;
bool _error = false;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_loadConversation();
}
void setError(bool e) => setState(() {
_error = e;
});
/// Load information about the being updated conversation
Future<void> _loadConversation() async {
setError(false);
final conversation = await ConversationsHelper()
.getSingle(widget.conversationID, force: true);
if (conversation == null) return setError(true);
//Load information about the members of the conversation
_membersInfo = await UsersHelper().getUsersInfo(conversation.members);
if (_membersInfo == null) return setError(true);
setState(() {
_conversation = conversation;
});
}
/// Build the body of this widget
Widget _buildBody() {
if (_error)
return buildErrorCard(
tr("Could not load information about the conversation"),
actions: [
FlatButton(
onPressed: _loadConversation,
child: Text(
tr("Retry").toUpperCase(),
style: TextStyle(color: Colors.white),
),
)
]);
if (_conversation == null) return buildLoadingPage();
return UpdateConversationScreen(
initialUsers: _membersInfo,
initialSettings: _conversation,
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(tr("Update a conversation")),
),
body: _buildBody(),
);
}
}

View File

@ -1,6 +1,6 @@
import 'package:comunic/helpers/conversations_helper.dart'; import 'package:comunic/helpers/conversations_helper.dart';
import 'package:comunic/lists/users_list.dart'; import 'package:comunic/lists/users_list.dart';
import 'package:comunic/models/conversation_settings.dart'; import 'package:comunic/models/conversation.dart';
import 'package:comunic/ui/routes/conversation_route.dart'; import 'package:comunic/ui/routes/conversation_route.dart';
import 'package:comunic/ui/tiles/simple_user_tile.dart'; import 'package:comunic/ui/tiles/simple_user_tile.dart';
import 'package:comunic/ui/widgets/pick_user_widget.dart'; import 'package:comunic/ui/widgets/pick_user_widget.dart';
@ -14,6 +14,15 @@ import 'package:flutter/material.dart';
enum _MembersMenuChoices { REMOVE } enum _MembersMenuChoices { REMOVE }
class UpdateConversationScreen extends StatefulWidget { class UpdateConversationScreen extends StatefulWidget {
final Conversation initialSettings;
final UsersList initialUsers;
const UpdateConversationScreen({
Key key,
this.initialSettings,
this.initialUsers,
}) : super(key: key);
@override @override
State<StatefulWidget> createState() => _UpdateConversationScreen(); State<StatefulWidget> createState() => _UpdateConversationScreen();
} }
@ -23,6 +32,22 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
UsersList _members = UsersList(); UsersList _members = UsersList();
bool _followConversation = true; bool _followConversation = true;
get isUpdating => widget.initialSettings != null;
get isOwner => !isUpdating || widget.initialSettings.isOwner;
@override
void initState() {
super.initState();
// Check if we are updating an existing conversation
if (widget.initialSettings != null) {
_nameController.text = widget.initialSettings.name;
_members = widget.initialUsers;
_followConversation = widget.initialSettings.following;
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
@ -33,8 +58,10 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
TextField( TextField(
controller: _nameController, controller: _nameController,
decoration: InputDecoration( decoration: InputDecoration(
labelText: tr("Conversation name (optionnal)"), labelText: tr("Conversation name (optionnal)"),
alignLabelWithHint: true), alignLabelWithHint: true,
enabled: isOwner,
),
), ),
// Add a member to the conversation // Add a member to the conversation
@ -42,6 +69,7 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
resetOnChoose: true, resetOnChoose: true,
keepFocusOnChoose: true, keepFocusOnChoose: true,
label: tr("Add member"), label: tr("Add member"),
enabled: isOwner,
onSelectUser: (user) => setState(() { onSelectUser: (user) => setState(() {
if (!_members.contains(user)) _members.add(user); if (!_members.contains(user)) _members.add(user);
}), }),
@ -57,17 +85,19 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
itemBuilder: (b, i) { itemBuilder: (b, i) {
return SimpleUserTile( return SimpleUserTile(
user: _members[i], user: _members[i],
trailing: PopupMenuButton<_MembersMenuChoices>( trailing: isOwner
onSelected: (choice) => ? PopupMenuButton<_MembersMenuChoices>(
_membersMenuItemSelected(i, choice), onSelected: (choice) =>
itemBuilder: (c) => _membersMenuItemSelected(i, choice),
<PopupMenuEntry<_MembersMenuChoices>>[ itemBuilder: (c) =>
PopupMenuItem( <PopupMenuEntry<_MembersMenuChoices>>[
child: Text(tr("Remove")), PopupMenuItem(
value: _MembersMenuChoices.REMOVE, child: Text(tr("Remove")),
) value: _MembersMenuChoices.REMOVE,
], )
), ],
)
: null,
); );
}, },
), ),
@ -90,7 +120,9 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
// Submit button // Submit button
RaisedButton( RaisedButton(
onPressed: _members.length < 1 ? null : _submitForm, onPressed: _members.length < 1 ? null : _submitForm,
child: Text(tr("Create the conversation")), child: Text(isUpdating
? tr("Update the conversation")
: tr("Create the conversation")),
) )
], ],
), ),
@ -109,24 +141,40 @@ class _UpdateConversationScreen extends State<UpdateConversationScreen> {
/// Submit the conversation /// Submit the conversation
Future<void> _submitForm() async { Future<void> _submitForm() async {
final settings = ConversationSettings( final settings = Conversation(
id: 0, id: isUpdating ? widget.initialSettings.id : 0,
name: _nameController.text, name: _nameController.text,
following: _followConversation, following: _followConversation,
members: _members.usersID, members: _members.usersID,
);
// Give random value to these fields as they are ignored here
lastActive: 0,
ownerID: 0,
sawLastMessage: true);
// Create the conversation // Create the conversation
final conversationID = await ConversationsHelper().createConversation(settings); var conversationID = settings.id;
bool error = false;
if (isUpdating)
error = !(await ConversationsHelper().updateConversation(settings));
else {
conversationID = await ConversationsHelper().createConversation(settings);
if (conversationID < 1) error = true;
}
// Check for errors // Check for errors
if(conversationID < 1) if (error)
return Scaffold.of(context).showSnackBar( return Scaffold.of(context).showSnackBar(SnackBar(
SnackBar(content: Text(tr("Could not create the conversation!")), duration: Duration(seconds: 1),)); content: Text(isUpdating
? tr("Could not update the conversation!")
: tr("Could not create the conversation!")),
duration: Duration(seconds: 1),
));
// Open the conversation // Open the conversation
Navigator.of(context).pushReplacement(MaterialPageRoute( Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (c) => ConversationRoute(conversationID: conversationID,) builder: (c) => ConversationRoute(
)); conversationID: conversationID,
)));
} }
} }

View File

@ -18,13 +18,15 @@ class PickUserWidget extends StatefulWidget {
final String label; final String label;
final bool resetOnChoose; final bool resetOnChoose;
final bool keepFocusOnChoose; final bool keepFocusOnChoose;
final bool enabled;
const PickUserWidget( const PickUserWidget(
{Key key, {Key key,
@required this.onSelectUser, @required this.onSelectUser,
@required this.label, @required this.label,
this.resetOnChoose = false, this.resetOnChoose = false,
this.keepFocusOnChoose = false}) this.keepFocusOnChoose = false,
this.enabled = true})
: assert(onSelectUser != null), : assert(onSelectUser != null),
assert(label != null), assert(label != null),
assert(resetOnChoose != null), assert(resetOnChoose != null),
@ -66,6 +68,7 @@ class _PickUserWidgetState extends State<PickUserWidget> {
focusNode: _focusNode, focusNode: _focusNode,
onChanged: (s) => _updateSuggestions(), onChanged: (s) => _updateSuggestions(),
controller: _controller, controller: _controller,
enabled: widget.enabled,
decoration: InputDecoration( decoration: InputDecoration(
labelText: widget.label, labelText: widget.label,
alignLabelWithHint: true, alignLabelWithHint: true,