Skip to content

Commit 90e8506

Browse files
authored
Merge pull request #101 from akintewe/feature/STRKWAGR-42-categories-hashtag-endpoints
Implement Categories and Hashtag Endpoints in Create Wager screen
2 parents b1bbc1a + 133ecd1 commit 90e8506

15 files changed

Lines changed: 634 additions & 124 deletions
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import 'package:dio/dio.dart';
2+
import '../../core/error/failures.dart';
3+
import '../../core/network/api_client.dart';
4+
import '../../domain/models/category.dart';
5+
import '../../presentation/providers/auth_provider.dart';
6+
import '../../presentation/states/auth_state.dart';
7+
import 'package:flutter_riverpod/flutter_riverpod.dart';
8+
9+
abstract class CategoryRemoteDatasource {
10+
Future<CategoriesResponse> getAllCategories();
11+
}
12+
13+
class CategoryRemoteDatasourceImpl implements CategoryRemoteDatasource {
14+
final ApiClient apiClient;
15+
final Ref ref;
16+
17+
CategoryRemoteDatasourceImpl(this.apiClient, this.ref);
18+
19+
@override
20+
Future<CategoriesResponse> getAllCategories() async {
21+
try {
22+
// Check if the user is authenticated
23+
final authState = ref.read(authNotifierProvider);
24+
if (authState is! AuthSuccess) {
25+
throw ServerFailure(
26+
message: 'Authentication required to fetch categories',
27+
);
28+
}
29+
30+
// Get the access token
31+
final token = authState.response.tokens.accessToken;
32+
33+
// Make the API request with the token
34+
final response = await apiClient.get(
35+
'/categories/all',
36+
options: Options(
37+
headers: {
38+
'Authorization': 'Bearer $token',
39+
},
40+
),
41+
);
42+
43+
// Pass the direct response data to the fromJson method
44+
return CategoriesResponse.fromJson(response.data);
45+
} on DioException catch (e) {
46+
throw ServerFailure(
47+
message: e.message ?? 'An error occurred while fetching categories',
48+
code: e.response?.statusCode,
49+
);
50+
} catch (e) {
51+
if (e is ServerFailure) {
52+
rethrow;
53+
}
54+
throw ServerFailure(message: e.toString());
55+
}
56+
}
57+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import 'package:dio/dio.dart';
2+
import '../../core/error/failures.dart';
3+
import '../../core/network/api_client.dart';
4+
import '../../domain/models/hashtag.dart';
5+
import '../../presentation/providers/auth_provider.dart';
6+
import '../../presentation/states/auth_state.dart';
7+
import 'package:flutter_riverpod/flutter_riverpod.dart';
8+
9+
abstract class HashtagRemoteDatasource {
10+
Future<HashtagsResponse> getAllHashtags();
11+
}
12+
13+
class HashtagRemoteDatasourceImpl implements HashtagRemoteDatasource {
14+
final ApiClient apiClient;
15+
final Ref ref;
16+
17+
HashtagRemoteDatasourceImpl(this.apiClient, this.ref);
18+
19+
@override
20+
Future<HashtagsResponse> getAllHashtags() async {
21+
try {
22+
// Check if the user is authenticated
23+
final authState = ref.read(authNotifierProvider);
24+
if (authState is! AuthSuccess) {
25+
throw ServerFailure(
26+
message: 'Authentication required to fetch hashtags',
27+
);
28+
}
29+
30+
// Get the access token
31+
final token = authState.response.tokens.accessToken;
32+
33+
// Make the API request with the token
34+
final response = await apiClient.get(
35+
'/hashtags',
36+
options: Options(
37+
headers: {
38+
'Authorization': 'Bearer $token',
39+
},
40+
),
41+
);
42+
43+
// Pass the direct response data to the fromJson method
44+
return HashtagsResponse.fromJson(response.data);
45+
} on DioException catch (e) {
46+
throw ServerFailure(
47+
message: e.message ?? 'An error occurred while fetching hashtags',
48+
code: e.response?.statusCode,
49+
);
50+
} catch (e) {
51+
if (e is ServerFailure) {
52+
rethrow;
53+
}
54+
throw ServerFailure(message: e.toString());
55+
}
56+
}
57+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import '../../domain/models/category.dart';
2+
import '../../domain/repositories/category_repository.dart';
3+
import '../datasources/category_remote_datasource.dart';
4+
5+
class CategoryRepositoryImpl implements CategoryRepository {
6+
final CategoryRemoteDatasource remoteDatasource;
7+
8+
CategoryRepositoryImpl(this.remoteDatasource);
9+
10+
@override
11+
Future<CategoriesResponse> getAllCategories() {
12+
return remoteDatasource.getAllCategories();
13+
}
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import '../../domain/models/hashtag.dart';
2+
import '../../domain/repositories/hashtag_repository.dart';
3+
import '../datasources/hashtag_remote_datasource.dart';
4+
5+
class HashtagRepositoryImpl implements HashtagRepository {
6+
final HashtagRemoteDatasource remoteDatasource;
7+
8+
HashtagRepositoryImpl(this.remoteDatasource);
9+
10+
@override
11+
Future<HashtagsResponse> getAllHashtags() {
12+
return remoteDatasource.getAllHashtags();
13+
}
14+
}

lib/domain/models/category.dart

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
class Category {
2+
final String id;
3+
final String name;
4+
final String createdAt;
5+
final String updatedAt;
6+
7+
Category({
8+
required this.id,
9+
required this.name,
10+
required this.createdAt,
11+
required this.updatedAt,
12+
});
13+
14+
factory Category.fromJson(Map<String, dynamic> json) => Category(
15+
id: json['id'] ?? '',
16+
name: json['name'] ?? '',
17+
createdAt: json['createdAt'] ?? '',
18+
updatedAt: json['updatedAt'] ?? '',
19+
);
20+
}
21+
22+
class CategoriesResponse {
23+
final List<Category> categories;
24+
25+
CategoriesResponse({required this.categories});
26+
27+
factory CategoriesResponse.fromJson(dynamic json) {
28+
// Handle both array response and object with data field
29+
List<dynamic> categoriesData = [];
30+
31+
if (json is List) {
32+
// Direct array response
33+
categoriesData = json;
34+
} else if (json is Map<String, dynamic>) {
35+
// Response with data field
36+
categoriesData = json['data'] ?? [];
37+
}
38+
39+
final categories = categoriesData
40+
.map((categoryJson) => Category.fromJson(categoryJson))
41+
.toList();
42+
return CategoriesResponse(categories: categories);
43+
}
44+
}

lib/domain/models/hashtag.dart

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
class Hashtag {
2+
final String id;
3+
final String name;
4+
final String createdAt;
5+
final String updatedAt;
6+
7+
Hashtag({
8+
required this.id,
9+
required this.name,
10+
required this.createdAt,
11+
required this.updatedAt,
12+
});
13+
14+
factory Hashtag.fromJson(Map<String, dynamic> json) => Hashtag(
15+
id: json['id'] ?? '',
16+
name: json['name'] ?? '',
17+
createdAt: json['createdAt'] ?? '',
18+
updatedAt: json['updatedAt'] ?? '',
19+
);
20+
}
21+
22+
class HashtagsResponse {
23+
final List<Hashtag> hashtags;
24+
25+
HashtagsResponse({required this.hashtags});
26+
27+
factory HashtagsResponse.fromJson(dynamic json) {
28+
// Handle both array response and object with data field
29+
List<dynamic> hashtagsData = [];
30+
31+
if (json is List) {
32+
// Direct array response
33+
hashtagsData = json;
34+
} else if (json is Map<String, dynamic>) {
35+
// Response with data field
36+
hashtagsData = json['data'] ?? [];
37+
}
38+
39+
final hashtags = hashtagsData
40+
.map((hashtagJson) => Hashtag.fromJson(hashtagJson))
41+
.toList();
42+
return HashtagsResponse(hashtags: hashtags);
43+
}
44+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import '../models/category.dart';
2+
3+
abstract class CategoryRepository {
4+
Future<CategoriesResponse> getAllCategories();
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import '../models/hashtag.dart';
2+
3+
abstract class HashtagRepository {
4+
Future<HashtagsResponse> getAllHashtags();
5+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import '../models/category.dart';
2+
import '../repositories/category_repository.dart';
3+
4+
class GetAllCategories {
5+
final CategoryRepository repository;
6+
7+
GetAllCategories(this.repository);
8+
9+
Future<CategoriesResponse> call() async {
10+
return await repository.getAllCategories();
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import '../models/hashtag.dart';
2+
import '../repositories/hashtag_repository.dart';
3+
4+
class GetAllHashtags {
5+
final HashtagRepository repository;
6+
7+
GetAllHashtags(this.repository);
8+
9+
Future<HashtagsResponse> call() async {
10+
return await repository.getAllHashtags();
11+
}
12+
}

0 commit comments

Comments
 (0)