Skip to content

Commit 2bb35a5

Browse files
feat: Set up services and repositories.
feat: Set up services and repositories.
2 parents 06eb214 + 9903d37 commit 2bb35a5

24 files changed

Lines changed: 838 additions & 18 deletions

lib/data/config/config.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
11
class Environment {
2+
/// URL that is common to most outgoing requests.
3+
static const String baseUrl = String.fromEnvironment(
4+
'BASE_URL',
5+
defaultValue: 'https://codephile.mdg.iitr.ac.in/v1/',
6+
);
7+
8+
/// Project identifier for Sentry error logging.
29
static const String sentryDSN = String.fromEnvironment('SENTRY_DSN');
310
}

lib/data/constants/strings.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
class AppStrings {
22
static const String hiveBoxName = 'app_box';
3+
4+
static const String authTokenKey = 'auth_token';
35
}

lib/data/services/local/SAMPLE

Whitespace-only changes.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import 'package:hive_flutter/hive_flutter.dart';
2+
3+
import '../../constants/strings.dart';
4+
5+
class StorageService {
6+
/// Service initializer
7+
static void init() => _box = Hive.box(AppStrings.hiveBoxName);
8+
9+
// Data
10+
/// The Hive [Box] which contains all data stored by the app.
11+
static late final Box _box;
12+
13+
// CRUD methods
14+
static T? _get<T>(String key) => _box.get(key);
15+
16+
static void _set<T>(String key, T? value) => _box.put(key, value);
17+
18+
/// Remove a key from storage.
19+
static T? delete<T>(String key) {
20+
final T? value = _box.get(key);
21+
_box.delete(key);
22+
return value;
23+
}
24+
25+
/// Safely check whether a key exists in storage.
26+
/// Optionally check if the value is non-null.
27+
static bool exists(String key, {bool checkForNull = false}) {
28+
if (!_box.containsKey(key)) return false;
29+
return !checkForNull || _box.get(key) != null;
30+
}
31+
32+
/// Get a stream of [BoxEvent]s performed on a particular [key].
33+
static Stream<BoxEvent> stream(String key) => _box.watch(key: key);
34+
35+
// Specific getters and setters
36+
/// Authorization token for API requests.
37+
static String? get authToken => _get<String>(AppStrings.authTokenKey);
38+
39+
/// Authorization token for API requests.
40+
static set authToken(String? token) =>
41+
_set<String>(AppStrings.authTokenKey, token);
42+
}

lib/data/services/remote/SAMPLE

