Skip to content

Commit 35df134

Browse files
Add comment feature (#32)
* add api calls and type for activity comments * start implementing comment on activity items * separate like and comments from item widget * keep working on comments * improve code * finish comment feature
1 parent 34061c8 commit 35df134

22 files changed

Lines changed: 619 additions & 106 deletions

lib/data/api/activity_api.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:dio/dio.dart';
2+
import 'package:run_flutter_run/data/model/response/activity_comment_response.dart';
23

34
import '../model/request/activity_request.dart';
45
import '../model/response/activity_response.dart';
@@ -86,4 +87,34 @@ class ActivityApi {
8687
await ApiHelper.makeRequest('${ActivityApi.url}dislike', 'POST_FORM_DATA',
8788
data: {'id': activityId});
8889
}
90+
91+
/// Comment the activity
92+
static Future<ActivityCommentResponse> createComment(
93+
String activityId, String comment) async {
94+
Response? response = await ApiHelper.makeRequest(
95+
'${ActivityApi.url}comment', 'POST_FORM_DATA',
96+
data: {'activityId': activityId, 'comment': comment});
97+
return ActivityCommentResponse.fromMap(response?.data);
98+
}
99+
100+
/// Edits an existing comment.
101+
///
102+
/// Returns an [ActivityCommentResponse] object.
103+
static Future<ActivityCommentResponse> editComment(
104+
String id, String comment) async {
105+
Response? response = await ApiHelper.makeRequest(
106+
'${ActivityApi.url}comment', 'PUT',
107+
data: {'id': id, 'comment': comment});
108+
return ActivityCommentResponse.fromMap(response?.data);
109+
}
110+
111+
/// Removes a comment by its ID.
112+
///
113+
/// Returns the response data as a string.
114+
static Future<String?> removeComment(String id) async {
115+
Response? response = await ApiHelper.makeRequest(
116+
'${ActivityApi.url}comment', 'DELETE',
117+
queryParams: {'id': int.parse(id)});
118+
return response?.data?.toString();
119+
}
89120
}

