Skip to content

Commit

Permalink
chore: add fetch survey detail
Browse files Browse the repository at this point in the history
  • Loading branch information
markgravity committed Jun 29, 2021
1 parent ba47934 commit 2ff25e8
Show file tree
Hide file tree
Showing 16 changed files with 230 additions and 29 deletions.
6 changes: 6 additions & 0 deletions lib/configs/factories.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@ final Map<Type, Function> _factories = {
UserInfo: () => UserInfo(),
AuthTokenInfo: () => AuthTokenInfo(),
SurveyInfo: () => SurveyInfo(),
ApiRawResponse: () => ApiRawResponse(),
ApiRawObject: () => ApiRawObject(),
DetailedSurveyInfo: () => DetailedSurveyInfo(),
SurveyQuestionInfo: () => SurveyQuestionInfo(),
SurveyQuestionDisplayType: (v) => SurveyQuestionDisplayType(v as String),
SurveyQuestionPickType: (v) => SurveyQuestionPickType(v as String),
};
5 changes: 5 additions & 0 deletions lib/core/viper/interactor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ part of 'module.dart';
class Interactor<D> {
D? delegate;
}

abstract class ArgumentsInteractor<D, A extends ModuleArguments>
extends Interactor<D> {
A? arguments;
}
23 changes: 22 additions & 1 deletion lib/core/viper/module.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ import 'package:survey/services/locator/locator_service.dart';

export 'package:survey/core/extensions/build_context.dart';
export 'package:survey/core/extensions/stream.dart';

part 'presenter.dart';

part 'interactor.dart';

part 'router.dart';

part 'view.dart';

abstract class Module<V extends View, I extends Interactor, P extends Presenter,
Expand All @@ -21,10 +25,27 @@ abstract class Module<V extends View, I extends Interactor, P extends Presenter,

view.delegate = presenter;
interactor.delegate = presenter;
presenter.configure(view: view, interactor: interactor, router: router);
presenter.configure(
view: view,
interactor: interactor,
router: router,
);
}

void dispose() {
presenter.dispose();
}
}

abstract class ArgumentsModule<
V extends View,
I extends ArgumentsInteractor,
P extends Presenter,
R extends Router,
A extends ModuleArguments> extends Module<V, I, P, R> {
void setArguments(Object? arguments) {
interactor.arguments = arguments as A?;
}
}

