Browse Source

message page

wr_202303
zsw 4 months ago
parent
commit
5e5ab323ca
  1. 20
      lib/im/SocketClient.dart
  2. 81
      lib/im/chat_details_page.dart
  3. 10
      lib/im/chat_setting.dart
  4. 33
      lib/im/database/hx_database.dart
  5. 41
      lib/im/database/message.dart
  6. 3
      lib/im/im_view/im_page.dart
  7. 6
      lib/retrofit/min_api.dart
  8. 4
      lib/retrofit/retrofit_api.dart
  9. 7
      lib/utils/upload_async.dart

20
lib/im/SocketClient.dart

@ -16,7 +16,7 @@ import 'package:shared_preferences/shared_preferences.dart';
class SocketClient {
//47.93.216.24:9090 线 192.168.10.129:9090
final String ip = kDebugMode ? '192.168.10.129' : '47.93.216.24';
final String ip = !kDebugMode ? '192.168.10.129' : '47.93.216.24';
final num port = 9090;
Socket _socket;
SharedPreferences _shared;
@ -42,7 +42,8 @@ class SocketClient {
MsgData dataResult = MsgData.fromBuffer(proto.body);
print('收到来自:${dataResult.from},消息内容: ${utf8.decode(dataResult.data)} ');
Map<String, dynamic> messageMap = createMessage(userId, utf8.decode(dataResult.data), msgType: dataResult.type.value, userId: dataResult.from);
Map<String, dynamic> messageMap = createMessage(userId, dataResult.data, msgType: dataResult.type.value, userId: dataResult.from);
if (callbacks[dataResult.from] != null) {
messageMap["state"] = 1;
}
@ -54,6 +55,7 @@ class SocketClient {
}
callbacks[userId]?.call(message); /// user self conversation list callback
});
}, onError: (Object error, StackTrace stackTrace) {
debugPrint("socket-listen-error: $error, stackTrace: $stackTrace");
showDebugToast("socket-listen-error: $error, stackTrace: $stackTrace");
@ -152,17 +154,26 @@ class SocketClient {
if (!checkSocket()) {
return;
}
debugPrint("socket-authRequest: request");
final authReq = AuthReq()
..uid = userId
..token = token;
final authReqBytes = authReq.writeToBuffer();
final proto = Proto(1, 1, authReqBytes); // operation seqId 1
final protoBytes = proto.toBytes();
_socket.add(protoBytes);
try {
_socket.add(protoBytes);
} catch (e) {
debugPrint("socket-authRequest: $e");
Future.delayed(const Duration(milliseconds: 1000), () {
authRequest(token);
});
}
}
Future<Message> sendMessage(String toId, String content) async {
Map<String, dynamic> message = createMessage(toId, content, userId: userId);
Map<String, dynamic> message = createSendMessage(toId, content, userId: userId);
message["state"] = 1;
int id = await hxDatabase.insert(message).catchError((error) {
debugPrint("insertMessage: $error");
});
@ -188,7 +199,6 @@ class SocketClient {
showDebugToast("socket-send-error: ${e.toString()}");
reconnect();
}
debugPrint("sendMessage: ${message["id"]}");
return Message.fromJson(message);
}

81
lib/im/chat_details_page.dart

@ -105,19 +105,19 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
Future refresh() async {
List<Message> messagePage =
await hxDatabase.queryUList(conversation, page: page + 1, pageSize: 10);
page += 1;
if (page == 1) {
messages = messagePage;
} else {
messages.addAll(messagePage);
}
await messageShowTime();
if (messagePage.isEmpty) {
refreshController.loadNoData();
return;
} else {
refreshController.loadComplete();
page += 1;
}
if (page == 1) {
messages = messagePage;
} else {
messages.addAll(messagePage);
}
await messageShowTime();
return Future.value();
}
@ -177,7 +177,7 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
void jumpToBottom() {
Future.delayed(const Duration(milliseconds: 100), () {
scrollController.position
.jumpTo(scrollController.position.maxScrollExtent);
.jumpTo(0);
});
}
@ -374,32 +374,28 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
enablePullDown: false,
enablePullUp: true,
header: MyHeader(),
reverse: true,
physics: BouncingScrollPhysics(),
footer: CustomFooter(
loadStyle: LoadStyle.ShowWhenLoading,
builder: (BuildContext context, LoadStatus mode) {
return SizedBox();
},
),
reverse: true,
scrollController: scrollController,
controller: refreshController,
onRefresh: () {
debugPrint("");
},
onLoading: () {
refresh().then((value) {
refreshState();
});
},
child: Container(
alignment: Alignment.topCenter,
child: SingleChildScrollView(
physics: BouncingScrollPhysics(),
controller: scrollController,
child: chatDetailsList(),
),
),
child: chatDetailsList(),
),
flex: 1,
),
input()
input(),
],
),
),
@ -409,27 +405,22 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
///
Widget chatDetailsList() {
return Container(
alignment: Alignment.topCenter,
child: ListView.builder(
itemCount: messages.length,
shrinkWrap: true,
reverse: true,
padding: EdgeInsets.zero,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, position) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
setState(() {
copyIndex = 0;
FocusScope.of(context).requestFocus(FocusNode());
});
},
child: chatDetailsItem(position),
);
},
),
return ListView.builder(
itemCount: messages.length,
reverse: true,
padding: EdgeInsets.zero,
itemBuilder: (context, position) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
setState(() {
copyIndex = 0;
FocusScope.of(context).requestFocus(FocusNode());
});
},
child: chatDetailsItem(position),
);
},
);
}
@ -936,8 +927,10 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
Message message = value;
messages.insert(0, message);
chatController.clear();
refreshState();
jumpToBottom();
messageShowTime().then((value) {
refreshState();
jumpToBottom();
});
});
},
maxLines: 8,
@ -1028,7 +1021,8 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
buttonMode: ButtonMode.MATERIAL,
checkPlatformCompatibility: true,
),
)),
),
),
),
///
@ -1227,4 +1221,5 @@ class _ChatDetailsPage extends State<ChatDetailsPage>
print(tex);
Clipboard.setData(ClipboardData(text: tex));
}
}