lib/data/api/helpers/api_helper.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ import '../user_api.dart';
1010
/// Helper class for making API requests.
1111
class ApiHelper {
1212
// switch url when back is down
13-
//static const String apiUrl =
14-
// 'http://lxgfjcmoky.us18.qoddiapp.com/api/'; //US server
1513
static const String apiUrl =
16-
'http://tiqorhzmyb.eu11.qoddiapp.com/api/'; //EU server
14+
'http://lxgfjcmoky.us18.qoddiapp.com/api/'; //US server
15+
//static const String apiUrl =
16+
// 'http://tiqorhzmyb.eu11.qoddiapp.com/api/'; //EU server
1717

1818
/// Makes an HTTP request to the specified [url] using the given [method].
1919
///
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import 'package:equatable/equatable.dart';
2+
3+
import '../../../domain/entities/activity_comment.dart';
4+
import 'user_response.dart';
5+
6+
/// Represents a response object for an activity comment.
7+
class ActivityCommentResponse extends Equatable {
8+
/// The ID of the comment.
9+
final String id;
10+
11+
/// The datetime of the comment
12+
final DateTime createdAt;
13+
14+
/// The user
15+
final UserResponse user;
16+
17+
/// The comment content
18+
final String content;
19+
20+
/// Constructs an ActivityCommentResponse object with the given parameters.
21+
const ActivityCommentResponse(
22+
{required this.id,
23+
required this.createdAt,
24+
required this.user,
25+
required this.content});
26+
27+
@override
28+
List<Object?> get props => [id, createdAt, user, content];
29+
30+
/// Creates a ActivityCommentResponse object from a JSON map.
31+
factory ActivityCommentResponse.fromMap(Map<String, dynamic> map) {
32+
return ActivityCommentResponse(
33+
id: map['id'].toString(),
34+
createdAt: DateTime.parse(map['createdAt']),
35+
user: UserResponse.fromMap(map['user']),
36+
content: map['content'].toString());
37+
}
38+
39+
/// Converts the ActivityCommentResponse object to a ActivityComment entity.
40+
ActivityComment toEntity() {
41+
return ActivityComment(
42+
id: id,
43+
createdAt: createdAt,
44+
user: user.toEntity(),
45+
content: content,
46+
);
47+
}
48+
}

lib/data/model/response/activity_response.dart

Lines changed: 57 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import 'package:equatable/equatable.dart';
22

33
import '../../../domain/entities/activity.dart';
4+
import '../../../domain/entities/activity_comment.dart';
45
import '../../../domain/entities/enum/activity_type.dart';
56
import '../../../domain/entities/location.dart';
67
import '../../../domain/entities/user.dart';
8+
import 'activity_comment_response.dart';
79
import 'location_response.dart';
810
import 'user_response.dart';
911

@@ -42,6 +44,9 @@ class ActivityResponse extends Equatable {
4244
/// has current user liked ?
4345
final bool hasCurrentUserLiked;
4446

47+
/// The list of comments
48+
final Iterable<ActivityCommentResponse> comments;
49+
4550
/// Constructs an ActivityResponse object with the given parameters.
4651
const ActivityResponse(
4752
{required this.id,
@@ -54,7 +59,8 @@ class ActivityResponse extends Equatable {
5459
required this.locations,
5560
required this.user,
5661
required this.likesCount,
57-
required this.hasCurrentUserLiked});
62+
required this.hasCurrentUserLiked,
63+
required this.comments});
5864

5965
@override
6066
List<Object?> get props => [
@@ -68,7 +74,8 @@ class ActivityResponse extends Equatable {
6874
...locations,
6975
user,
7076
likesCount,
71-
hasCurrentUserLiked
77+
hasCurrentUserLiked,
78+
...comments
7279
];
7380

7481
/// Creates an ActivityResponse object from a JSON map.
@@ -80,23 +87,28 @@ class ActivityResponse extends Equatable {
8087
);
8188

8289
return ActivityResponse(
83-
id: map['id'].toString(),
84-
type: activityType,
85-
startDatetime: DateTime.parse(map['startDatetime']),
86-
endDatetime: DateTime.parse(map['endDatetime']),
87-
distance: map['distance'].toDouble(),
88-
speed: map['speed'] is String
89-
? double.parse(map['speed'])
90-
: map['speed'].toDouble(),
91-
time: map['time'].toDouble(),
92-
likesCount: map['likesCount'].toDouble(),
93-
hasCurrentUserLiked: map['hasCurrentUserLiked'],
94-
locations: (map['locations'] as List<dynamic>)
95-
.map<LocationResponse>((item) => LocationResponse.fromMap(item))
96-
.toList(),
97-
user: UserResponse.fromMap(
98-
map['user'],
99-
));
90+
id: map['id'].toString(),
91+
type: activityType,
92+
startDatetime: DateTime.parse(map['startDatetime']),
93+
endDatetime: DateTime.parse(map['endDatetime']),
94+
distance: map['distance'].toDouble(),
95+
speed: map['speed'] is String
96+
? double.parse(map['speed'])
97+
: map['speed'].toDouble(),
98+
time: map['time'].toDouble(),
99+
likesCount: map['likesCount'].toDouble(),
100+
hasCurrentUserLiked: map['hasCurrentUserLiked'],
101+
locations: (map['locations'] as List<dynamic>)
102+
.map<LocationResponse>((item) => LocationResponse.fromMap(item))
103+
.toList(),
104+
user: UserResponse.fromMap(
105+
map['user'],
106+
),
107+
comments: (map['comments'] as List<dynamic>)
108+
.map<ActivityCommentResponse>(
109+
(item) => ActivityCommentResponse.fromMap(item))
110+
.toList(),
111+
);
100112
}
101113

102114
/// Converts the ActivityResponse object to an Activity entity.
@@ -111,22 +123,32 @@ class ActivityResponse extends Equatable {
111123
}).toList()
112124
..sort((a, b) => a.datetime.compareTo(b.datetime));
113125

126+
final activityComments = comments.map<ActivityComment>((comment) {
127+
return ActivityComment(
128+
id: comment.id,
129+
createdAt: comment.createdAt,
130+
user: comment.user.toEntity(),
131+
content: comment.content,
132+
);
133+
}).toList()
134+
..sort((a, b) => a.createdAt.compareTo(b.createdAt));
135+
114136
return Activity(
115-
id: id,
116-
type: type,
117-
startDatetime: startDatetime,
118-
endDatetime: endDatetime,
119-
distance: distance,
120-
speed: speed,
121-
time: time,
122-
locations: activityLocations,
123-
likesCount: likesCount,
124-
hasCurrentUserLiked: hasCurrentUserLiked,
125-
user: User(
126-
id: user.id,
127-
username: user.username,
128-
firstname: user.firstname,
129-
lastname: user.lastname),
130-
);
137+
id: id,
138+
type: type,
139+
startDatetime: startDatetime,
140+
endDatetime: endDatetime,
141+
distance: distance,
142+
speed: speed,
143+
time: time,
144+
locations: activityLocations,
145+
likesCount: likesCount,
146+
hasCurrentUserLiked: hasCurrentUserLiked,
147+
user: User(
148+
id: user.id,
149+
username: user.username,
150+
firstname: user.firstname,
151+
lastname: user.lastname),
152+
comments: activityComments);
131153
}
132154
}

lib/data/repositories/activity_repository_impl.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:hooks_riverpod/hooks_riverpod.dart';
2+
import 'package:run_flutter_run/domain/entities/activity_comment.dart';
23