abstract class ModuleArguments {}
12 changes: 10 additions & 2 deletions lib/core/viper/view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ abstract class View<D> {

abstract class ViewState<V extends StatefulWidget, M extends Widget, D>
extends State<V> implements View<D> {
@visibleForTesting
static Module? overriddenModule;
Module? _disposedModule;
@override
Expand All @@ -17,12 +18,19 @@ abstract class ViewState<V extends StatefulWidget, M extends Widget, D>
void initState() {
super.initState();

_getModule()?.assembly(this);
final module = _getModule();
module?.assembly(this);
}

@override
void didChangeDependencies() {
_disposedModule = _getModule();
final module = _getModule();

if (module is ArgumentsModule) {
module.setArguments(ModalRoute.of(context)?.settings.arguments);
}

_disposedModule = module;
super.didChangeDependencies();
}

Expand Down
3 changes: 3 additions & 0 deletions lib/gen/configs.gen.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import 'package:survey/gen/flavors.gen.dart';
import 'package:flutter/widgets.dart';
import 'package:survey/models/auth_token_info.dart';
import 'package:survey/models/detailed_survey_info.dart';
import 'package:survey/models/survey_info.dart';
import 'package:survey/models/survey_question_info.dart';
import 'package:survey/models/user_info.dart';
import 'package:survey/modules/forgot_password/forgot_password_module.dart';
import 'package:survey/modules/home/home_module.dart';
import 'package:survey/modules/landing/landing_module.dart';
import 'package:survey/modules/login/login_module.dart';
import 'package:survey/modules/side_menu/side_menu_module.dart';
import 'package:survey/modules/survey_detail/survey_detail_module.dart';
import 'package:survey/services/api/api_service.dart';

part 'package:survey/configs/app.dart';
part 'package:survey/configs/factories.dart';
Expand Down
12 changes: 7 additions & 5 deletions lib/models/auth_token_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ class AuthTokenInfo with Mappable {

@override
void mapping(Mapper map) {
map<String>("access_token", accessToken, (v) => accessToken = v as String);
map<String>("token_type", tokenType, (v) => tokenType = v as String);
map<DateTime>("expires_in", expiresIn, (v) => expiresIn = v as DateTime,
DateTransform());
map<String>("attributes.access_token", accessToken,
(v) => accessToken = v as String);
map<String>(
"refresh_token", refreshToken, (v) => refreshToken = v as String);
"attributes.token_type", tokenType, (v) => tokenType = v as String);
map<DateTime>("attributes.expires_in", expiresIn,
(v) => expiresIn = v as DateTime, const DateTransform());
map<String>("attributes.refresh_token", refreshToken,
(v) => refreshToken = v as String);
}
}
7 changes: 7 additions & 0 deletions lib/models/detailed_survey_info.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'package:survey/models/survey_info.dart';
import 'package:survey/models/survey_question_info.dart';

// ignore: must_be_immutable
class DetailedSurveyInfo extends SurveyInfo {
late List<SurveyQuestionInfo> questions;
}
9 changes: 6 additions & 3 deletions lib/models/survey_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ import 'package:equatable/equatable.dart';

// ignore: must_be_immutable
class SurveyInfo extends Equatable with Mappable {
String? id;
String? title;
String? description;
String? coverImageUrl;

@override
void mapping(Mapper map) {
map<String>("title", title, (v) => title = v as String?);
map<String>("description", description, (v) => description = v as String?);
map<String>("cover_image_url", coverImageUrl, (v) {
map<String>("id", id, (v) => id = v as String?);
map<String>("attributes.title", title, (v) => title = v as String?);
map<String>("attributes.description", description,
(v) => description = v as String?);
map<String>("attributes.cover_image_url", coverImageUrl, (v) {
final url = v as String?;
coverImageUrl = url != null ? "${url}l" : null;
});
Expand Down
87 changes: 87 additions & 0 deletions lib/models/survey_question_info.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import 'package:object_mapper/object_mapper.dart';

class SurveyQuestionInfo with Mappable {
String? id;
String? content;
int? displayOrder;
SurveyQuestionPickType? pickType;
SurveyQuestionDisplayType? displayType;
bool? isMandatory;
String? coverImageUrl;
double? coverImageOpacity;

@override
void mapping(Mapper map) {
map<String>(
"id",
id,
(v) => id = v as String,
);
map<String>(
"attributes.text",
content,
(v) => content = v as String,
);
map<int>(
"attributes.display_order",
displayOrder,
(v) => displayOrder = v as int,
);
map<SurveyQuestionPickType>(
"attributes.pick",
pickType,
(v) => pickType = v as SurveyQuestionPickType?,
const EnumTransform<SurveyQuestionPickType, String>(),
);
map<SurveyQuestionDisplayType>(
"attributes.display_type",
displayType,
(v) => displayType = v as SurveyQuestionDisplayType?,
const EnumTransform<SurveyQuestionDisplayType, String>(),
);
map<bool>(
"attributes.is_mandatory",
isMandatory,
(v) => isMandatory = v as bool,
);
map<String>(
"attributes.cover_image_url",
coverImageUrl,
(v) => coverImageUrl = v as String?,
);
map<double>(
"attributes.cover_image_opacity",
coverImageOpacity,
(v) => coverImageOpacity = v as double?,
);
}
}

// ignore: avoid_implementing_value_types
class SurveyQuestionDisplayType extends Enumerable<String> {
const SurveyQuestionDisplayType(this.rawValue);

@override
final String rawValue;

static const intro = SurveyQuestionDisplayType("intro");
static const star = SurveyQuestionDisplayType("star");
static const heart = SurveyQuestionDisplayType("heart");
static const smiley = SurveyQuestionDisplayType("smiley");
static const choice = SurveyQuestionDisplayType("choice");
static const nps = SurveyQuestionDisplayType("NPS");
static const textarea = SurveyQuestionDisplayType("textarea");
static const textField = SurveyQuestionDisplayType("textfield");
static const outro = SurveyQuestionDisplayType("outro");
}

class SurveyQuestionPickType extends Enumerable<String> {
const SurveyQuestionPickType(this.rawValue);

@override
final String rawValue;

static const none = SurveyQuestionDisplayType("none");
static const one = SurveyQuestionDisplayType("one");
static const any = SurveyQuestionDisplayType("any");
}
4 changes: 2 additions & 2 deletions lib/models/user_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class UserInfo with Mappable {

@override
void mapping(Mapper map) {
map("email", email, (v) => email = v as String);
map("avatar_url", avatarUrl, (v) => avatarUrl = v as String);
map("attributes.email", email, (v) => email = v as String);
map("attributes.avatar_url", avatarUrl, (v) => avatarUrl = v as String);
}
}
9 changes: 9 additions & 0 deletions lib/repositories/survey_repository.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:survey/models/detailed_survey_info.dart';
import 'package:survey/models/survey_info.dart';
import 'package:survey/services/api/survey/survey_api_service.dart';
import 'package:survey/services/local_storage/local_storage_service.dart';
Expand All @@ -11,6 +12,8 @@ abstract class SurveyRepository {
Future<List<SurveyInfo>> fetchSurveysFromCached();

Future<List<SurveyInfo>> fetchSurveysFromRemote();

Future<DetailedSurveyInfo> fetchDetailedSurvey(String id);
}

class SurveyRepositoryImpl implements SurveyRepository {
Expand Down Expand Up @@ -45,4 +48,10 @@ class SurveyRepositoryImpl implements SurveyRepository {

return list.items;
}

@override
Future<DetailedSurveyInfo> fetchDetailedSurvey(String id) {
final params = SurveyInfoParams(id: id);
return _surveyApiService.info(params: params);
}
}
30 changes: 30 additions & 0 deletions lib/services/api/api_raw_response.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
part of 'api_service.dart';

class ApiRawResponse with Mappable {
ApiRawObject? data;
Map<String, dynamic>? meta;
List<ApiRawObject>? included;

@override
void mapping(Mapper map) {
map<ApiRawObject>("data", data, (v) => data = v as ApiRawObject?);
map<Map<String, dynamic>>(
"meta", meta, (v) => meta = v as Map<String, dynamic>?);
map<ApiRawObject>(
"included", included, (v) => included = v as List<ApiRawObject>?);
}
}

class ApiRawObject with Mappable {
String? id;
String? type;
Map<String, dynamic>? attributes;

@override
void mapping(Mapper map) {
map<String>("id", id, (v) => id = v as String);
map<String>("type", type, (v) => type = v as String);
map<Map<String, dynamic>>("attributes", attributes,
(v) => attributes = v as Map<String, dynamic>);
}
}
16 changes: 9 additions & 7 deletions lib/services/api/api_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ part 'api_service_register.dart';

part 'api_list_object.dart';

part 'api_raw_response.dart';

abstract class ApiService {
Future<T> call<T>({
required HttpMethod method,
Expand Down Expand Up @@ -146,17 +148,19 @@ class ApiServiceImpl implements ApiService {
}

T _convertResponseToObject<T>(Map<String, dynamic> response) {
if (T == ApiRawResponse) {
return Mapper.fromJson(response).toObject<T>()!;
}

if (response["data"] == null && T.toString() == "void") {
return null as T;
}

if (response["data"] is! Map<String, dynamic> ||
response["data"]["attributes"] is! Map<String, dynamic>) {
if (response["data"] is! Map<String, dynamic>) {
throw ApiException.invalidResponseStructure;
}

return Mapper.fromJson(
response["data"]["attributes"] as Map<String, dynamic>)
return Mapper.fromJson(response["data"] as Map<String, dynamic>)
.toObject<T>()!;
}

Expand All @@ -167,9 +171,7 @@ class ApiServiceImpl implements ApiService {
}

final items = (response["data"] as List<dynamic>)
.where((e) => e["attributes"] is Map<String, dynamic>)
.map((e) => Mapper.fromJson(e["attributes"] as Map<String, dynamic>)
.toObject<T>()!)
.map((e) => Mapper.fromJson(e as Map<String, dynamic>).toObject<T>()!)
.toList();

return ApiListObject<T>(items: items);
Expand Down
Loading

0 comments on commit 2ff25e8

Please sign in to comment.