Whitespace-only changes.
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import 'package:dio/dio.dart';
2+
import 'package:flutter/foundation.dart';
3+
4+
import '../../config/config.dart';
5+
import '../local/storage_service.dart';
6+
7+
class ApiService {
8+
/// Service initializer
9+
static void init() => _channel = Dio();
10+
11+
// Data
12+
/// The [Dio] channel through which all requests will be routed.
13+
static late final Dio _channel;
14+
15+
/// Safe method to send GET request to an endpoint **below** [Environment.baseUrl].
16+
static Future<Map<String, dynamic>> get(
17+
String endpoint, {
18+
String? baseUrl,
19+
Map<String, dynamic>? headers,
20+
Map<String, dynamic>? query,
21+
}) async {
22+
Response? response;
23+
try {
24+
response = await _channel.get(
25+
(baseUrl ?? Environment.baseUrl) + endpoint,
26+
queryParameters: query,
27+
options: Options(
28+
validateStatus: (status) {
29+
return status! < 500;
30+
},
31+
headers: headers,
32+
),
33+
);
34+
return {
35+
'status_code': response.statusCode ?? 0,
36+
'data': response.data ?? 'null',
37+
};
38+
} on Exception catch (exception, stacktrace) {
39+
debugPrint(
40+
'ERROR: Failed during a GET request.\n'
41+
'Endpoint: $endpoint\nQuery: $query\n'
42+
'Exception: $exception\nStacktrace: $stacktrace',
43+
);
44+
}
45+
return {
46+
'status_code': response?.statusCode ?? 0,
47+
'data': response?.data ?? 'null',
48+
};
49+
}
50+
51+
/// Safe method to send POST request to an endpoint **below** [Environment.baseUrl].
52+
static Future<Map<String, dynamic>> post(
53+
String endpoint, {
54+
Map<String, dynamic>? headers,
55+
Map<String, dynamic>? data,
56+
}) async {
57+
Response? response;
58+
try {
59+
response = await _channel.post(
60+
Environment.baseUrl + endpoint,
61+
data: data,
62+
options: Options(
63+
validateStatus: (status) {
64+
return status! < 500;
65+
},
66+
headers: headers,
67+
),
68+
);
69+
return {
70+
'status_code': response.statusCode ?? 0,
71+
'data': response.data ?? 'null',
72+
};
73+
} on Exception catch (exception, stacktrace) {
74+
debugPrint(
75+
'ERROR: Failed during a POST request.\n'
76+
'Endpoint: $endpoint\nData: $data\n'
77+
'Exception: $exception\nStacktrace: $stacktrace',
78+
);
79+
}
80+
return {
81+
'status_code': response?.statusCode ?? 0,
82+
'data': response?.data ?? 'null',
83+
};
84+
}
85+
86+
/// Safe method to send PUT request to an endpoint **below** [Environment.baseUrl].
87+
static Future<Map<String, dynamic>> put(
88+
String endpoint, {
89+
Map<String, dynamic>? headers,
90+
Map<String, dynamic>? data,
91+
}) async {
92+
Response? response;
93+
try {
94+
response = await _channel.put(
95+
Environment.baseUrl + endpoint,
96+
data: data,
97+
options: Options(
98+
validateStatus: (status) {
99+
return status! < 500;
100+
},
101+
headers: headers,
102+
),
103+
);
104+
return {
105+
'status_code': response.statusCode ?? 0,
106+
'data': response.data ?? 'null',
107+
};
108+
} on Exception catch (exception, stacktrace) {
109+
debugPrint(
110+
'ERROR: Failed during a PUT request.\n'
111+
'Endpoint: $endpoint\nData: $data\n'
112+
'Exception: $exception\nStacktrace: $stacktrace',
113+
);
114+
}
115+
return {
116+
'status_code': response?.statusCode ?? 0,
117+
'data': response?.data ?? 'null',
118+
};
119+
}
120+
121+
/// Adds common tokens to outgoing requests.
122+
static void addTokenToHeaders(Map<String, dynamic> headers) {
123+
final token = StorageService.authToken;
124+
headers.addAll({'authorization': token});
125+
}
126+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import 'package:freezed_annotation/freezed_annotation.dart';
2+
3+
part 'activity_details.freezed.dart';
4+
part 'activity_details.g.dart';
5+
6+
@freezed
7+
class ActivityDetails with _$ActivityDetails {
8+
factory ActivityDetails({
9+
int? correct,
10+
int? details,
11+
@JsonKey(name: 'created_at') DateTime? createdAt,
12+
}) = _ActivityDetails;
13+
14+
factory ActivityDetails.fromJson(Map<String, dynamic> json) =>
15+
_$ActivityDetailsFromJson(json);
16+
}

lib/domain/models/contest.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import 'package:freezed_annotation/freezed_annotation.dart';
2+
3+
part 'contest.freezed.dart';
4+
part 'contest.g.dart';
5+
6+
@freezed
7+
class Contest with _$Contest {
8+
factory Contest({
9+
List<Ongoing>? ongoing,
10+
String? timestamp,
11+
List<Upcoming>? upcoming,
12+
}) = _Contest;
13+
14+
factory Contest.fromJson(Map<String, dynamic> json) =>
15+
_$ContestFromJson(json);
16+
}
17+
18+
@freezed
19+
class Ongoing with _$Ongoing {
20+
factory Ongoing({
21+
@JsonKey(name: 'EndTime') required DateTime endTime,
22+
@JsonKey(name: 'Name') required String name,
23+
@JsonKey(name: 'Platform') required String platform,
24+
required String url,
25+
@JsonKey(name: 'challenge_type') required String challengeType,
26+
}) = _Ongoing;
27+
28+
factory Ongoing.fromJson(Map<String, dynamic> json) =>
29+
_$OngoingFromJson(json);
30+
}
31+
32+
@freezed
33+
class Upcoming with _$Upcoming {
34+
factory Upcoming({
35+
@JsonKey(name: 'StartTime') required DateTime startTime,
36+
@JsonKey(name: 'EndTime') required DateTime endTime,
37+
@JsonKey(name: 'Name') required String name,
38+
@JsonKey(name: 'Platform') required String platform,
39+
required String url,
40+
@JsonKey(name: 'challenge_type') required String challengeType,
41+
String? duration,
42+
}) = _Upcoming;
43+
44+
factory Upcoming.fromJson(Map<String, dynamic> json) =>
45+
_$UpcomingFromJson(json);
46+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import 'package:freezed_annotation/freezed_annotation.dart';
2+
3+
part 'contest_filter.freezed.dart';
4+
part 'contest_filter.g.dart';
5+
6+
@freezed
7+
class ContestFilter with _$ContestFilter {
8+
factory ContestFilter({
9+
int? duration,
10+
bool? ongoing,
11+
bool? upcoming,
12+
DateTime? startDate,
13+
List<dynamic>? platform,
14+
}) = _ContestFilter;
15+
16+
factory ContestFilter.fromJson(Map<String, dynamic> json) =>
17+
_$ContestFilterFromJson(json);
18+
}

lib/domain/models/feed.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import 'package:freezed_annotation/freezed_annotation.dart';
2+
3+
import 'submission.dart';
4+
5+
part 'feed.freezed.dart';
6+
part 'feed.g.dart';
7+
8+
@freezed
9+
class Feed with _$Feed {
10+
factory Feed({
11+
required String userId,
12+
required String username,
13+
required String? fullname,
14+
String? picture,
15+
Submission? submission,
16+
}) = _Feed;
17+
18+
factory Feed.fromJson(Map<String, dynamic> json) => _$FeedFromJson(json);
19+
}

0 commit comments

Comments
 (0)