630 lines
21 KiB
Dart
630 lines
21 KiB
Dart
import 'dart:convert';
|
|
import 'package:birzha/components/tabview.dart';
|
|
import 'package:birzha/models/chatroom/chatroom.dart';
|
|
import 'package:birzha/models/products/composableProduct.dart';
|
|
import 'package:birzha/models/products/my_product.dart';
|
|
import 'package:birzha/models/settings/settingsModel.dart';
|
|
import 'package:birzha/services/helpers.dart';
|
|
import 'package:birzha/services/imageUpload.dart';
|
|
import 'package:birzha/services/modals.dart';
|
|
import 'package:birzha/services/requests.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/scheduler.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:birzha/models/exceptions/exception.dart';
|
|
import 'package:birzha/models/user/user.dart';
|
|
import 'package:birzha/core/manager/manager.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:file_picker/file_picker.dart' as picker;
|
|
import 'dart:async';
|
|
import 'dart:io';
|
|
import 'package:async/async.dart';
|
|
|
|
class AppUserManager extends Manager<User> {
|
|
static AppUserManager of(BuildContext context, {bool listen = false}) {
|
|
return Provider.of<AppUserManager>(context, listen: listen);
|
|
}
|
|
|
|
AppUserManager(SharedPreferences prefs) : super(User.spawnFromPrefs(prefs));
|
|
|
|
void checkCode(BuildContext context, String code) {
|
|
addTask(
|
|
Task(
|
|
computation: () async {
|
|
try {
|
|
var response = await _checkSmsCode(dataSync.token ?? "", code);
|
|
|
|
var decoded = jsonDecode(response.body);
|
|
|
|
if (decoded is Map) {
|
|
final success = decoded["status"];
|
|
final message = decoded['message']?['backendCode'.translation];
|
|
|
|
if (!success && message != null && message is String) {
|
|
throw MessageException(message);
|
|
} else if (!success && message is! String) {
|
|
throw OtherException();
|
|
}
|
|
}
|
|
|
|
SchedulerBinding.instance?.addPostFrameCallback((timeStamp) {
|
|
syncAccount(context);
|
|
});
|
|
|
|
return dataSync.copy;
|
|
} catch (ex) {
|
|
AppExceptions.exceptionHandler(context, ex);
|
|
throw ex;
|
|
}
|
|
},
|
|
key: 'sms_verify',
|
|
),
|
|
);
|
|
}
|
|
|
|
void syncAccount(BuildContext context) {
|
|
addTask(
|
|
Task(
|
|
computation: () async {
|
|
try {
|
|
var newSample = dataSync.copy;
|
|
if (newSample.isRegistered) {
|
|
var userAccountResponse = await _getAccountRequest(newSample.token!);
|
|
var decoded = jsonDecode(userAccountResponse.body);
|
|
if (decoded['me'] != null) newSample.jSON = {...newSample.jSON, ...decoded['me']};
|
|
}
|
|
return newSample;
|
|
} catch (ex) {
|
|
AppExceptions.exceptionHandler(context, ex);
|
|
throw ex;
|
|
}
|
|
},
|
|
key: 'sync',
|
|
),
|
|
);
|
|
}
|
|
|
|
void sendSmsCode(BuildContext context, VoidCallback onSuccess) {
|
|
addTask(
|
|
Task(
|
|
computation: () async {
|
|
try {
|
|
debugPrint('dataSync.token: ${dataSync.token}');
|
|
|
|
var response = await _sendSmsCode(dataSync.token ?? "");
|
|
|
|
var decoded = jsonDecode(response.body);
|
|
bool success = decoded['result'] == 0 || decoded['result'] == '0';
|
|
if (success) {
|
|
onSuccess();
|
|
return dataSync.copy;
|
|
} else {
|
|
var message = decoded['message']?['backendCode'.translation];
|
|
if (message != null && message is String) {
|
|
throw MessageException(message);
|
|
} else {
|
|
throw OtherException();
|
|
}
|
|
}
|
|
} catch (ex) {
|
|
AppExceptions.exceptionHandler(context, ex);
|
|
throw ex;
|
|
}
|
|
},
|
|
key: 'send_sms'),
|
|
);
|
|
}
|
|
|
|
void verifyMail(BuildContext context) {
|
|
addTask(Task(
|
|
computation: () async {
|
|
try {
|
|
print('backendCode'.translation);
|
|
String? message;
|
|
var response = await _verifyEmailRequest(dataSync.token ?? "");
|
|
var decoded = jsonDecode(response.body);
|
|
print(decoded);
|
|
if (decoded is String) {
|
|
message = decoded;
|
|
}
|
|
showSnackBar(
|
|
context,
|
|
content: message ?? 'verificationMailSent'.translation,
|
|
backgroundColor: Colors.blue,
|
|
textColor: Colors.white,
|
|
duration: const Duration(seconds: 10),
|
|
);
|
|
return dataSync.copy;
|
|
} catch (ex) {
|
|
AppExceptions.exceptionHandler(context, ex);
|
|
throw ex;
|
|
}
|
|
},
|
|
key: 'verify_mail'));
|
|
}
|
|
|
|
void login(BuildContext context, SampleUser user) {
|
|
addTask(
|
|
Task(
|
|
computation: () async {
|
|
try {
|
|
var response = await _loginRequest(user);
|
|
var decoded = jsonDecode(response.body);
|
|
if (decoded['user'] == null)
|
|
throw AppExceptions.recognizer(decoded);
|
|
else {
|
|
var newSample = SampleUser(data: {...decoded['user'], 'token': decoded['token']});
|
|
if (newSample.isRegistered) {
|
|
var userAccountResponse = await _getAccountRequest(newSample.token!);
|
|
var decoded = jsonDecode(userAccountResponse.body);
|
|
if (decoded['me'] != null) newSample.jSON = {...newSample.jSON, ...decoded['me']};
|
|
}
|
|
return newSample.castToRealUser();
|
|
}
|
|
} catch (ex) {
|
|
AppExceptions.exceptionHandler(context, ex);
|
|
throw ex;
|
|
}
|
|
},
|
|
key: 'login',
|
|
),
|
|
);
|
|
}
|
|
|
|
void register(BuildContext context, SampleUser user) {
|
|
addTask(Task(
|
|
computation: () async {
|
|
try {
|
|
var response = await _registerRequest(user);
|
|
var decoded = jsonDecode(response.body);
|
|
if (decoded['user'] == null)
|
|
throw AppExceptions.recognizer(decoded);
|
|
else {
|
|
var newSample = SampleUser(data: {...decoded['user'], 'token': decoded['token']});
|
|
if (newSample.isRegistered) {
|
|
var userAccountResponse = await _getAccountRequest(newSample.token!);
|
|
var decoded = jsonDecode(userAccountResponse.body);
|
|
if (decoded['me'] != null) newSample.jSON = {...newSample.jSON, ...decoded['me']};
|
|
}
|
|
return newSample.castToRealUser();
|
|
}
|
|
} catch (ex) {
|
|
AppExceptions.exceptionHandler(context, ex);
|
|
throw ex;
|
|
}
|
|
},
|
|
key: 'register'));
|
|
}
|
|
|
|
void logout() async {
|
|
addTask(Task(
|
|
computation: () {
|
|
return SynchronousFuture(SampleUser(data: {}));
|
|
},
|
|
key: 'logout'));
|
|
}
|
|
|
|
void update(BuildContext context, SampleUser user) {
|
|
addTask(
|
|
Task(
|
|
computation: () async {
|
|
try {
|
|
debugPrint('sample user $user');
|
|
|
|
var response = await _updateRequest(user, dataSync.token!);
|
|
|
|
debugPrint('response: $response');
|
|
|
|
var decoded = jsonDecode(response.body);
|
|
debugPrint('decoded: $decoded');
|
|
|
|
if (decoded['me'] == null)
|
|
throw AppExceptions.recognizer(decoded);
|
|
else {
|
|
var newSample = SampleUser(data: {
|
|
...dataSync.jSON,
|
|
...decoded['me'],
|
|
});
|
|
|
|
SchedulerBinding.instance?.addPostFrameCallback((timeStamp) {
|
|
syncAccount(context);
|
|
});
|
|
|
|
return newSample.castToRealUser();
|
|
}
|
|
} catch (ex) {
|
|
AppExceptions.exceptionHandler(context, ex);
|
|
throw ex;
|
|
}
|
|
},
|
|
key: 'update',
|
|
),
|
|
);
|
|
}
|
|
|
|
void balanceUp(BuildContext context, String cardType, double amount) {
|
|
addTask(
|
|
Task(
|
|
computation: () async {
|
|
try {
|
|
var response = await _balanceUpRequest(dataSync.token!, cardType, amount);
|
|
var decoded = jsonDecode(response.body);
|
|
|
|
var status = decoded['formUrl'] != null && decoded['formUrl'] is String;
|
|
if (!status)
|
|
throw AppExceptions.recognizer(decoded);
|
|
else
|
|
linkLauncher(decoded['formUrl']);
|
|
|
|
return dataSync.copy;
|
|
} catch (ex) {
|
|
AppExceptions.exceptionHandler(context, ex);
|
|
throw ex;
|
|
}
|
|
},
|
|
key: 'balanceUp',
|
|
),
|
|
);
|
|
}
|
|
|
|
void uploadBill(BuildContext context, picker.PlatformFile? file) {
|
|
addTask(
|
|
Task(
|
|
computation: () async {
|
|
try {
|
|
String? message;
|
|
if (file != null) {
|
|
debugPrint('file ${file.name}');
|
|
message = await uploadImage(
|
|
baseUrl(path: kApiPath + '/balance_update'),
|
|
dataSync,
|
|
file, //fileResult!.files.first,
|
|
'bank_file',
|
|
{
|
|
'type': 'bank',
|
|
},
|
|
onUploadProgressCallback: (a, b) {
|
|
debugPrint('a $a');
|
|
debugPrint('b $b');
|
|
},
|
|
);
|
|
}
|
|
|
|
debugPrint('message $message');
|
|
if (message != null) {
|
|
showSnackBar(
|
|
context,
|
|
content: message,
|
|
backgroundColor: Colors.blue,
|
|
textColor: Colors.white,
|
|
duration: const Duration(seconds: 3),
|
|
);
|
|
}
|
|
|
|
return dataSync.copy;
|
|
} catch (ex) {
|
|
AppExceptions.exceptionHandler(context, ex);
|
|
throw ex;
|
|
}
|
|
},
|
|
key: 'uploadBill'),
|
|
);
|
|
}
|
|
|
|
void buyFromSeller(BuildContext navigatorContext, Vendor vendor, void Function(Chatroom room) onSuccess) {
|
|
addTask(Task(
|
|
computation: () async {
|
|
try {
|
|
if (!dataSync.isRegistered) {
|
|
Tabnavigator.maybeOf(navigatorContext)?.changePage(2);
|
|
return dataSync.copy;
|
|
}
|
|
await Future.delayed(Duration(seconds: 3));
|
|
var response = await _initChatRequest(dataSync.token ?? "", vendor);
|
|
var data = jsonDecode(response.body);
|
|
var isSuccess = data['data'] != null;
|
|
if (isSuccess) {
|
|
onSuccess(Chatroom.init(data: {
|
|
...data['data'],
|
|
'vendor': {...vendor.jSON}
|
|
}));
|
|
return dataSync.copy;
|
|
} else {
|
|
throw AppExceptions.recognizer(data);
|
|
}
|
|
} catch (ex) {
|
|
AppExceptions.exceptionHandler(navigatorContext, ex);
|
|
throw ex;
|
|
}
|
|
},
|
|
key: 'buy'));
|
|
}
|
|
|
|
void composeStep1(BuildContext context, List<String> sendKeys) {
|
|
addTask(Task(
|
|
computation: () async {
|
|
try {
|
|
var composable = ComposableProduct();
|
|
await Future.delayed(const Duration(seconds: 3));
|
|
var response = await _postCompositionRequest(composable, dataSync.token ?? "", sendKeys);
|
|
var decoded = jsonDecode(response.body);
|
|
var success = decoded["status_code"] == 200 || decoded["status_code"] == '200';
|
|
if (success) {
|
|
int? id = decoded['data']?['product']?['id'];
|
|
ComposableProduct().saveProgress(id);
|
|
return dataSync.copy;
|
|
} else {
|
|
throw AppExceptions.recognizer(decoded);
|
|
}
|
|
} catch (ex) {
|
|
AppExceptions.exceptionHandler(context, ex);
|
|
throw ex;
|
|
}
|
|
},
|
|
key: 'compose_1'));
|
|
}
|
|
|
|
void composeStep2(BuildContext context, List<String> sendKeys) {
|
|
addTask(Task(
|
|
computation: () async {
|
|
try {
|
|
var composable = ComposableProduct();
|
|
if ((composable.localImages.isEmpty && composable.primaryKey == null) || (composable.localImages.isEmpty && composable.remoteImages.isEmpty)) {
|
|
throw MessageException('selectAtLeastImage'.translation);
|
|
}
|
|
var response = await _postMoreRequest(composable, dataSync.token ?? "", sendKeys);
|
|
var byteArray = await response.stream.toBytes();
|
|
var stringBody = utf8.decode(byteArray);
|
|
print(stringBody);
|
|
var decoded = jsonDecode(stringBody);
|
|
var success = decoded["status_code"] == 200 || decoded["status_code"] == '200';
|
|
if (success) {
|
|
ComposableProduct().jSON.clear();
|
|
return dataSync.copy;
|
|
} else {
|
|
throw AppExceptions.recognizer(decoded);
|
|
}
|
|
} catch (ex) {
|
|
AppExceptions.exceptionHandler(context, ex);
|
|
throw ex;
|
|
}
|
|
},
|
|
key: 'compose_2'));
|
|
}
|
|
|
|
void deletePost(BuildContext context, MyProduct product) {
|
|
addTask(Task(
|
|
computation: () async {
|
|
try {
|
|
var response = await _deletePost(product, dataSync.token ?? "");
|
|
var decoded = jsonDecode(response.body);
|
|
var success = decoded["status_code"] == 200 || decoded["status_code"] == '200';
|
|
if (success) {
|
|
return dataSync.copy;
|
|
} else {
|
|
throw AppExceptions.recognizer(decoded);
|
|
}
|
|
} catch (ex) {
|
|
AppExceptions.exceptionHandler(context, ex);
|
|
throw ex;
|
|
}
|
|
},
|
|
key: 'delete_post'));
|
|
}
|
|
|
|
void deleteImage(BuildContext context, ComposableProduct product, RemoteImageModel image, void Function() onSuccess) {
|
|
addTask(Task(
|
|
computation: () async {
|
|
var navContext = Navigator.of(context).context;
|
|
try {
|
|
var response = await _deleteImage(product, image, dataSync.token ?? "");
|
|
print(response.body);
|
|
var decoded = jsonDecode(response.body);
|
|
var success = decoded["status_code"] == 200 || decoded["status_code"] == '200';
|
|
if (success) {
|
|
onSuccess();
|
|
return dataSync.copy;
|
|
} else {
|
|
throw AppExceptions.recognizer(decoded);
|
|
}
|
|
} catch (ex) {
|
|
AppExceptions.exceptionHandler(navContext, ex);
|
|
throw ex;
|
|
}
|
|
},
|
|
key: 'delete_image'));
|
|
}
|
|
|
|
void publishPost(BuildContext context, MyProduct product) {
|
|
addTask(Task(
|
|
computation: () async {
|
|
try {
|
|
var response = await _publish(product, dataSync.token ?? "");
|
|
var decoded = jsonDecode(response.body);
|
|
var success = decoded["status_code"] == 200 || decoded["status_code"] == '200';
|
|
if (success) {
|
|
return dataSync.copy;
|
|
} else {
|
|
throw AppExceptions.recognizer(decoded);
|
|
}
|
|
} catch (ex) {
|
|
AppExceptions.exceptionHandler(context, ex);
|
|
throw ex;
|
|
}
|
|
},
|
|
key: 'publish_post'));
|
|
}
|
|
|
|
Map<String, TaskStatus> _statusTable = {};
|
|
|
|
TaskStatus getStatusByKey(String key) {
|
|
return _statusTable[key] ?? TaskStatus.None;
|
|
}
|
|
|
|
@override
|
|
Future<void> destroyTask(String taskId) async {
|
|
_statusTable.remove(taskId);
|
|
return super.destroyTask(taskId);
|
|
}
|
|
|
|
@override
|
|
Future<void> valueListener(newValue) async {
|
|
super.valueListener(newValue);
|
|
var prefs = await SharedPreferences.getInstance();
|
|
if (newValue.jSON.isEmpty)
|
|
prefs.remove('user');
|
|
else
|
|
await prefs.setString('user', jsonEncode({...newValue.storingData}));
|
|
}
|
|
|
|
@override
|
|
void listenerCallBack(TaskResult<User?> result, String taskKey) {
|
|
_statusTable[taskKey] = result.status;
|
|
notifyListeners();
|
|
}
|
|
}
|
|
|
|
Future<http.Response> _loginRequest(SampleUser user) {
|
|
return http.post(
|
|
baseUrl(path: 'api' + '/login'),
|
|
headers: {'Content-Type': 'application/json', 'Accept': 'application/json'},
|
|
body: jsonEncode(
|
|
{...user.jSON},
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<http.Response> _registerRequest(SampleUser user) {
|
|
return http.post(
|
|
baseUrl(path: 'api' + '/signup'),
|
|
headers: {'Content-Type': 'application/json', 'Accept': 'application/json'},
|
|
body: jsonEncode(
|
|
{...user.jSON},
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<http.Response> _updateRequest(SampleUser user, String token) {
|
|
return http.post(
|
|
baseUrl(path: 'api' + '/me'),
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
'Authorization': 'Bearer $token',
|
|
},
|
|
body: jsonEncode(
|
|
{...user.jSON},
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<http.Response> _postCompositionRequest(ComposableProduct composition, String token, List<String> keys) {
|
|
return http.post(
|
|
baseUrl(path: kApiPath + '/products'),
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
'Authorization': 'Bearer $token',
|
|
},
|
|
body: jsonEncode(
|
|
{...composition.jSON}..removeWhere((key, value) => !keys.contains(key)),
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<http.Response> _deletePost(MyProduct composition, String token) {
|
|
return http.delete(baseUrl(path: kApiPath + '/my-products/${composition.primaryKey}'),
|
|
headers: {'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Bearer $token'});
|
|
}
|
|
|
|
Future<http.Response> _deleteImage(ComposableProduct product, RemoteImageModel image, String token) {
|
|
return http.delete(baseUrl(path: kApiPath + '/products/${product.primaryKey}/image-delete/${image.id}'),
|
|
headers: {'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Bearer $token'});
|
|
}
|
|
|
|
Future<http.Response> _publish(MyProduct composition, String token) {
|
|
return http.post(baseUrl(path: kApiPath + '/products/${composition.primaryKey}/publish'),
|
|
headers: {'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Bearer $token'});
|
|
}
|
|
|
|
Future<http.StreamedResponse> _postMoreRequest(ComposableProduct composition, String token, List<String> keys) {
|
|
var multipart = http.MultipartRequest('POST', baseUrl(path: kApiPath + '/products/${composition.primaryKey}'));
|
|
var images = composition.localImages;
|
|
var files = <http.MultipartFile>[];
|
|
for (var image in images) {
|
|
var file = File(image.primaryKey);
|
|
var stream = http.ByteStream(DelegatingStream(file.openRead()));
|
|
var multipartFile = http.MultipartFile('new_img[]', stream, file.lengthSync(), filename: image.primaryKey.split('/').last);
|
|
files.add(multipartFile);
|
|
}
|
|
if (files.isNotEmpty) {
|
|
multipart.files.addAll(files);
|
|
}
|
|
multipart.fields.addAll({
|
|
for (var key in composition.jSON.keys.where((element) => keys.contains(element)).where((element) => element != 'new_img'))
|
|
key: composition.jSON[key].toString(),
|
|
});
|
|
for (var i = 0; i < composition.remoteImages.length; i++) {
|
|
multipart.fields['old_img[$i]'] = composition.remoteImages[i].primaryKey;
|
|
}
|
|
multipart.headers.addAll(
|
|
{'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Bearer $token'},
|
|
);
|
|
return multipart.send();
|
|
}
|
|
|
|
Future<http.Response> _balanceUpRequest(String token, String cardType, double amount) {
|
|
return http.post(
|
|
baseUrl(path: kApiPath + '/balance_update'),
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
'Authorization': 'Bearer $token',
|
|
},
|
|
body: jsonEncode(
|
|
{
|
|
'type': 'online',
|
|
'amount': amount.toString(),
|
|
'card_type': cardType,
|
|
},
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<http.Response> _getAccountRequest(String token) {
|
|
return http.get(
|
|
baseUrl(path: 'api' + '/me'),
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
'Authorization': 'Bearer $token',
|
|
},
|
|
);
|
|
}
|
|
|
|
Future<http.Response> _initChatRequest(String token, Vendor vendor) {
|
|
return http.post(
|
|
baseUrl(path: kApiPath + '/messages/initialize-chatting/' + vendor.primaryKey.toString()),
|
|
headers: {'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Bearer $token'},
|
|
);
|
|
}
|
|
|
|
Future<http.Response> _verifyEmailRequest(String token) {
|
|
print(jsonEncode({'locale': 'backendCode'.translation}));
|
|
return http.post(baseUrl(path: kApiPath + '/send-email-verification-link'),
|
|
headers: {'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Bearer $token'},
|
|
body: jsonEncode({'locale': 'backendCode'.translation}));
|
|
}
|
|
|
|
Future<http.Response> _sendSmsCode(String token) {
|
|
return http.post(baseUrl(path: kApiPath + '/send-sms-code'),
|
|
headers: {'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Bearer $token'});
|
|
}
|
|
|
|
Future<http.Response> _checkSmsCode(String token, String code) {
|
|
return http.post(baseUrl(path: kApiPath + '/check-sms-code'),
|
|
headers: {'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Bearer $token'}, body: jsonEncode({"sms_code": code}));
|
|
}
|