10
lib/im/chat_setting.dart

@ -5,9 +5,11 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:huixiang/im/database/message.dart';
import 'package:huixiang/retrofit/retrofit_api.dart';
import 'package:huixiang/view_widget/my_appbar.dart';
import 'package:flutter/cupertino.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../generated/l10n.dart';
import '../../utils/font_weight.dart';
import '../main.dart';
@ -26,6 +28,7 @@ class ChatSetting extends StatefulWidget {
class _ChatSetting extends State<ChatSetting> {
ImUser imUser;
String selfUserId = "";
@override
void initState() {
@ -34,6 +37,7 @@ class _ChatSetting extends State<ChatSetting> {
}
void initData() async {
selfUserId = (await SharedPreferences.getInstance()).getString("userId");
imUser = await hxDatabase.queryImUserById(widget.arguments["userId"]);
setState(() {
@ -79,8 +83,7 @@ class _ChatSetting extends State<ChatSetting> {
onChanged: (bool value) async {
if (imUser == null) return;
imUser.isTop = value ? 1 : 0;
await hxDatabase
.insertOrUpdateImUser(imUser.toJson());
await hxDatabase.insertOrUpdateImUser(imUser.toJson());
setState(() {});
},
),
@ -112,8 +115,7 @@ class _ChatSetting extends State<ChatSetting> {
],
),
onTap: () async {
await hxDatabase
.deleteByUser(widget.arguments["userId"] ?? "");
await hxDatabase.deleteByUser(conversationId(widget.arguments["userId"] ?? "", selfUserId));
// SmartDialog.showToast("删除成功",
// alignment: Alignment.center);
},

33
lib/im/database/hx_database.dart

@ -11,15 +11,15 @@ class HxDatabase {
Database db;
void open({String key}) async {
// _migrations.add(Migration(1, 2, (Database database) async {
// database.execute('ALTER TABLE `Message` ADD COLUMN `replyId` VARCHAR(20) DEFAULT NULL AFTER `toId`');
// _migrations.add(Migration(3, 4, (Database database) async {
// await database.execute('ALTER TABLE ImUser ADD COLUMN IF NOT EXISTS `isTop` INTEGER DEFAULT 0');
// }));
String databaseName = 'hx.db';
if (key?.isNotEmpty ?? false) {
databaseName = 'hx_$key.db';
}
await openDatabase(databaseName, version: 2,
await openDatabase(databaseName, version: 1,
onCreate: (Database db, int version) async {
db.execute(
'CREATE TABLE IF NOT EXISTS `Message` (`id` INTEGER, `conversationId` VARCHAR(40), `fromId` VARCHAR(20), `toId` VARCHAR(20), `replyId` VARCHAR(20), `content` TEXT, `attach` TEXT, `msgType` INTEGER, `time` VARCHAR(20), `state` INTEGER, `isDelete` INTEGER, PRIMARY KEY (`id`))');
@ -27,10 +27,7 @@ class HxDatabase {
'CREATE TABLE IF NOT EXISTS `ImUser` (`id` INTEGER, `mid` VARCHAR(20), `nickname` VARCHAR(20), `avatar` VARCHAR(200), `phone` VARCHAR(200), `isDelete` INTEGER, `isTop` INTEGER, PRIMARY KEY (`id`))');
}, onConfigure: (database) async {
await database.execute('PRAGMA foreign_keys = ON');
try {
await database.execute(
'ALTER TABLE ImUser ADD COLUMN isTop INTEGER DEFAULT 0');
}catch (e){}
debugPrint("database-version: ${await database.getVersion()}");
}, onUpgrade: (database, startVersion, endVersion) async {
await runMigrations(database, startVersion, endVersion, _migrations);
}, onOpen: (Database db) {
@ -51,10 +48,8 @@ class HxDatabase {
Future<Message> lastMessage(String conversationId) async {
await _dbIsOpen();
String sql =
'SELECT * FROM Message WHERE conversationId = ? ORDER BY time DESC LIMIT 1';
List<Message> messages =
await db.rawQuery(sql, [conversationId]).then((value) {
String sql = 'SELECT * FROM Message WHERE conversationId = ? ORDER BY time DESC LIMIT 1';
List<Message> messages = await db.rawQuery(sql, [conversationId]).then((value) {
return value.map((e) {
debugPrint("Message: $e");
return Message.fromJson(e);
@ -89,7 +84,7 @@ class HxDatabase {
return Message.fromJson(e);
}).toList();
}, onError: (error) {
debugPrint("MessageError: $error");
debugPrint("Message-error: $error");
});
}
@ -100,7 +95,7 @@ class HxDatabase {
return db.rawQuery(sql, [conversationId, start, pageSize]).then((value) {
return value.map((e) => Message.fromJson(e)).toList();
}, onError: (error) {
debugPrint("Messageerror: $error");
debugPrint("Message-error: $error");
});
}
@ -110,7 +105,7 @@ class HxDatabase {
return db.rawQuery(sql, [conversationId]).then((value) {
return value.map((e) => Message.fromJson(e)).toList();
}, onError: (error) {
debugPrint("Messageerror: $error");
debugPrint("Message-error: $error");
});
}
@ -118,14 +113,18 @@ class HxDatabase {
await _dbIsOpen();
String userStr = conversationIds.join(",");
debugPrint("userStr: $userStr");
List<Message> messages = await db.rawQuery(
"SELECT * FROM Message WHERE `conversationId` IN ($userStr) AND state = 0 AND isDelete = 0",
List<Message> messages = await db.query("Message",
where: "`conversationId` IN (?) AND state = 0 AND isDelete = 0",
whereArgs: [userStr],
).then((value) {
return value.map((e) => Message.fromJson(e)).toList();
}, onError: (error) {
debugPrint("Message-error: $error");
});
return (messages??[]).lGroupBy((p) => p.fromId).mGroupCount;
messages.forEach((element) {
debugPrint("messageUnreadCount: ${element.toJson()}");
});
return (messages??[]).lGroupBy((p) => p.conversationId).mGroupCount;
}
Future<List<Map>> queryListAll() async{

41
lib/im/database/message.dart

@ -1,5 +1,12 @@
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:flutter/cupertino.dart';
import 'package:huixiang/im/out/message.pb.dart';
import 'package:huixiang/im/out/message.pbenum.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
class Message {
int id;
@ -57,7 +64,39 @@ class Message {
};
}
createMessage(var toId, String content, {String attach, int msgType, userId, replyId}) {
createSendMessage(var toId, String content, {String attach, int msgType, userId, replyId}) {
return <String, dynamic>{
"conversationId": conversationId(userId, toId),
"fromId": userId,
"toId": toId,
"replyId": replyId,
"content": content,
"attach": attach,
"msgType": msgType ?? 1,
"time": "${DateTime.now().millisecondsSinceEpoch}",
"state": 0,
"isDelete": 0
};
}
createMessage(var toId, Uint8List dataBytes, {String attach, int msgType, userId, replyId}) {
String content = "";
MsgType type = MsgType.values[msgType];
if (type == MsgType.TEXT) {
content = utf8.decode(dataBytes);
}
if (type == MsgType.IMAGE || type == MsgType.AUDIO || type == MsgType.VIDEO) {
Map<String, dynamic> result = ImageGallerySaver.saveImage(
dataBytes,
isReturnImagePathOfIOS: true,
);
bool isSuccess = result["isSuccess"] != null && result["isSuccess"];
if (isSuccess) {
attach = result["filePath"];
}
}
return <String, dynamic>{
"conversationId": conversationId(userId, toId),
"fromId": userId,

3
lib/im/im_view/im_page.dart

@ -139,6 +139,7 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
/// update conversation unreadcount
queryUnreadCount(conversationIds) async {
unreadCountMap = await hxDatabase.messageUnreadCount(conversationIds);
debugPrint("unreadCountMap: $unreadCountMap");
}
/// update imuser info by mids
@ -169,7 +170,7 @@ class _IMPage extends State<IMPage> implements OnChatMessage {
/// update one conversation last message ,and update conversation sort
void updateLastMessage(String conversationId) async {
Message message = await hxDatabase.lastMessage(conversationId);
debugPrint("lastmessage: $conversationId ${message.content} ${message.toJson()}");
debugPrint("lastmessage: $conversationId ${message?.content} ${message?.toJson()}");
if (message != null) {
lastMessageMap[conversationId] = message;
await sortConversation(lastMessageMap);

6
lib/retrofit/min_api.dart

@ -27,8 +27,9 @@ import 'data/shopping_home_config.dart';
part 'min_api.g.dart';
const localBaseUrl = "http://192.168.10.54:8765/app/";///
// const localBaseUrl = "http://192.168.10.54:8765/app/";///
// const localBaseUrl = "http://pos-test.api.lotus-wallet.com/app/";///
const localBaseUrl = "https://pos.api.lotus-wallet.com/app/";///线
const serviceBaseUrl = "https://pos.api.lotus-wallet.com/app/";///线
@ -44,8 +45,7 @@ abstract class MinApiService {
String storeId,
bool showLoading = false,
}) {
Map<String, dynamic> headers =
(token == null || token == "") ? {} : {'token': "Bearer $token"};
Map<String, dynamic> headers = (token == null || token == "") ? {} : {'token': "Bearer $token"};
if (tenant != null && tenant != "") {
headers["tenant"] = tenant;
}

4
lib/retrofit/retrofit_api.dart

@ -70,13 +70,13 @@ import 'data/wx_pay.dart';
part 'retrofit_api.g.dart';
const localBaseUrl = "http://192.168.10.54:8766/app/";///
// const localBaseUrl = "http://192.168.10.54:8766/app/";///
// const localBaseUrl = "http://platform.test.api.lotus-wallet.com/app/";///
const localBaseUrl = "https://pos.platform.lotus-wallet.com/app/";///线
const serviceBaseUrl = "https://pos.platform.lotus-wallet.com/app/";///线
const ipBaseUrl = "http://whois.pconline.com.cn";
///ip
@RestApi(baseUrl: localBaseUrl)
abstract class ApiService {

7
lib/utils/upload_async.dart

@ -34,7 +34,8 @@ class UploadAsync {
decoration: BoxDecoration(
color: Colors.grey, borderRadius: BorderRadius.circular(2)),
child: dynamicType == 0
? Expanded(child: Column(
? Expanded(
child: Column(
children: [
Text(
dynamicText,
@ -49,8 +50,8 @@ class UploadAsync {
Expanded(child:
Container(
height: 20.h,
))
]))
),),
],),)
: ClipRRect(
borderRadius: BorderRadius.circular(2),
child: Image.file(

Loading…
Cancel
Save