Commit 1edd930e authored by DatHV's avatar DatHV
Browse files

update profile web.

parent a7abbe4d
......@@ -125,4 +125,5 @@ class APIPaths {//sandbox
static const String myProductMarkAsUsed = "/myProductMarkAsUsed/1.0.0";
static const String myProductMarkAsNotUsedYet = "/myProductMarkAsNotUsedYet/1.0.0";
static const String submitCampaignViewVoucherComplete = "/campaign/api/v3.0/view-voucher/complete";
static const String webUpdateProfile = "/user/api/v2.0/account/users/loginPartner/updateProfile";
}
......@@ -21,6 +21,7 @@ class AuthInterceptor extends Interceptor {
APIPaths.refreshToken,
APIPaths.logout,
APIPaths.deleteNotification,
APIPaths.webUpdateProfile,
'assets',
];
......
......@@ -700,4 +700,10 @@ extension RestfulAPIClientAllRequest on RestfulAPIClient {
return SubmitViewVoucherCompletedResponse.fromJson(data as Json);
});
}
Future<BaseResponseModel<EmptyCodable>> webUpdateProfile(Json body) async {
return requestNormal(APIPaths.webUpdateProfile, Method.POST, body, (data) {
return EmptyCodable.fromJson(data as Json);
});
}
}
/// No-op fallbacks for JS interop helpers when building for non-web targets.
F allowInterop<F extends Function>(F callback) => callback;
dynamic dartify(dynamic source) => source;
import 'package:js/js_util.dart' as js_util;
F allowInterop<F extends Function>(F callback) =>
js_util.allowInterop(callback);
dynamic dartify(dynamic source) => js_util.dartify(source);
......@@ -105,10 +105,6 @@ Future<dynamic> webPermissionsRequest(dynamic type) async {
return null;
}
Future<dynamic> webPremissionsRequest(dynamic type) async {
return null;
}
Future<dynamic> webSaveStore(dynamic data) async {
return null;
}
......@@ -121,6 +117,6 @@ Future<dynamic> webClearStore() async {
return null;
}
Future<dynamic> webGetInfo(dynamic key) async {
Future<Map<String, dynamic>?> webGetInfo(dynamic key) async {
return null;
}
......@@ -175,9 +175,7 @@ Future<dynamic> webPaymentRequest(Map<String, dynamic> payload) async {
}
}
Future<VoidCallback?> webListenNotificationEvent(
ValueChanged<dynamic> onEvent,
) async {
Future<VoidCallback?> webListenNotificationEvent(ValueChanged<dynamic> onEvent) async {
try {
return await XAppSDKService().listenNotificationEvent(onEvent);
} catch (e) {
......@@ -186,9 +184,7 @@ Future<VoidCallback?> webListenNotificationEvent(
}
}
Future<VoidCallback?> webListenPaymentEvent(
ValueChanged<dynamic> onEvent,
) async {
Future<VoidCallback?> webListenPaymentEvent(ValueChanged<dynamic> onEvent) async {
try {
return await XAppSDKService().listenPaymentEvent(onEvent);
} catch (e) {
......@@ -206,9 +202,6 @@ Future<dynamic> webPermissionsRequest(dynamic type) async {
}
}
Future<dynamic> webPremissionsRequest(dynamic type) =>
webPermissionsRequest(type);
Future<dynamic> webSaveStore(dynamic data) async {
try {
return await XAppSDKService().saveStore(data);
......@@ -236,7 +229,7 @@ Future<dynamic> webClearStore() async {
}
}
Future<dynamic> webGetInfo(dynamic key) async {
Future<Map<String, dynamic>?> webGetInfo(dynamic key) async {
try {
return await XAppSDKService().getInfo(key);
} catch (e) {
......
import 'package:flutter/foundation.dart';
import 'package:mypoint_flutter_app/core/services/web/web_helper.dart';
class WebData {
WebData._internal();
static final WebData _instance = WebData._internal();
static WebData get instance => _instance;
static const Duration _sdkTimeout = Duration(seconds: 10);
static const String _defaultUserInfoKey = 'USER_ID';
Map<String, dynamic>? _cachedUserInfo;
/// Convenience static getter for cached data.
static Map<String, dynamic>? get cachedUserInfo => _instance._cachedUserInfo;
/// Return cached info if available, otherwise refresh from SDK.
static Future<Map<String, dynamic>?> getInfoFromSDK({
bool forceRefresh = false,
dynamic key,
}) {
return _instance._getInfoFromSDK(forceRefresh: forceRefresh, key: key);
}
/// Refresh user info data and store it in memory.
Future<Map<String, dynamic>?> refreshUserInfo({dynamic key}) =>
_getInfoFromSDK(forceRefresh: true, key: key);
/// Retrieve cached data (without hitting SDK).
Map<String, dynamic>? get userInfo => _cachedUserInfo;
/// Retrieve cached full name (if available).
String? get _fullName => _extractFullName(_cachedUserInfo);
/// Retrieve cached identity number (if available).
String? get _identityNumber => _extractIdentityNumber(_cachedUserInfo);
/// Retrieve cached email (if available).
String? get _email => _extractEmail(_cachedUserInfo);
/// Retrieve cached gender label/value (if available).
String? get _gender => _extractGender(_cachedUserInfo);
/// Retrieve cached address (if available).
String? get _address => _extractAddress(_cachedUserInfo);
/// Retrieve cached birthday (if available).
String? get _birthday => _extractBirthday(_cachedUserInfo);
/// Retrieve cached avatar (if available).
String? get _avatar => _extractAvatar(_cachedUserInfo);
/// Convenience static getter for cached full name.
static String? getFullName() => _instance._fullName;
/// Convenience static getter for cached identity number.
static String? getIdentityNumber() => _instance._identityNumber;
/// Convenience static getter for cached email.
static String? getEmail() => _instance._email;
/// Convenience static getter for cached gender.
static String? getGender() => _instance._gender;
/// Convenience static getter for cached address.
static String? getAddress() => _instance._address;
/// Convenience static getter for cached birthday.
static String? getBirthday() => _instance._birthday;
/// Convenience static getter for cached avatar.
static String? getAvatar() => _instance._avatar;
/// Clear cached SDK data.
void clearUserInfo() {
_cachedUserInfo = null;
}
Future<Map<String, dynamic>?> _getInfoFromSDK({
bool forceRefresh = false,
dynamic key,
}) async {
if (!kIsWeb) return null;
if (!forceRefresh && _cachedUserInfo != null) {
return _cachedUserInfo;
}
try {
debugPrint('🔍 WebInfoData - Requesting info from x-app-sdk...');
final ready = await _ensureSdkReady();
if (!ready) {
debugPrint('⚠️ WebInfoData - SDK not ready');
return null;
}
final response = await webGetInfo(key ?? _defaultUserInfoKey).timeout(_sdkTimeout);
debugPrint('🔍 WebInfoData - getInfo response: $response');
if (response == null || response.isEmpty) {
final error = webGetLastError();
debugPrint('⚠️ WebInfoData - getInfo returned empty payload: $error');
_cachedUserInfo = null;
return null;
}
final status = response['status']?.toString().toLowerCase();
if (status != null && status != 'success') {
debugPrint('⚠️ WebInfoData - getInfo returned non-success status: $status');
_cachedUserInfo = null;
return null;
}
final userInfo = _mapFromDynamic(response['data']);
if (userInfo != null && userInfo.isNotEmpty) {
_cachedUserInfo = userInfo;
debugPrint('✅ WebInfoData - User info cached from x-app-sdk');
return _cachedUserInfo;
}
debugPrint('⚠️ WebInfoData - getInfo returned missing profile data');
_cachedUserInfo = null;
} catch (e) {
debugPrint('❌ WebInfoData - Error getting profile from SDK: $e');
_cachedUserInfo = null;
}
return null;
}
static Future<bool> _ensureSdkReady() async {
try {
await webInitializeXAppSDK().timeout(_sdkTimeout);
} catch (e) {
debugPrint('⚠️ WebInfoData - SDK init attempt failed: $e');
}
final ready = webIsSDKInitialized();
return ready;
}
static Map<String, dynamic>? _mapFromDynamic(dynamic source) {
if (source == null) return null;
if (source is Map<String, dynamic>) {
return source.map((key, value) => MapEntry(key, _normalizeDynamicValue(value)));
}
if (source is Map) {
final mapped = <String, dynamic>{};
source.forEach((key, dynamic value) {
if (key == null) {
return;
}
mapped[key.toString()] = _normalizeDynamicValue(value);
});
return mapped;
}
return null;
}
static dynamic _normalizeDynamicValue(dynamic value) {
if (value is Map) {
return _mapFromDynamic(value);
}
if (value is Iterable) {
return List<dynamic>.from(value.map(_normalizeDynamicValue));
}
return value;
}
String? _extractFullName(Map<String, dynamic>? data) {
if (data == null || data.isEmpty) {
return null;
}
final possibleKeys = <String>[
'Full_Name',
'FullName',
'fullName',
'fullname',
'full_name',
'name',
];
for (final key in possibleKeys) {
final value = data[key];
if (value is String && value.trim().isNotEmpty) {
return value.trim();
}
}
final workerSite = data['worker_site'];
if (workerSite is Map) {
final nestedName = _extractFullName(_mapFromDynamic(workerSite));
if (nestedName != null && nestedName.isNotEmpty) {
return nestedName;
}
}
return null;
}
String? _extractIdentityNumber(Map<String, dynamic>? data) {
if (data == null || data.isEmpty) {
return null;
}
const possibleKeys = <String>[
'identityNumber',
'identity_number',
'identificationNumber',
'identification_number',
'idNumber',
'id_number',
];
for (final key in possibleKeys) {
final value = data[key];
if (value is String && value.trim().isNotEmpty) {
return value.trim();
}
}
final workerSite = data['worker_site'];
if (workerSite is Map) {
final nestedIdentity = _extractIdentityNumber(_mapFromDynamic(workerSite));
if (nestedIdentity != null && nestedIdentity.isNotEmpty) {
return nestedIdentity;
}
}
return null;
}
String? _extractEmail(Map<String, dynamic>? data) {
if (data == null || data.isEmpty) {
return null;
}
const possibleKeys = <String>[
'email',
'Email',
'userEmail',
'user_email',
];
for (final key in possibleKeys) {
final value = data[key];
if (value is String && value.trim().isNotEmpty) {
return value.trim();
}
}
final workerSite = data['worker_site'];
if (workerSite is Map) {
final nestedEmail = _extractEmail(_mapFromDynamic(workerSite));
if (nestedEmail != null && nestedEmail.isNotEmpty) {
return nestedEmail;
}
}
return null;
}
String? _extractGender(Map<String, dynamic>? data) {
if (data == null || data.isEmpty) {
return null;
}
const possibleKeys = <String>[
'gender',
'Gender',
'sex',
'sexLabel',
'sex_label',
];
for (final key in possibleKeys) {
final value = data[key];
if (value == null) {
continue;
}
if (value is String && value.trim().isNotEmpty) {
return value.trim();
}
if (value is num) {
return value.toString();
}
}
final workerSite = data['worker_site'];
if (workerSite is Map) {
final nestedGender = _extractGender(_mapFromDynamic(workerSite));
if (nestedGender != null && nestedGender.isNotEmpty) {
return nestedGender;
}
}
return null;
}
String? _extractAddress(Map<String, dynamic>? data) {
if (data == null || data.isEmpty) {
return null;
}
const possibleKeys = <String>[
'address',
'Address',
'addressFull',
'address_full',
'permanentAddress',
'permanent_address',
];
for (final key in possibleKeys) {
final value = data[key];
if (value is String && value.trim().isNotEmpty) {
return value.trim();
}
}
final workerSite = data['worker_site'];
if (workerSite is Map) {
final nestedAddress = _extractAddress(_mapFromDynamic(workerSite));
if (nestedAddress != null && nestedAddress.isNotEmpty) {
return nestedAddress;
}
}
return null;
}
String? _extractBirthday(Map<String, dynamic>? data) {
if (data == null || data.isEmpty) {
return null;
}
const possibleKeys = <String>[
'birthday',
'birthDay',
'birth_day',
'dob',
'dateOfBirth',
'date_of_birth',
];
for (final key in possibleKeys) {
final value = data[key];
if (value is String && value.trim().isNotEmpty) {
return value.trim();
}
}
final workerSite = data['worker_site'];
if (workerSite is Map) {
final nestedBirthday = _extractBirthday(_mapFromDynamic(workerSite));
if (nestedBirthday != null && nestedBirthday.isNotEmpty) {
return nestedBirthday;
}
}
return null;
}
String? _extractAvatar(Map<String, dynamic>? data) {
if (data == null || data.isEmpty) {
return null;
}
const possibleKeys = <String>[
'avatar',
'avatarUrl',
'avatarURL',
'avatar_url',
'avatar2',
'avatar_2',
'profileImage',
'profile_image',
];
for (final key in possibleKeys) {
final value = data[key];
if (value is String && value.trim().isNotEmpty) {
return value.trim();
}
}
final workerSite = data['worker_site'];
if (workerSite is Map) {
final nestedAvatar = _extractAvatar(_mapFromDynamic(workerSite));
if (nestedAvatar != null && nestedAvatar.isNotEmpty) {
return nestedAvatar;
}
}
final workingSite = data['working_site'];
if (workingSite is Map) {
final nestedAvatar = _extractAvatar(_mapFromDynamic(workingSite));
if (nestedAvatar != null && nestedAvatar.isNotEmpty) {
return nestedAvatar;
}
}
return null;
}
}
import 'package:flutter/foundation.dart';
import 'package:mypoint_flutter_app/core/utils/extensions/string_extension.dart';
import 'package:universal_html/html.dart' as html;
import 'package:universal_html/js_util.dart';
import 'js_interop_stub.dart'
if (dart.library.html) 'js_interop_web.dart' as js_interop;
/// X-App-SDK Service for Flutter Web
/// Provides integration with x-app-sdk module and exposes supported APIs to Dart
class XAppSDKService {
......@@ -16,6 +20,14 @@ class XAppSDKService {
final Set<dynamic> _listenerDisposers = <dynamic>{};
bool _browserMode = false;
static bool isBrowserMode() => _instance._browserMode;
static const Map<String, String> _infoKeyPropertyMap = <String, String>{
'user': 'USER',
'userid': 'USER',
'user_id': 'USER',
};
/// Check if SDK is available and initialized
bool get isInitialized => _isInitialized;
......@@ -58,32 +70,25 @@ class XAppSDKService {
if (!await _ensureSdkReady()) {
return null;
}
debugPrint('🔍 XAppSDKService: Getting token...');
try {
final result = await _invokeSdkMethod(
'getToken',
ensureInitialized: false,
);
final result = await _invokeSdkMethod('getToken', ensureInitialized: false);
if (result != null) {
final status = getProperty(result, 'status')?.toString();
final data = getProperty(result, 'data');
final message = getProperty(result, 'message');
final errorMessage = message?.toString();
if (status == 'success' && data != null && data.toString().isNotEmpty) {
final tokenString = data.toString();
final tokenString = data?.toString().orEmpty ?? '';
final isPlaceholder = tokenString.trim().toLowerCase().contains("tokenex.tokenex");
print('🔍 XAppSDKService: getToken result - status: $status, token length: ${tokenString}, isPlaceholder: $isPlaceholder');
if (status == 'success' && data != null && tokenString.isNotEmpty && !isPlaceholder) {
_cachedToken = tokenString;
_lastError = null;
final preview =
tokenString.length > 8 ? tokenString.substring(0, 8) : tokenString;
debugPrint(
'✅ XAppSDKService: Token retrieved successfully: $preview...');
final preview = tokenString.length > 8 ? tokenString.substring(0, 8) : tokenString;
debugPrint('✅ XAppSDKService: Token retrieved successfully: $preview...');
return _cachedToken;
} else {
final details =
errorMessage?.isNotEmpty == true ? ' - $errorMessage' : '';
final details = errorMessage?.isNotEmpty == true ? ' - $errorMessage' : '';
_lastError = 'SDK returned status: $status$details';
debugPrint('❌ XAppSDKService: $_lastError');
}
......@@ -95,7 +100,6 @@ class XAppSDKService {
_lastError = 'Error getting token: $e';
debugPrint('❌ XAppSDKService: $_lastError');
}
return null;
}
......@@ -179,7 +183,7 @@ class XAppSDKService {
final disposer = await _invokeAfterEnsure(
'listenNotifiactionEvent',
args: <dynamic>[
allowInterop((dynamic event) {
js_interop.allowInterop((dynamic event) {
try {
onEvent(event);
} catch (error, stackTrace) {
......@@ -201,7 +205,7 @@ class XAppSDKService {
final disposer = await _invokeAfterEnsure(
'listenPaymentEvent',
args: <dynamic>[
allowInterop((dynamic event) {
js_interop.allowInterop((dynamic event) {
try {
onEvent(event);
} catch (error, stackTrace) {
......@@ -233,9 +237,47 @@ class XAppSDKService {
/// Clear Super App store data
Future<dynamic> clearStore() => _invokeAfterEnsure('clearStore');
/// Get user info
Future<dynamic> getInfo(dynamic key) =>
_invokeAfterEnsure('getInfo', args: <dynamic>[key]);
/// Get user info via SDK (returns the raw response map)
Future<Map<String, dynamic>?> getInfo(dynamic key) async {
if (!await _ensureSdkReady()) {
return null;
}
debugPrint('🔍 XAppSDKService: Getting info for key: $key');
try {
final resolvedKey = await _resolveInfoKeyValue(key);
final result = await _invokeSdkMethod(
'getInfo',
args: <dynamic>[resolvedKey ?? key],
ensureInitialized: false,
);
if (result == null) {
_lastError ??= 'getInfo returned null';
debugPrint('❌ XAppSDKService: $_lastError');
return null;
}
var infoMap = _convertDynamicToMap(result);
if (infoMap == null || infoMap.isEmpty) {
_lastError = 'getInfo returned invalid data';
debugPrint('❌ XAppSDKService: $_lastError');
return null;
}
if (_browserMode) {
infoMap = _sanitizeBrowserInfoData(infoMap);
debugPrint('ℹ️ XAppSDKService: Browser mode detected - returning empty info data');
}
_lastError = null;
debugPrint('✅ XAppSDKService: Info retrieved for $key');
return infoMap;
} catch (e) {
_lastError = 'Error getting info: $e';
debugPrint('❌ XAppSDKService: $_lastError');
}
return null;
}
/// Clear cached token
void clearToken() {
......@@ -529,4 +571,96 @@ class XAppSDKService {
} catch (_) {}
return false;
}
Future<dynamic> _resolveInfoKeyValue(dynamic key) async {
if (key == null) {
return null;
}
if (key is! String) {
return key;
}
final normalized = key.trim();
if (normalized.isEmpty) {
return normalized;
}
final candidates = <String>[];
final lower = normalized.toLowerCase();
candidates.add(normalized);
final upper = normalized.toUpperCase();
if (upper != normalized) {
candidates.add(upper);
}
if (lower != normalized && lower != upper) {
candidates.add(lower);
}
final aliasProperty = _infoKeyPropertyMap[lower];
if (aliasProperty != null && !candidates.contains(aliasProperty)) {
candidates.add(aliasProperty);
}
final module = await _loadSdkModule();
if (module != null) {
try {
final infoEnum = getProperty(module, 'EKeyInfor');
if (infoEnum != null) {
for (final candidate in candidates) {
try {
final value = getProperty(infoEnum, candidate);
if (value != null) {
return value;
}
} catch (_) {}
}
}
} catch (_) {}
}
return normalized;
}
Map<String, dynamic>? _convertDynamicToMap(dynamic source) {
if (source == null) {
return null;
}
if (source is Map<String, dynamic>) {
return source.map((key, value) => MapEntry(key, _normalizeDynamicValue(value)));
}
if (source is Map) {
final normalized = <String, dynamic>{};
source.forEach((key, dynamic value) {
if (key == null) {
return;
}
normalized[key.toString()] = _normalizeDynamicValue(value);
});
return normalized;
}
try {
final dartValue = js_interop.dartify(source);
if (dartValue is Map) {
return _convertDynamicToMap(dartValue);
}
} catch (_) {}
return null;
}
dynamic _normalizeDynamicValue(dynamic value) {
if (value is Map) {
return _convertDynamicToMap(value);
}
if (value is Iterable) {
return List<dynamic>.from(value.map(_normalizeDynamicValue));
}
return value;
}
Map<String, dynamic> _sanitizeBrowserInfoData(Map<String, dynamic> payload) {
final sanitized = Map<String, dynamic>.from(payload);
if (sanitized.containsKey('data')) {
sanitized['data'] = <String, dynamic>{};
return sanitized;
}
return <String, dynamic>{};
}
}
......@@ -66,10 +66,13 @@ class HomeGreetingHeader extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
GestureDetector(
onTap: _onProfileTap,
child: Text(
'Xin chào $name!',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black87),
),
),
Row(
children: [
GestureDetector(
......@@ -152,6 +155,10 @@ class HomeGreetingHeader extends StatelessWidget {
);
}
void _onProfileTap() {
Get.toNamed(personalEditScreen);
}
void _onSearchTap() {
Get.toNamed(vouchersScreen, arguments: {"enableSearch": true});
}
......
......@@ -65,7 +65,6 @@ class _HoverViewState extends State<HoverView> {
if (!mounted) return;
setState(() {
_remainingSeconds.value--;
debugPrint("Remaining seconds: ${_remainingSeconds.value}");
if (_remainingSeconds.value <= 0) _timer?.cancel();
});
});
......
......@@ -4,6 +4,8 @@ import 'package:mypoint_flutter_app/shared/preferences/data_preference.dart';
import 'package:mypoint_flutter_app/features/personal/personal_edit_item_model.dart';
import 'package:mypoint_flutter_app/features/personal/personal_edit_viewmodel.dart';
import 'package:mypoint_flutter_app/features/personal/personal_gender.dart';
import 'package:mypoint_flutter_app/shared/widgets/image_loader.dart';
import '../../core/services/web/web_info_data.dart';
import '../../shared/widgets/base_view/base_screen.dart';
import '../../shared/widgets/base_view/basic_state.dart';
import '../../core/theme/base_color.dart';
......@@ -116,11 +118,20 @@ class _PersonalEditScreenState extends BaseState<PersonalEditScreen> with BasicS
}
Widget _buildAvatarItem() {
final avatar = WebData.getAvatar();
return Center(
child: Stack(
alignment: Alignment.bottomRight,
children: [
ClipOval(child: Image.asset("assets/images/bg_default_11.png", width: 100, height: 100, fit: BoxFit.cover)),
ClipOval(
child: loadNetworkImage(
url: avatar,
width: 100,
height: 100,
fit: BoxFit.cover,
placeholderAsset: "assets/images/bg_default_11.png"
)
),
Positioned(
bottom: 4,
right: 4,
......
......@@ -5,6 +5,7 @@ import 'package:mypoint_flutter_app/features/personal/personal_edit_item_model.d
import 'package:mypoint_flutter_app/features/personal/personal_gender.dart';
import '../../core/network/restful_api_viewmodel.dart';
import '../../app/config/constants.dart';
import '../../core/services/web/web_info_data.dart';
import '../../shared/preferences/data_preference.dart';
import '../../shared/router_gage.dart';
import '../../core/utils/validation_utils.dart';
......@@ -35,17 +36,28 @@ class PersonalEditViewModel extends RestfulApiViewModel {
);
birthday = profile.workerSite?.birthday?.toDateFormat('yyyy-MM-dd');
gender = PersonalGender.from(profile.workerSite?.sex ?? "U");
// var webGender = WebData.getGender().orEmpty;
// if (webGender.isNotEmpty) {
// gender = PersonalGender.fromInt(webGender);
// }
// var name = WebData.getFullName() ?? profile.workerSite?.fullname ?? "";
// var email = WebData.getEmail() ?? profile.workerSite?.email;
// var address = WebData.getAddress() ?? profile.workerSite?.addressFull;
// var identification = WebData.getIdentityNumber() ?? profile.workerSite?.identificationNumber;
var name = profile.workerSite?.fullname ?? "";
var email = profile.workerSite?.email;
var address = profile.workerSite?.addressFull;
var identification = profile.workerSite?.identificationNumber;
editDataModel.value = PersonalEditDataModel(
name: name,
nickname: profile.workerSite?.nickname,
phone: profile.workerSite?.phoneNumber,
email: profile.workerSite?.email,
identificationNumber: profile.workerSite?.identificationNumber,
email: email,
identificationNumber: identification,
birthday: birthday,
gender: gender,
address: profile.workerSite?.addressFull,
address: address,
province: province.value,
district: district.value,
);
......
......@@ -14,6 +14,17 @@ enum PersonalGender {
}
}
static PersonalGender fromInt(String gender) {
switch (gender) {
case '1':
return PersonalGender.female;
case '0':
return PersonalGender.male;
default:
return PersonalGender.unknown;
}
}
String get value {
switch (this) {
case PersonalGender.female:
......
......@@ -6,6 +6,7 @@ import 'package:mypoint_flutter_app/core/utils/extensions/num_extension.dart';
import 'package:mypoint_flutter_app/shared/preferences/data_preference.dart';
import 'package:mypoint_flutter_app/shared/preferences/point/point_manager.dart';
import '../../app/config/constants.dart';
import '../../core/services/web/web_info_data.dart';
import '../../shared/widgets/base_view/base_screen.dart';
import '../../shared/widgets/base_view/basic_state.dart';
import '../../app/routing/directional_action_type.dart';
......@@ -14,6 +15,7 @@ import '../../core/theme/base_color.dart';
import '../../shared/router_gage.dart';
import '../../core/services/logout_service.dart';
import '../../shared/widgets/alert/data_alert_model.dart';
import '../../shared/widgets/image_loader.dart';
import '../home/header_home_viewmodel.dart';
import '../home/models/header_home_model.dart';
import '../popup_manager/popup_runner_helper.dart';
......@@ -78,6 +80,7 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
final level = DataPreference.instance.rankName ?? "Hạng Đồng";
final email = DataPreference.instance.profile?.workerSite?.email ?? "";
final topWebPadding = Constants.extendTopPaddingNavigation;
final avatar = WebData.getAvatar();
return Container(
height: width * 163 / 375 + topWebPadding,
decoration: BoxDecoration(image: DecorationImage(image: NetworkImage(data.background ?? ""), fit: BoxFit.cover)),
......@@ -102,7 +105,13 @@ class _PersonalScreenState extends BaseState<PersonalScreen> with BasicState, Po
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2),
),
child: ClipOval(child: Image.asset("assets/images/ic_logo.png", width: 64, height: 64)),
child: ClipOval(
child: loadNetworkImage(
url: avatar,
fit: BoxFit.cover,
placeholderAsset: "assets/images/ic_logo.png"
)
),
),
const SizedBox(width: 8),
Column(
......
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';
import 'package:mypoint_flutter_app/app/routing/app_navigator.dart';
import 'package:mypoint_flutter_app/core/utils/extensions/string_extension.dart';
import 'package:mypoint_flutter_app/core/network/restful_api_viewmodel.dart';
import 'package:mypoint_flutter_app/core/network/restful_api_client_all_request.dart';
import 'package:mypoint_flutter_app/features/register_campaign/model/registration_form_package_model.dart';
import 'package:mypoint_flutter_app/shared/router_gage.dart';
import '../../core/services/web/web_info_data.dart';
import '../../core/services/web/x_app_sdk_service.dart';
import '../login/model/login_token_response_model.dart';
import '../personal/model/profile_response_model.dart';
import 'models/update_response_model.dart';
......@@ -21,6 +25,7 @@ class SplashScreenViewModel extends RestfulApiViewModel {
static const Duration _sdkTimeout = Duration(seconds: 10);
Future<void> checkUpdateApp() async {
debugPrint('🔍 SplashScreen - Checking for app update... ${DateTime.now().toString()}');
if (kIsWeb) {
checkUpdateResponse?.call(null);
return;
......@@ -49,6 +54,7 @@ class SplashScreenViewModel extends RestfulApiViewModel {
final token = await _getTokenFromSDK();
if (token.orEmpty.isNotEmpty) {
await DataPreference.instance.saveLoginToken(LoginTokenResponseModel(accessToken: token));
await _cacheProfileFromSDK();
}
}
if (DataPreference.instance.logged) {
......@@ -92,6 +98,22 @@ class SplashScreenViewModel extends RestfulApiViewModel {
}
}
Future<void> _cacheProfileFromSDK() async {
if (!kIsWeb) return;
try {
final sdkProfile = await WebData.getInfoFromSDK(forceRefresh: true);
if (sdkProfile == null || sdkProfile.isEmpty) {
debugPrint('⚠️ SplashScreen - No profile data from x-app-sdk');
return;
}
debugPrint('✅ SplashScreen - Cached user profile from x-app-sdk: $sdkProfile');
final response = client.webUpdateProfile({'metadata': sdkProfile.toJsonString()});
debugPrint('🔍 SplashScreen - webUpdateProfile response: ${response.toString()}');
} catch (e) {
debugPrint('⚠️ SplashScreen - Failed to cache SDK profile: $e');
}
}
Future<void> _loadProfileAndNavigate() async {
final profile = await _fetchUserProfile();
if (profile == null) {
......@@ -104,14 +126,27 @@ class SplashScreenViewModel extends RestfulApiViewModel {
}
Future<void> directionWhenTokenInvalid() async {
// if (kIsWeb) {
// print('❌ No token found on web, closing app');
// final closeSuccess = await webCloseApp({
// 'message': 'No token found, cannot proceed',
// 'timestamp': DateTime.now().millisecondsSinceEpoch,
// });
// if (closeSuccess) return;
// }
if (kIsWeb && !XAppSDKService.isBrowserMode()) {
debugPrint('❌ No token found on web platform. directionWhenTokenInvalid');
Future.microtask(() {
AppNavigator.showAlertError(
content: "MyPoint cần thông tin của bạn.\nSố điện thoại được sử dụng để định danh và sử dụng các tính năng trên MyPoint.",
onConfirmed: () async {
final closeSuccess = await webCloseApp({
'message': 'User acknowledged missing token',
'timestamp': DateTime
.now()
.millisecondsSinceEpoch,
});
debugPrint('❌ No token found on web, user acknowledged. Close app success: $closeSuccess');
if (!closeSuccess) {
Get.offAllNamed(onboardingScreen);
}
}
);
});
return;
}
final phone = DataPreference.instance.phoneNumberUsedForLoginScreen;
final displayName = DataPreference.instance.displayName;
if (phone.isEmpty) {
......
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../core/services/web/web_info_data.dart';
import '../../features/login/model/login_token_response_model.dart';
import '../../features/personal/model/profile_response_model.dart';
import '../../features/popup_manager/popup_manager_viewmodel.dart';
......@@ -46,8 +47,12 @@ class DataPreference {
String get displayName {
var name = _profile?.workerSite?.fullname ?? "";
if (name.isEmpty) {
if (kIsWeb) {
name = WebData.getFullName() ?? "Quý Khách";
} else {
name = "Quý Khách";
}
}
return name;
}
String? get rankName => _profile?.workingSite?.primaryMembership?.membershipLevel?.levelName ?? "";
......
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