birzha_mobile/lib/models/chatroom/chatroom.dart

304 lines
9.7 KiB
Dart

import 'dart:collection';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:birzha/models/categories/category.dart';
import 'package:birzha/models/chatroom/message.dart';
import 'package:birzha/models/exceptions/exception.dart';
import 'package:birzha/models/products/post.dart';
import 'package:birzha/models/settings/settingsModel.dart';
import 'package:birzha/models/user/user.dart';
import 'package:birzha/models/user/userManager.dart';
import 'package:birzha/services/requests.dart';
import 'package:flutter/material.dart';
import 'package:birzha/core/manager/manager.dart';
import 'package:birzha/core/orm/orm.dart';
import 'package:provider/provider.dart';
class Chatroom extends Manager<_ChatroomData> implements _ChatroomData {
Set<LocalMessage> _availableLocalMessages = {};
Set<RemoteMessage> get _availableRemoteMessages => {
for (var data in jSON['messages'] ?? []) RemoteMessage(data: {...data})
};
Set<Message> get messages => UnmodifiableSetView({..._availableLocalMessages, ..._availableRemoteMessages});
int get lastIndexOfLocalMessages => _lastMessageInBufferId;
void _clearSentMessages() {
_availableLocalMessages.removeWhere((element) => element.status == MessagesStatus.sent);
}
@override
get jSON => {...dataSync.jSON};
@override
set jSON(other) {}
int _lastMessageInBufferId = -1;
Chatroom({required Map<String, dynamic> data}) : super(_ChatroomData(data: {...data}));
factory Chatroom.init({required Map<String, dynamic> data}) {
var newData = {...data};
newData['id'] = data['chatroom_id'];
newData['messages'] = [];
return Chatroom(data: {...newData});
}
@override
int get primaryKey => jSON[primaryKeyField];
@override
String get primaryKeyField => 'id';
@override
Vendor? get sender => dataSync.sender;
@override
String get subTitile => dataSync.subTitile;
@override
String get title => dataSync.title;
@override
int get unread => dataSync.unread;
@override
bool get hasNewMessages => dataSync.hasNewMessages;
@override
@protected
_ChatroomData get dataSync => super.dataSync;
@override
@protected
get value => super.value;
@override
@protected
set value(other) {
super.value = other;
}
@override
void listenerCallBack(TaskResult<_ChatroomData?> result, String taskKey) {
_statusTable[taskKey] = result.status;
notifyListeners();
}
@override
Future<void> destroyTask(String taskId) async {
_statusTable.remove(taskId);
return super.destroyTask(taskId);
}
Map<String, TaskStatus> _statusTable = {};
TaskStatus getStatusByKey(String key) {
return _statusTable[key] ?? TaskStatus.None;
}
void loadPastMessages(BuildContext context, [VoidCallback? onDone]) {
addTask(Task(
key: 'load',
computation: () async {
try {
var response = await _loadMoreRequest(AppUserManager.of(context).dataSync.token ?? "", dataSync, _howManyMessagesToSkip);
var decoded = jsonDecode(response.body);
var isSuccess = decoded['status_code'] == 200 || decoded['status_code'] == '200';
if (!isSuccess) {
throw AppExceptions.recognizer(decoded);
} else {
var data = decoded['data'];
var newMessages = data['messages'];
List processed = [];
if (newMessages is Iterable) {
processed = [...newMessages];
} else {
var mapMessages = newMessages as Map;
processed = [for (var key in mapMessages.keys) mapMessages[key]];
}
if (onDone != null) {
onDone();
}
return dataSync.copy..jSON['messages'] = [...jSON['messages'], ...processed.reversed];
}
} catch (ex) {
if (onDone != null) {
onDone();
}
AppExceptions.exceptionHandler(context, ex);
throw ex;
}
}));
}
void refresh(BuildContext context, [VoidCallback? onDone]) {
addTask(Task(
key: 'load',
computation: () async {
try {
var response = await _loadMoreRequest(AppUserManager.of(context).dataSync.token ?? "", dataSync, 0);
var decoded = jsonDecode(response.body);
var isSuccess = decoded['status_code'] == 200 || decoded['status_code'] == '200';
if (!isSuccess) {
throw AppExceptions.recognizer(decoded);
} else {
var data = decoded['data'];
var newMessages = data['messages'];
List processed = [];
if (newMessages is Iterable) {
processed = [...newMessages];
} else {
var mapMessages = newMessages as Map;
processed = [for (var key in mapMessages.keys) mapMessages[key]];
}
if (onDone != null) {
onDone();
}
_clearSentMessages();
return dataSync.copy
..jSON['messages'] = [
...processed.reversed,
...jSON['messages'],
];
}
} catch (ex, trace) {
print(trace);
if (onDone != null) {
onDone();
}
AppExceptions.exceptionHandler(context, ex);
throw ex;
}
}));
}
int get _howManyMessagesToSkip {
var sentMessages = messages.where((element) => element.status == MessagesStatus.sent);
return sentMessages.length;
}
void retextMessage(BuildContext context, Message message) {
_availableLocalMessages.remove(message);
notifyListeners();
textMessage(context, message.text);
}
void textMessage(BuildContext context, String text) {
addTask(Task(
computation: () async {
var message = LocalMessage(date: DateTime.fromMillisecondsSinceEpoch(DateTime.now().millisecondsSinceEpoch), id: _lastMessageInBufferId, text: text);
try {
_lastMessageInBufferId--;
message.status = MessagesStatus.sending;
_availableLocalMessages = {message, ..._availableLocalMessages};
notifyListeners();
var response = await _sendMessageRequest(
message,
AppUserManager.of(context).dataSync.token ?? "",
dataSync,
);
var decoded = jsonDecode(response.body);
var isSuccess = decoded['status_code'] == 200 || decoded['status_code'] == '200';
if (!isSuccess) {
throw AppExceptions.recognizer(decoded);
} else {
message.status = MessagesStatus.sent;
return dataSync.copy..jSON['last_message'] = {...message.toRemoteMessage().jSON};
}
} catch (ex) {
message.status = MessagesStatus.failed;
AppExceptions.exceptionHandler(context, ex);
throw ex;
}
},
key: 'message' + (_lastMessageInBufferId).toString()));
}
@protected
@override
_ChatroomData get copy => throw UnimplementedError();
static Chatroom of(BuildContext context, {bool listen = false}) {
return Provider.of<Chatroom>(context, listen: listen);
}
}
Future<http.Response> _sendMessageRequest(LocalMessage message, String token, _ChatroomData chatroom) {
return http.post(baseUrl(path: kApiPath + '/messages/' + chatroom.primaryKey.toString()), body: jsonEncode({'msg': message.text}), headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
});
}
Future<http.Response> _loadMoreRequest(String token, _ChatroomData chatroom, int skip) {
return http.get(baseUrl(path: kApiPath + '/messages/chatroom/' + chatroom.primaryKey.toString() + '/load-more', queryParameters: {'skip': skip.toString()}),
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer $token',
});
}
class _ChatroomData extends Post with CopyAbleMixin<_ChatroomData> {
_ChatroomData({required Map<String, dynamic> data}) : super(data);
Vendor? get sender {
if (jSON['vendor'] != null) {
return Vendor(data: {...jSON['vendor']});
}
if (jSON['users'] is! Iterable) {
return null;
}
var users = jSON['users'] as Iterable;
if (users.isEmpty) {
return null;
}
return Vendor(data: {...users.first});
}
String get title => (sender?.name.isEmpty ?? true) ? (sender?.phone ?? "") : sender!.name;
String get subTitile => jSON['last_message'] is! Map ? '' : RemoteMessage(data: {...jSON['last_message']}).text;
int get unread => jSON['count_unread_messages'] is! int ? 0 : jSON['count_unread_messages'];
bool get hasNewMessages => unread > 0;
@override
_ChatroomData get copy => _ChatroomData(data: {...jSON});
}
class ChatroomSerializer extends RemoteCategory<Chatroom> {
ChatroomSerializer() : super(data: {'id': -5});
@override
get copy => ChatroomSerializer();
@override
String get name => 'messages'.translation;
@override
Chatroom postBuilderDelegate(Map<String, dynamic> data) => Chatroom(data: {...data});
@override
Uri postUri(BuildContext context, int page) {
return baseUrl(path: kApiPath + '/messages');
}
@override
Future<List<Chatroom>> getPosts(BuildContext context, int page) {
return FutureGetList<Chatroom>(
postUri(context, page),
parser: (response) {
var decoded = jsonDecode(response.body);
var chatrooms = decoded['data']['chatrooms'] as Iterable;
return [
for (var chatroomRaw in chatrooms) Chatroom(data: {...chatroomRaw})
];
},
).fetch({'Accept': 'application/json', 'Authorization': 'Bearer ${AppUserManager.of(context).dataSync.token}'});
}
@override
bool get needPagination => false;
}