34
import '../../domain/entities/activity.dart';
45
import '../../domain/repositories/activity_repository.dart';
@@ -63,4 +64,22 @@ class ActivityRepositoryImpl extends ActivityRepository {
6364
Future<void> dislike(String id) async {
6465
await ActivityApi.dislike(id);
6566
}
67+
68+
@override
69+
Future<ActivityComment?> createComment(
70+
String activityId, String comment) async {
71+
final response = await ActivityApi.createComment(activityId, comment);
72+
return response.toEntity();
73+
}
74+
75+
@override
76+
Future<ActivityComment> editComment(String id, String comment) async {
77+
final response = await ActivityApi.editComment(id, comment);
78+
return response.toEntity();
79+
}
80+
81+
@override
82+
Future<String?> removeComment({required String id}) async {
83+
return await ActivityApi.removeComment(id);
84+
}
6685
}

lib/domain/entities/activity.dart

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:equatable/equatable.dart';
2+
import 'package:run_flutter_run/domain/entities/activity_comment.dart';
23

34
import 'enum/activity_type.dart';
45
import 'location.dart';
@@ -39,6 +40,9 @@ class Activity extends Equatable {
3940
/// has current user liked ?
4041
final bool hasCurrentUserLiked;
4142

43+
/// The list of comments associated with the activity.
44+
final Iterable<ActivityComment> comments;
45+
4246
/// Constructs an Activity object with the given parameters.
4347
const Activity(
4448
{required this.id,
@@ -51,23 +55,27 @@ class Activity extends Equatable {
5155
required this.locations,
5256
required this.user,
5357
required this.likesCount,
54-
required this.hasCurrentUserLiked});
58+
required this.hasCurrentUserLiked,
59+
required this.comments});
5560

5661
Activity copy(
57-
{ActivityType? type, double? likesCount, bool? hasCurrentUserLiked}) {
62+
{ActivityType? type,
63+
double? likesCount,
64+
bool? hasCurrentUserLiked,
65+
Iterable<ActivityComment>? comments}) {
5866
return Activity(
59-
id: id,
60-
type: type ?? this.type,
61-
startDatetime: startDatetime,
62-
endDatetime: endDatetime,
63-
distance: distance,
64-
speed: speed,
65-
time: time,
66-
locations: locations,
67-
user: user,
68-
likesCount: likesCount ?? this.likesCount,
69-
hasCurrentUserLiked: hasCurrentUserLiked ?? this.hasCurrentUserLiked,
70-
);
67+
id: id,
68+
type: type ?? this.type,
69+
startDatetime: startDatetime,
70+
endDatetime: endDatetime,
71+
distance: distance,
72+
speed: speed,
73+
time: time,
74+
locations: locations,
75+
user: user,
76+
likesCount: likesCount ?? this.likesCount,
77+
hasCurrentUserLiked: hasCurrentUserLiked ?? this.hasCurrentUserLiked,
78+
comments: comments ?? this.comments);
7179
}
7280

7381
@override
@@ -82,6 +90,7 @@ class Activity extends Equatable {
8290
...locations,
8391
user,
8492
likesCount,
85-
hasCurrentUserLiked
93+
hasCurrentUserLiked,
94+
...comments
8695
];
8796
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import 'package:equatable/equatable.dart';
2+
3+
import 'user.dart';
4+
5+
/// Represents an activity comment.
6+
class ActivityComment extends Equatable {
7+
/// The ID of the activityComment.
8+
final String id;
9+
10+
/// The datetime of the comment.
11+
final DateTime createdAt;
12+
13+
/// The user
14+
final User user;
15+
16+
/// The content
17+
final String content;
18+
19+
/// Constructs a Location object with the given parameters.
20+
const ActivityComment({
21+
required this.id,
22+
required this.createdAt,
23+
required this.user,
24+
required this.content,
25+
});
26+
27+
@override
28+
List<Object?> get props => [id, createdAt, user, content];
29+
}

lib/domain/repositories/activity_repository.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'package:run_flutter_run/domain/entities/activity_comment.dart';
2+
13
import '../../data/model/request/activity_request.dart';
24
import '../entities/activity.dart';
35

@@ -29,4 +31,13 @@ abstract class ActivityRepository {
2931

3032
/// Dislike the activity
3133
Future<void> dislike(String id);
34+
35+
/// Removes a comment by its ID.
36+
Future<String?> removeComment({required String id});
37+
38+
/// Adds a new comment.
39+
Future<ActivityComment?> createComment(String activityId, String comment);
40+
41+
/// Edits an existing comment.
42+
Future<ActivityComment> editComment(String id, String comment);
3243
}

0 commit comments

Comments
 (0)