Commit e92ea8bf authored by DatHV's avatar DatHV
Browse files

update logic

parent 5413611e
...@@ -10,4 +10,7 @@ class APIPaths { ...@@ -10,4 +10,7 @@ class APIPaths {
static const String otpCreateNew = "/otpCreateNew/1.0.0"; static const String otpCreateNew = "/otpCreateNew/1.0.0";
static const String websitePageGetDetail = "/websitePageGetDetail/1.0.0"; static const String websitePageGetDetail = "/websitePageGetDetail/1.0.0";
static const String websitePage = "/user/api/v2.0/websitePage"; static const String websitePage = "/user/api/v2.0/websitePage";
static const String websiteFolderGetPageList = "/websiteFolderGetPageList/1.0.0";
static const String otpVerifyForDoingNextEvent = "/otpVerifyForDoingNextEvent/1.0.0";
static const String accountPasswordReset = "/accountPasswordReset/1.0.0";
} }
...@@ -7,20 +7,22 @@ part 'directional_screen.g.dart'; ...@@ -7,20 +7,22 @@ part 'directional_screen.g.dart';
@JsonSerializable() @JsonSerializable()
class DirectionalScreen { class DirectionalScreen {
@JsonKey(name: "click_action_type") @JsonKey(name: "click_action_type")
final String clickActionType; final String? clickActionType;
@JsonKey(name: "click_action_param") @JsonKey(name: "click_action_param")
final String? clickActionParam; final String? clickActionParam;
final ClickActionType? actionType;
DirectionalScreen({ DirectionalScreen({
required this.clickActionType, this.clickActionType,
this.clickActionParam, this.clickActionParam,
this.actionType,
}); });
factory DirectionalScreen.fromJson(Map<String, dynamic> json) => _$DirectionalScreenFromJson(json); factory DirectionalScreen.fromJson(Map<String, dynamic> json) => _$DirectionalScreenFromJson(json);
Map<String, dynamic> toJson() => _$DirectionalScreenToJson(this); Map<String, dynamic> toJson() => _$DirectionalScreenToJson(this);
void begin() { void begin() {
final type = ClickActionTypeExtension.fromString(clickActionType); final type = ClickActionTypeExtension.fromString(clickActionType ?? actionType?.key ?? "");
if (type == null) { if (type == null) {
print("Không nhận diện được action type: $clickActionType"); print("Không nhận diện được action type: $clickActionType");
return; return;
......
...@@ -8,12 +8,21 @@ part of 'directional_screen.dart'; ...@@ -8,12 +8,21 @@ part of 'directional_screen.dart';
DirectionalScreen _$DirectionalScreenFromJson(Map<String, dynamic> json) => DirectionalScreen _$DirectionalScreenFromJson(Map<String, dynamic> json) =>
DirectionalScreen( DirectionalScreen(
clickActionType: json['click_action_type'] as String, clickActionType: json['click_action_type'] as String?,
clickActionParam: json['click_action_param'] as String?, clickActionParam: json['click_action_param'] as String?,
actionType: $enumDecodeNullable(
_$ClickActionTypeEnumMap,
json['actionType'],
),
); );
Map<String, dynamic> _$DirectionalScreenToJson(DirectionalScreen instance) => Map<String, dynamic> _$DirectionalScreenToJson(DirectionalScreen instance) =>
<String, dynamic>{ <String, dynamic>{
'click_action_type': instance.clickActionType, 'click_action_type': instance.clickActionType,
'click_action_param': instance.clickActionParam, 'click_action_param': instance.clickActionParam,
'actionType': _$ClickActionTypeEnumMap[instance.actionType],
}; };
const _$ClickActionTypeEnumMap = {
ClickActionType.campaignDetail: 'campaignDetail',
};
...@@ -6,8 +6,10 @@ import 'package:mypoint_flutter_app/extensions/string_extension.dart'; ...@@ -6,8 +6,10 @@ import 'package:mypoint_flutter_app/extensions/string_extension.dart';
import 'package:mypoint_flutter_app/networking/restful_api.dart'; import 'package:mypoint_flutter_app/networking/restful_api.dart';
import '../configs/device_info.dart'; import '../configs/device_info.dart';
import '../model/update_response_object.dart'; import '../model/update_response_object.dart';
import '../screen/faqs/faqs_model.dart';
import '../screen/onboarding/model/check_phone_response_model.dart'; import '../screen/onboarding/model/check_phone_response_model.dart';
import '../screen/onboarding/model/onboarding_info_model.dart'; import '../screen/onboarding/model/onboarding_info_model.dart';
import '../screen/otp/model/create_otp_response_model.dart';
import '../screen/otp/model/otp_verify_response_model.dart'; import '../screen/otp/model/otp_verify_response_model.dart';
import '../screen/pageDetail/model/campaign_detail_model.dart'; import '../screen/pageDetail/model/campaign_detail_model.dart';
import '../screen/pageDetail/model/detail_page_rule_type.dart'; import '../screen/pageDetail/model/detail_page_rule_type.dart';
...@@ -48,7 +50,7 @@ extension RestfullAPIClientAllApi on RestfulAPIClient { ...@@ -48,7 +50,7 @@ extension RestfullAPIClientAllApi on RestfulAPIClient {
} }
Future<BaseResponseModel<OTPVerifyResponseModel>> verifyOTP(String otp, String mfaToken) async { Future<BaseResponseModel<OTPVerifyResponseModel>> verifyOTP(String otp, String mfaToken) async {
final body = {"otp": otp, "mfaToken": mfaToken,}; final body = {"otp": otp, "mfaToken": mfaToken};
return requestNormal( return requestNormal(
APIPaths.verifyOtpWithAction, APIPaths.verifyOtpWithAction,
Method.POST, Method.POST,
...@@ -58,7 +60,7 @@ extension RestfullAPIClientAllApi on RestfulAPIClient { ...@@ -58,7 +60,7 @@ extension RestfullAPIClientAllApi on RestfulAPIClient {
} }
Future<BaseResponseModel<OTPResendResponseModel>> resendOTP(String mfaToken) async { Future<BaseResponseModel<OTPResendResponseModel>> resendOTP(String mfaToken) async {
final body = {"mfaToken": mfaToken,}; final body = {"mfaToken": mfaToken};
return requestNormal( return requestNormal(
APIPaths.retryOtpWithAction, APIPaths.retryOtpWithAction,
Method.POST, Method.POST,
...@@ -70,27 +72,42 @@ extension RestfullAPIClientAllApi on RestfulAPIClient { ...@@ -70,27 +72,42 @@ extension RestfullAPIClientAllApi on RestfulAPIClient {
Future<BaseResponseModel<EmptyCodable>> signup(String phone, String password) async { Future<BaseResponseModel<EmptyCodable>> signup(String phone, String password) async {
var deviceKey = await DeviceInfo.getDeviceId(); var deviceKey = await DeviceInfo.getDeviceId();
final body = {"username": phone, "password": password.toSha256(), "device_key": deviceKey}; final body = {"username": phone, "password": password.toSha256(), "device_key": deviceKey};
return requestNormal(APIPaths.signup, Method.POST, body, (data) => EmptyCodable.fromJson(data as Json));
}
Future<BaseResponseModel<CreateOTPResponseModel>> otpCreateNew(String ownerId) async {
var deviceKey = await DeviceInfo.getDeviceId();
final body = {"owner_id": ownerId, "ttl": Constants.otpTtl, "resend_after_second": Constants.otpTtl};
return requestNormal( return requestNormal(
APIPaths.signup, APIPaths.otpCreateNew,
Method.POST, Method.POST,
body, body,
(data) => EmptyCodable.fromJson(data as Json), (data) => CreateOTPResponseModel.fromJson(data as Json),
); );
} }
Future<BaseResponseModel<EmptyCodable>> otpCreateNew(String ownerId,) async { Future<BaseResponseModel<CreateOTPResponseModel>> otpVerifyForDoingNextEvent(
var deviceKey = await DeviceInfo.getDeviceId(); String ownerId,
final body = {"owner_id": ownerId, "ttl": Constants.otpTtl, "resend_after_second": Constants.otpTtl}; String otp,
String nextEventName,
) async {
final body = {
"owner_id": ownerId,
"otp": otp,
"next_event_name": nextEventName,
"ttdne": 180, // TODO
"ttl": Constants.otpTtl, "resend_after_second": Constants.otpTtl,
};
return requestNormal( return requestNormal(
APIPaths.otpCreateNew, APIPaths.otpVerifyForDoingNextEvent,
Method.POST, Method.POST,
body, body,
(data) => EmptyCodable.fromJson(data as Json), (data) => CreateOTPResponseModel.fromJson(data as Json),
); );
} }
Future<BaseResponseModel<CampaignDetailResponseModel>> websitePageGetDetail(String id) async { Future<BaseResponseModel<CampaignDetailResponseModel>> websitePageGetDetail(String id) async {
final body = {"website_page_id": "18756", "access_token": "",}; final body = {"website_page_id": id};
return requestNormal( return requestNormal(
APIPaths.websitePageGetDetail, APIPaths.websitePageGetDetail,
Method.POST, Method.POST,
...@@ -100,7 +117,7 @@ extension RestfullAPIClientAllApi on RestfulAPIClient { ...@@ -100,7 +117,7 @@ extension RestfullAPIClientAllApi on RestfulAPIClient {
} }
Future<BaseResponseModel<CampaignDetailResponseModel>> websitePage(DetailPageRuleType rule) async { Future<BaseResponseModel<CampaignDetailResponseModel>> websitePage(DetailPageRuleType rule) async {
final body = {"code": rule.key,}; final body = {"code": rule.key};
return requestNormal( return requestNormal(
APIPaths.websitePage, APIPaths.websitePage,
Method.GET, Method.GET,
...@@ -108,4 +125,24 @@ extension RestfullAPIClientAllApi on RestfulAPIClient { ...@@ -108,4 +125,24 @@ extension RestfullAPIClientAllApi on RestfulAPIClient {
(data) => CampaignDetailResponseModel.fromJson(data as Json), (data) => CampaignDetailResponseModel.fromJson(data as Json),
); );
} }
Future<BaseResponseModel<FAQItemModelResponse>> websiteFolderGetPageList() async {
final body = {"folder_uri": "FAQ"};
return requestNormal(
APIPaths.websiteFolderGetPageList,
Method.POST,
body,
(data) => FAQItemModelResponse.fromJson(data as Json),
);
}
Future<BaseResponseModel<EmptyCodable>> accountPasswordReset(String phone, String password) async {
final body = {"login_name": phone, "password": password.toSha256()};
return requestNormal(
APIPaths.accountPasswordReset,
Method.POST,
body,
(data) => EmptyCodable.fromJson(data as Json),
);
}
} }
...@@ -38,13 +38,11 @@ class CreatePasswordViewModel extends GetxController { ...@@ -38,13 +38,11 @@ class CreatePasswordViewModel extends GetxController {
Future<void> onSubmit() async { Future<void> onSubmit() async {
if (!isButtonEnabled.value) return; if (!isButtonEnabled.value) return;
try { try {
final response = await repository.signup(newPassword.value); final response = await repository.setPassword(newPassword.value);
if (response.isSuccess) { if (response.isSuccess) {
errorMessage.value = ""; errorMessage.value = "";
// TODO: Điều hướng sang màn hình tiếp theo
// e.g. Get.offAllNamed("/home");
} else { } else {
errorMessage.value = "Tạo mật khẩu thất bại. Thử lại sau."; errorMessage.value = response.errorMessage ?? "Tạo mật khẩu thất bại. Thử lại sau.";
} }
} catch (e) { } catch (e) {
errorMessage.value = "Có lỗi xảy ra: $e"; errorMessage.value = "Có lỗi xảy ra: $e";
......
import 'package:get/get.dart';
import 'package:mypoint_flutter_app/networking/restful_api_request.dart';
import 'package:mypoint_flutter_app/screen/create_pass/signup_create_password_repository.dart';
import '../../base/base_response_model.dart';
import '../../base/restful_api_viewmodel.dart';
import '../login/login_screen.dart';
import '../splash/splash_screen_viewmodel.dart';
class ResetCreatePasswordRepository extends RestfulApiViewModel implements ICreatePasswordRepository {
@override
late String phoneNumber;
ResetCreatePasswordRepository(this.phoneNumber);
@override
Future<BaseResponseModel<EmptyCodable>> setPassword(String password) async {
showLoading();
return client.accountPasswordReset(phoneNumber, password).then((value) {
hideLoading();
if (value.status == "success" || value.code == 200) {
print("Reset password success");
Get.off(() => LoginScreen(phoneNumber: phoneNumber));
}
return value;
});
}
}
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:mypoint_flutter_app/networking/restful_api_request.dart'; import 'package:mypoint_flutter_app/networking/restful_api_request.dart';
import 'package:mypoint_flutter_app/screen/login/login_screen.dart'; import 'package:mypoint_flutter_app/screen/login/login_screen.dart';
import '../../base/base_response_model.dart'; import '../../base/base_response_model.dart';
...@@ -10,8 +9,7 @@ import '../splash/splash_screen_viewmodel.dart'; ...@@ -10,8 +9,7 @@ import '../splash/splash_screen_viewmodel.dart';
abstract class ICreatePasswordRepository { abstract class ICreatePasswordRepository {
late String phoneNumber; late String phoneNumber;
Future<bool?> createPassword(String newPassword); Future<BaseResponseModel<EmptyCodable>> setPassword(String password);
Future<BaseResponseModel<EmptyCodable>> signup(String password);
} }
class SignUpCreatePasswordRepository extends RestfulApiViewModel implements ICreatePasswordRepository { class SignUpCreatePasswordRepository extends RestfulApiViewModel implements ICreatePasswordRepository {
...@@ -21,7 +19,7 @@ class SignUpCreatePasswordRepository extends RestfulApiViewModel implements ICre ...@@ -21,7 +19,7 @@ class SignUpCreatePasswordRepository extends RestfulApiViewModel implements ICre
SignUpCreatePasswordRepository(this.phoneNumber); SignUpCreatePasswordRepository(this.phoneNumber);
@override @override
Future<BaseResponseModel<EmptyCodable>> signup(String password) async { Future<BaseResponseModel<EmptyCodable>> setPassword(String password) async {
showLoading(); showLoading();
return client.signup(phoneNumber, password).then((value) { return client.signup(phoneNumber, password).then((value) {
hideLoading(); hideLoading();
...@@ -32,10 +30,4 @@ class SignUpCreatePasswordRepository extends RestfulApiViewModel implements ICre ...@@ -32,10 +30,4 @@ class SignUpCreatePasswordRepository extends RestfulApiViewModel implements ICre
return value; return value;
}); });
} }
@override
Future<bool?> createPassword(String newPassword) {
// TODO: implement createPassword
throw UnimplementedError();
}
} }
import 'package:flutter/material.dart';
import 'faqs_model.dart';
class FAQDetailScreen extends StatelessWidget {
final FAQItemModel faqItem;
const FAQDetailScreen({super.key, required this.faqItem});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(faqItem.title ?? "", style: const TextStyle(fontWeight: FontWeight.bold)),
backgroundColor: Colors.white,
foregroundColor: Colors.black,
elevation: 0,
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
faqItem.chapeau ?? "",
style: const TextStyle(fontSize: 16, color: Colors.black87),
),
),
);
}
}
import 'package:json_annotation/json_annotation.dart';
part 'faqs_model.g.dart';
@JsonSerializable()
class FAQItemModel {
final String? thumbnail;
@JsonKey(name: "page_id")
final String? pageId;
final String? title;
@JsonKey(name: "publish_at_date")
final String? publishAtDate;
final String? chapeau;
FAQItemModel({
this.thumbnail,
this.pageId,
this.title,
this.publishAtDate,
this.chapeau,
});
factory FAQItemModel.fromJson(Map<String, dynamic> json) => _$FAQItemModelFromJson(json);
Map<String, dynamic> toJson() => _$FAQItemModelToJson(this);
}
@JsonSerializable()
class FAQItemModelResponse {
final List<FAQItemModel>? items;
FAQItemModelResponse({
this.items,
});
factory FAQItemModelResponse.fromJson(Map<String, dynamic> json) => _$FAQItemModelResponseFromJson(json);
Map<String, dynamic> toJson() => _$FAQItemModelResponseToJson(this);
}
\ No newline at end of file
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'faqs_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
FAQItemModel _$FAQItemModelFromJson(Map<String, dynamic> json) => FAQItemModel(
thumbnail: json['thumbnail'] as String?,
pageId: json['page_id'] as String?,
title: json['title'] as String?,
publishAtDate: json['publish_at_date'] as String?,
chapeau: json['chapeau'] as String?,
);
Map<String, dynamic> _$FAQItemModelToJson(FAQItemModel instance) =>
<String, dynamic>{
'thumbnail': instance.thumbnail,
'page_id': instance.pageId,
'title': instance.title,
'publish_at_date': instance.publishAtDate,
'chapeau': instance.chapeau,
};
FAQItemModelResponse _$FAQItemModelResponseFromJson(
Map<String, dynamic> json,
) => FAQItemModelResponse(
items:
(json['items'] as List<dynamic>?)
?.map((e) => FAQItemModel.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$FAQItemModelResponseToJson(
FAQItemModelResponse instance,
) => <String, dynamic>{'items': instance.items};
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:mypoint_flutter_app/screen/pageDetail/campaign_detail_screen.dart';
import 'package:mypoint_flutter_app/widgets/back_button.dart';
import '../../base/base_screen.dart';
import '../../base/basic_state.dart';
import '../../resouce/base_color.dart';
import 'faqs_viewmodel.dart';
class FAQScreen extends BaseScreen {
const FAQScreen({super.key});
@override
State<FAQScreen> createState() => _FAQScreenState();
}
class _FAQScreenState extends BaseState<FAQScreen> with BasicState {
final FAQViewModel _controller = Get.put(FAQViewModel());
@override
Widget createBody() {
return Scaffold(
appBar: AppBar(
leading: CustomBackButton(),
title: const Text("Câu hỏi thường gặp", style: TextStyle(fontWeight: FontWeight.bold)),
backgroundColor: Colors.white,
foregroundColor: Colors.black,
elevation: 0,
),
body: Column(
children: [
Obx(() {
if (_controller.isLoading.value) {
return const Expanded(child: Center(child: CircularProgressIndicator()));
}
if (_controller.faqItems.isEmpty) {
return const Expanded(child: Center(child: Text("Không có dữ liệu.")));
}
return Expanded(child: _buildFAQList());
}),
],
),
);
}
Widget _buildFAQList() {
return ListView.builder(
itemCount: _controller.faqItems.length,
itemBuilder: (context, index) {
final item = _controller.faqItems[index];
return GestureDetector(
onTap: () {
if (item.pageId != null && item.pageId!.isNotEmpty) {
Get.to(() => CampaignDetailScreen(pageId: item.pageId));
} else {
Get.snackbar(
"Thông báo",
"Không thể mở chi tiết vì thiếu ID!",
backgroundColor: Colors.redAccent,
colorText: Colors.white,
);
}
},
child: Column(
children: [
Container(
color: BaseColor.second200,
child: ListTile(
leading: const Icon(Icons.help_outline, color: BaseColor.second500),
title: Text(
item.title ?? "",
style: const TextStyle(fontWeight: FontWeight.bold, color: BaseColor.second700),
),
trailing: Icon(Icons.arrow_forward_ios, size: 16, color: BaseColor.second500),
),
),
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.only(left: 56, right: 16, bottom: 10), // 👈 56 = icon width + padding mặc định
child: Align(
alignment: Alignment.centerLeft,
child: Text(item.chapeau ?? "", style: const TextStyle(fontSize: 16, color: BaseColor.second500)),
),
),
const SizedBox(height: 8),
],
),
);
},
);
}
}
import 'dart:convert';
import 'package:get/get.dart';
import 'package:mypoint_flutter_app/networking/restful_api_request.dart';
import '../../base/restful_api_viewmodel.dart';
import 'faqs_model.dart';
class FAQViewModel extends RestfulApiViewModel {
var faqItems = <FAQItemModel>[].obs;
var isLoading = true.obs;
@override
void onInit() {
super.onInit();
fetchFAQItems();
}
Future<void> fetchFAQItems() async {
showLoading();
isLoading(true);
client.websiteFolderGetPageList().then((value) {
hideLoading();
isLoading(false);
faqItems.value = value.data?.items ?? [];
});
}
}
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:mypoint_flutter_app/configs/constants.dart';
import 'package:mypoint_flutter_app/networking/restful_api_request.dart'; import 'package:mypoint_flutter_app/networking/restful_api_request.dart';
import 'package:mypoint_flutter_app/screen/onboarding/onboarding_screen.dart'; import 'package:mypoint_flutter_app/screen/onboarding/onboarding_screen.dart';
import 'package:mypoint_flutter_app/screen/otp/forgot_pass_otp_repository.dart';
import 'package:mypoint_flutter_app/screen/otp/otp_screen.dart';
import '../../base/restful_api_viewmodel.dart'; import '../../base/restful_api_viewmodel.dart';
import '../../permission/biometric_manager.dart'; import '../../permission/biometric_manager.dart';
// login_state_enum.dart // login_state_enum.dart
enum LoginState { enum LoginState { idle, typing, done, error }
idle,
typing,
done,
error,
}
class LoginViewModel extends RestfulApiViewModel { class LoginViewModel extends RestfulApiViewModel {
final BiometricManager _biometricManager = BiometricManager(); final BiometricManager _biometricManager = BiometricManager();
...@@ -70,7 +68,14 @@ class LoginViewModel extends RestfulApiViewModel { ...@@ -70,7 +68,14 @@ class LoginViewModel extends RestfulApiViewModel {
showLoading(); showLoading();
client.otpCreateNew(phoneNumber).then((value) { client.otpCreateNew(phoneNumber).then((value) {
hideLoading(); hideLoading();
print(value); // TODO: handle error later
if (value.isSuccess) {
Get.to(
OtpScreen(
repository: ForgotPassOTPRepository(phoneNumber, value.data?.resendAfterSecond ?? Constants.otpTtl),
),
);
}
}); });
} }
...@@ -79,8 +84,7 @@ class LoginViewModel extends RestfulApiViewModel { ...@@ -79,8 +84,7 @@ class LoginViewModel extends RestfulApiViewModel {
// Kiểm tra thiết bị hỗ trợ // Kiểm tra thiết bị hỗ trợ
final canUse = await canUseBiometrics(); final canUse = await canUseBiometrics();
if (!canUse || biometricType.value == BiometricTypeEnum.none) { if (!canUse || biometricType.value == BiometricTypeEnum.none) {
Get.snackbar("Thông báo", "Thiết bị không hỗ trợ sinh trắc học", Get.snackbar("Thông báo", "Thiết bị không hỗ trợ sinh trắc học", snackPosition: SnackPosition.BOTTOM);
snackPosition: SnackPosition.BOTTOM);
return; return;
} }
...@@ -88,7 +92,8 @@ class LoginViewModel extends RestfulApiViewModel { ...@@ -88,7 +92,8 @@ class LoginViewModel extends RestfulApiViewModel {
final success = await _biometricManager.showCustomBiometricDialog( final success = await _biometricManager.showCustomBiometricDialog(
context, context,
title: "Xác thực sinh trắc học", title: "Xác thực sinh trắc học",
content: (biometricType.value == BiometricTypeEnum.faceId) content:
(biometricType.value == BiometricTypeEnum.faceId)
? "Bạn có muốn đăng nhập bằng Face ID không?" ? "Bạn có muốn đăng nhập bằng Face ID không?"
: "Bạn có muốn đăng nhập bằng vân tay không?", : "Bạn có muốn đăng nhập bằng vân tay không?",
confirmText: "Đồng ý", confirmText: "Đồng ý",
......
...@@ -7,6 +7,7 @@ import '../../base/base_screen.dart'; ...@@ -7,6 +7,7 @@ import '../../base/base_screen.dart';
import '../../base/basic_state.dart'; import '../../base/basic_state.dart';
import '../../configs/constants.dart'; import '../../configs/constants.dart';
import '../../resouce/base_color.dart'; import '../../resouce/base_color.dart';
import '../faqs/faqs_screen.dart';
import '../login/login_screen.dart'; import '../login/login_screen.dart';
import '../otp/otp_screen.dart'; import '../otp/otp_screen.dart';
import '../otp/verify_otp_repository.dart'; import '../otp/verify_otp_repository.dart';
...@@ -64,8 +65,8 @@ class _OnboardingScreenState extends BaseState<OnboardingScreen> with BasicState ...@@ -64,8 +65,8 @@ class _OnboardingScreenState extends BaseState<OnboardingScreen> with BasicState
() => OtpScreen( () => OtpScreen(
repository: VerifyOtpRepository( repository: VerifyOtpRepository(
_viewModel.phoneNumber.value, _viewModel.phoneNumber.value,
response!.otpTtl ?? 0, response?.otpTtl ?? 0,
response!.mfaToken ?? "", response?.mfaToken ?? "",
), ),
), ),
); );
...@@ -203,7 +204,7 @@ class _OnboardingScreenState extends BaseState<OnboardingScreen> with BasicState ...@@ -203,7 +204,7 @@ class _OnboardingScreenState extends BaseState<OnboardingScreen> with BasicState
const TextSpan(text: " và "), const TextSpan(text: " và "),
WidgetSpan( WidgetSpan(
child: GestureDetector( child: GestureDetector(
onTap: () => Get.to(CampaignDetailScreen(type: DetailPageRuleType.privacyPolicy)), onTap: () => Get.to(FAQScreen()),// Get.to(CampaignDetailScreen(type: DetailPageRuleType.privacyPolicy)),
child: const Text( child: const Text(
"Chính sách bảo mật", "Chính sách bảo mật",
style: TextStyle( style: TextStyle(
......
import 'package:get/get.dart';
import 'package:get/get_core/src/get_main.dart';
import 'package:mypoint_flutter_app/base/base_response_model.dart';
import 'package:mypoint_flutter_app/networking/restful_api_request.dart';
import 'package:mypoint_flutter_app/screen/create_pass/create_pass_screen.dart';
import 'package:mypoint_flutter_app/screen/create_pass/reset_create_password_repository.dart';
import '../../base/restful_api_viewmodel.dart';
import '../splash/splash_screen_viewmodel.dart';
import 'otp_viewmodel.dart';
class ForgotPassOTPRepository extends RestfulApiViewModel implements IOtpRepository {
ForgotPassOTPRepository(this.phoneNumber, this.otpTtl);
@override
int otpTtl;
@override
String phoneNumber;
@override
Future<int?> resendOtp() {
throw UnimplementedError();
}
@override
Future<void> sendOtp() {
throw UnimplementedError();
}
@override
Future<BaseResponseModel<EmptyCodable>> verifyOtp(String otpCode) {
showLoading();
return client.otpVerifyForDoingNextEvent(phoneNumber, otpCode, "RESET_PASSWORD").then((value) {
hideLoading();
if (value.status == "success" || value.code == 200) {
Get.off(() => CreatePasswordScreen(repository: ResetCreatePasswordRepository(phoneNumber)));
}
return value;
});
}
}
\ No newline at end of file
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import '../../splash/splash_screen_viewmodel.dart';
part 'create_otp_response_model.g.dart'; part 'create_otp_response_model.g.dart';
@JsonSerializable() @JsonSerializable()
class CreateOTPResponseModel { class CreateOTPResponseModel extends EmptyCodable {
@JsonKey(name: 'resend_after_second') @JsonKey(name: 'resend_after_second')
int? resendAfterSecond; int? resendAfterSecond;
CreateOTPResponseModel({ CreateOTPResponseModel({
this.resendAfterSecond, this.resendAfterSecond,
}); }) : super.fromJson(null);
factory CreateOTPResponseModel.fromJson(Map<String, dynamic> json) => _$CreateOTPResponseModelFromJson(json); factory CreateOTPResponseModel.fromJson(Map<String, dynamic> json) => _$CreateOTPResponseModelFromJson(json);
Map<String, dynamic> toJson() => _$CreateOTPResponseModelToJson(this); Map<String, dynamic> toJson() => _$CreateOTPResponseModelToJson(this);
......
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
import '../../splash/splash_screen_viewmodel.dart';
import 'otp_claim_verify_response_model.dart'; import 'otp_claim_verify_response_model.dart';
part 'otp_verify_response_model.g.dart'; part 'otp_verify_response_model.g.dart';
@JsonSerializable() @JsonSerializable()
class OTPVerifyResponseModel { class OTPVerifyResponseModel extends EmptyCodable {
OTPClaimVerifyResponseModel? claim; OTPClaimVerifyResponseModel? claim;
OTPVerifyResponseModel({ OTPVerifyResponseModel({
this.claim, this.claim,
}); }) : super.fromJson(null);
factory OTPVerifyResponseModel.fromJson(Map<String, dynamic> json) => _$OTPVerifyResponseModelFromJson(json); factory OTPVerifyResponseModel.fromJson(Map<String, dynamic> json) => _$OTPVerifyResponseModelFromJson(json);
Map<String, dynamic> toJson() => _$OTPVerifyResponseModelToJson(this); Map<String, dynamic> toJson() => _$OTPVerifyResponseModelToJson(this);
......
...@@ -4,12 +4,13 @@ import 'package:mypoint_flutter_app/base/base_response_model.dart'; ...@@ -4,12 +4,13 @@ import 'package:mypoint_flutter_app/base/base_response_model.dart';
import 'package:mypoint_flutter_app/configs/constants.dart'; import 'package:mypoint_flutter_app/configs/constants.dart';
import 'package:mypoint_flutter_app/screen/create_pass/create_pass_screen.dart'; import 'package:mypoint_flutter_app/screen/create_pass/create_pass_screen.dart';
import 'package:mypoint_flutter_app/screen/login/login_screen.dart'; import 'package:mypoint_flutter_app/screen/login/login_screen.dart';
import 'package:mypoint_flutter_app/screen/splash/splash_screen_viewmodel.dart';
import '../create_pass/signup_create_password_repository.dart'; import '../create_pass/signup_create_password_repository.dart';
import 'model/otp_verify_response_model.dart'; import 'model/otp_verify_response_model.dart';
abstract class IOtpRepository { abstract class IOtpRepository {
Future<void> sendOtp(); Future<void> sendOtp();
Future<BaseResponseModel<OTPVerifyResponseModel>> verifyOtp(String otpCode); Future<BaseResponseModel<EmptyCodable>> verifyOtp(String otpCode);
Future<int?> resendOtp(); Future<int?> resendOtp();
late String phoneNumber; late String phoneNumber;
late int otpTtl; late int otpTtl;
...@@ -40,12 +41,6 @@ class OtpViewModel extends GetxController { ...@@ -40,12 +41,6 @@ class OtpViewModel extends GetxController {
} }
Future<void> sendOtp() async { Future<void> sendOtp() async {
try {
await repository.sendOtp();
startCountdown();
} catch (e) {
errorMessage.value = "Gửi OTP thất bại. Vui lòng thử lại.";
}
} }
void startCountdown() { void startCountdown() {
...@@ -81,14 +76,6 @@ class OtpViewModel extends GetxController { ...@@ -81,14 +76,6 @@ class OtpViewModel extends GetxController {
final response = await repository.verifyOtp(otpCode.value); final response = await repository.verifyOtp(otpCode.value);
if (response.isSuccess) { if (response.isSuccess) {
errorMessage.value = ""; errorMessage.value = "";
if (response.data?.claim?.action == "signup") {
Get.off(() => CreatePasswordScreen(repository: SignUpCreatePasswordRepository(repository.phoneNumber)));
return;
}
if (response.data?.claim?.action == "login") {
Get.off(() => LoginScreen(phoneNumber: repository.phoneNumber));
return;
}
} else { } else {
errorMessage.value = response.errorMessage ?? ""; errorMessage.value = response.errorMessage ?? "";
} }
......
...@@ -4,6 +4,9 @@ import 'package:get/get.dart'; ...@@ -4,6 +4,9 @@ import 'package:get/get.dart';
import 'package:mypoint_flutter_app/networking/restful_api_request.dart'; import 'package:mypoint_flutter_app/networking/restful_api_request.dart';
import '../../base/base_response_model.dart'; import '../../base/base_response_model.dart';
import '../../base/restful_api_viewmodel.dart'; import '../../base/restful_api_viewmodel.dart';
import '../create_pass/create_pass_screen.dart';
import '../create_pass/signup_create_password_repository.dart';
import '../login/login_screen.dart';
import 'model/otp_verify_response_model.dart'; import 'model/otp_verify_response_model.dart';
import 'otp_viewmodel.dart'; import 'otp_viewmodel.dart';
...@@ -24,6 +27,11 @@ class VerifyOtpRepository extends RestfulApiViewModel implements IOtpRepository ...@@ -24,6 +27,11 @@ class VerifyOtpRepository extends RestfulApiViewModel implements IOtpRepository
showLoading(); showLoading();
return client.verifyOTP(otpCode, mfaToken).then((value) { return client.verifyOTP(otpCode, mfaToken).then((value) {
hideLoading(); hideLoading();
if (value.data?.claim?.action == "signup") {
Get.off(() => CreatePasswordScreen(repository: SignUpCreatePasswordRepository(phoneNumber)));
} else if (value.data?.claim?.action == "login") {
Get.off(() => LoginScreen(phoneNumber: phoneNumber));
}
return value; return value;
}); });
} }
......
...@@ -3,6 +3,8 @@ import 'package:flutter_widget_from_html/flutter_widget_from_html.dart'; ...@@ -3,6 +3,8 @@ import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import '../../base/base_screen.dart'; import '../../base/base_screen.dart';
import '../../base/basic_state.dart'; import '../../base/basic_state.dart';
import '../../directional/directional_action_type.dart';
import '../../directional/directional_screen.dart';
import '../../extensions/string_extension.dart'; // tuỳ dự án import '../../extensions/string_extension.dart'; // tuỳ dự án
import '../../resouce/base_color.dart'; import '../../resouce/base_color.dart';
import '../../widgets/back_button.dart'; import '../../widgets/back_button.dart';
...@@ -128,7 +130,9 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba ...@@ -128,7 +130,9 @@ class _CampaignDetailScreenState extends BaseState<CampaignDetailScreen> with Ba
SizedBox(height: 12,), SizedBox(height: 12,),
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
// Xử lý khi bấm nút DirectionalScreen(
clickActionType: pageDetail?.buttonClickActionType,
clickActionParam: pageDetail?.buttonClickActionParam).begin();
}, },
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: parseHexColor(buttonColor), backgroundColor: parseHexColor(buttonColor),
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment