mirror of
https://github.com/Linloir/Simple-TCP-Client.git
synced 2025-12-17 08:48:11 +08:00
More Functions
- message list persistence - bug fix for contact list tag
This commit is contained in:
parent
c3d0a91c48
commit
e7f690159c
@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
|||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion flutter.compileSdkVersion
|
compileSdkVersion 33
|
||||||
ndkVersion flutter.ndkVersion
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
|||||||
@ -31,4 +31,5 @@
|
|||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
</application>
|
</application>
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-13 14:03:56
|
* @Date : 2022-10-13 14:03:56
|
||||||
* @LastEditTime : 2022-10-15 11:45:04
|
* @LastEditTime : 2022-10-17 17:43:55
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -107,13 +107,13 @@ class ChatCubit extends Cubit<ChatState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> fetchFile({required String messageMd5}) async {
|
Future<void> fetchFile({required String messageMd5}) async {
|
||||||
var newHistory = [...state.chatHistory];
|
// var newHistory = [...state.chatHistory];
|
||||||
var index = newHistory.indexWhere((e) => e.message.contentmd5 == messageMd5);
|
// var index = newHistory.indexWhere((e) => e.message.contentmd5 == messageMd5);
|
||||||
if(index != -1) {
|
// if(index != -1) {
|
||||||
newHistory[index] = newHistory[index].copyWith(
|
// newHistory[index] = newHistory[index].copyWith(
|
||||||
status: ChatHistoryStatus.done
|
// status: ChatHistoryStatus.done
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
var clonedTCPRepository = await tcpRepository.clone();
|
var clonedTCPRepository = await tcpRepository.clone();
|
||||||
clonedTCPRepository.pushRequest(FetchFileRequest(
|
clonedTCPRepository.pushRequest(FetchFileRequest(
|
||||||
msgmd5: messageMd5,
|
msgmd5: messageMd5,
|
||||||
@ -147,6 +147,7 @@ class ChatCubit extends Cubit<ChatState> {
|
|||||||
}
|
}
|
||||||
emit(state.copyWith(chatHistory: newHistory));
|
emit(state.copyWith(chatHistory: newHistory));
|
||||||
}
|
}
|
||||||
|
clonedTCPRepository.dispose();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
fileFetchSubscriptionMap.addEntries([MapEntry(messageMd5, subscription)]);
|
fileFetchSubscriptionMap.addEntries([MapEntry(messageMd5, subscription)]);
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-13 14:02:28
|
* @Date : 2022-10-13 14:02:28
|
||||||
* @LastEditTime : 2022-10-13 23:02:04
|
* @LastEditTime : 2022-10-17 17:00:45
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:tcp_client/home/cubit/home_state.dart';
|
import 'package:tcp_client/home/cubit/home_state.dart';
|
||||||
import 'package:tcp_client/repositories/local_service_repository/local_service_repository.dart';
|
import 'package:tcp_client/repositories/local_service_repository/local_service_repository.dart';
|
||||||
import 'package:tcp_client/repositories/tcp_repository/tcp_repository.dart';
|
import 'package:tcp_client/repositories/tcp_repository/tcp_repository.dart';
|
||||||
@ -14,12 +15,22 @@ class HomeCubit extends Cubit<HomeState> {
|
|||||||
HomeCubit({
|
HomeCubit({
|
||||||
required this.localServiceRepository,
|
required this.localServiceRepository,
|
||||||
required this.tcpRepository,
|
required this.tcpRepository,
|
||||||
}): super(const HomeState(page: HomePagePosition.message));
|
required this.pageController
|
||||||
|
}): super(const HomeState(page: HomePagePosition.message)) {
|
||||||
|
pageController.addListener(() {
|
||||||
|
emit(state.copyWith(page: HomePagePosition.fromValue((pageController.page ?? 0).round())));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
final LocalServiceRepository localServiceRepository;
|
final LocalServiceRepository localServiceRepository;
|
||||||
final TCPRepository tcpRepository;
|
final TCPRepository tcpRepository;
|
||||||
|
final PageController pageController;
|
||||||
|
|
||||||
void switchPage(HomePagePosition newPage) {
|
void switchPage(HomePagePosition newPage) {
|
||||||
emit(state.copyWith(page: newPage));
|
pageController.animateToPage(
|
||||||
|
newPage.value,
|
||||||
|
duration: const Duration(milliseconds: 500),
|
||||||
|
curve: Curves.easeInOutCubicEmphasized
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-11 11:05:08
|
* @Date : 2022-10-11 11:05:08
|
||||||
* @LastEditTime : 2022-10-14 15:57:30
|
* @LastEditTime : 2022-10-17 16:57:57
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -72,7 +72,8 @@ class HomePage extends StatelessWidget {
|
|||||||
BlocProvider<HomeCubit>(
|
BlocProvider<HomeCubit>(
|
||||||
create: (context) => HomeCubit(
|
create: (context) => HomeCubit(
|
||||||
localServiceRepository: localServiceRepository,
|
localServiceRepository: localServiceRepository,
|
||||||
tcpRepository: tcpRepository
|
tcpRepository: tcpRepository,
|
||||||
|
pageController: PageController()
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@ -83,64 +84,75 @@ class HomePage extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class HomePageView extends StatelessWidget {
|
class HomePageView extends StatelessWidget {
|
||||||
HomePageView({
|
const HomePageView({
|
||||||
required this.userID,
|
required this.userID,
|
||||||
super.key
|
super.key
|
||||||
});
|
});
|
||||||
|
|
||||||
final PageController _controller = PageController();
|
|
||||||
final int userID;
|
final int userID;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocListener<HomeCubit, HomeState>(
|
return Scaffold(
|
||||||
listenWhen:(previous, current) => current.page != previous.page,
|
appBar: AppBar(
|
||||||
listener: (context, state) {
|
title: BlocBuilder<HomeCubit, HomeState>(
|
||||||
_controller.animateToPage(
|
builder: (context, state) {
|
||||||
state.page.value,
|
return Text(
|
||||||
duration: const Duration(milliseconds: 375),
|
state.page.literal,
|
||||||
curve: Curves.easeInOutCubicEmphasized
|
style: const TextStyle(
|
||||||
);
|
fontWeight: FontWeight.bold
|
||||||
},
|
),
|
||||||
child: Scaffold(
|
);
|
||||||
appBar: AppBar(
|
},
|
||||||
title: BlocBuilder<HomeCubit, HomeState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
return Text(
|
|
||||||
state.page.literal,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.bold
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
IconButton(
|
|
||||||
icon: const Icon(Icons.search_rounded),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).push(SearchPage.route(
|
|
||||||
localServiceRepository: context.read<LocalServiceRepository>(),
|
|
||||||
tcpRepository: context.read<TCPRepository>(),
|
|
||||||
userRepository: context.read<UserRepository>()
|
|
||||||
));
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
body: Center(
|
actions: [
|
||||||
child: BlocBuilder<HomeCubit, HomeState>(
|
IconButton(
|
||||||
builder:(context, state) => PageView(
|
icon: const Icon(Icons.search_rounded),
|
||||||
controller: _controller,
|
onPressed: () {
|
||||||
onPageChanged: (value) => context.read<HomeCubit>().switchPage(HomePagePosition.fromValue(value)),
|
Navigator.of(context).push(SearchPage.route(
|
||||||
children: [
|
localServiceRepository: context.read<LocalServiceRepository>(),
|
||||||
const MessagePage(),
|
tcpRepository: context.read<TCPRepository>(),
|
||||||
const ContactPage(),
|
userRepository: context.read<UserRepository>()
|
||||||
MyProfilePage(userID: userID)
|
));
|
||||||
],
|
},
|
||||||
),
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: BlocBuilder<HomeCubit, HomeState>(
|
||||||
|
builder:(context, state) => PageView(
|
||||||
|
controller: context.read<HomeCubit>().pageController,
|
||||||
|
children: [
|
||||||
|
MessagePage(),
|
||||||
|
const ContactPage(),
|
||||||
|
MyProfilePage(userID: userID)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
bottomNavigationBar: BlocBuilder<HomeCubit, HomeState>(
|
||||||
|
builder: (context, state) => BottomNavigationBar(
|
||||||
|
items: const [
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
activeIcon: Icon(Icons.message_rounded),
|
||||||
|
icon: Icon(Icons.message_outlined),
|
||||||
|
label: 'Message'
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
activeIcon: Icon(Icons.contacts_rounded),
|
||||||
|
icon: Icon(Icons.contacts_outlined),
|
||||||
|
label: 'Contacts'
|
||||||
|
),
|
||||||
|
BottomNavigationBarItem(
|
||||||
|
activeIcon: Icon(Icons.person_rounded),
|
||||||
|
icon: Icon(Icons.person_outline_rounded),
|
||||||
|
label: 'Me'
|
||||||
|
),
|
||||||
|
],
|
||||||
|
currentIndex: state.page.value,
|
||||||
|
onTap: (value) => context.read<HomeCubit>().switchPage(HomePagePosition.fromValue(value))
|
||||||
|
),
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-12 23:36:07
|
* @Date : 2022-10-12 23:36:07
|
||||||
* @LastEditTime : 2022-10-14 11:45:35
|
* @LastEditTime : 2022-10-17 17:35:05
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -21,15 +21,16 @@ class ContactPage extends StatelessWidget {
|
|||||||
return Container(
|
return Container(
|
||||||
child: BlocBuilder<ContactCubit, ContactState>(
|
child: BlocBuilder<ContactCubit, ContactState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
|
var indexedData = state.indexedData;
|
||||||
return AzListView(
|
return AzListView(
|
||||||
data: state.indexedData,
|
data: indexedData,
|
||||||
itemCount: state.contacts.length,
|
itemCount: indexedData.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return ContactTile(
|
return ContactTile(
|
||||||
userInfo: state.contacts[index],
|
userInfo: (indexedData[index] as ContactModel).userInfo,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
physics: const BouncingScrollPhysics(),
|
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
|
||||||
susItemBuilder: (context, index) {
|
susItemBuilder: (context, index) {
|
||||||
return Container(
|
return Container(
|
||||||
height: 40,
|
height: 40,
|
||||||
@ -38,7 +39,7 @@ class ContactPage extends StatelessWidget {
|
|||||||
color: Colors.grey[200],
|
color: Colors.grey[200],
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: Text(
|
child: Text(
|
||||||
ContactModel(userInfo: state.contacts[index]).getSuspensionTag(),
|
indexedData[index].getSuspensionTag(),
|
||||||
softWrap: false,
|
softWrap: false,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 14.0,
|
fontSize: 14.0,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-13 14:01:39
|
* @Date : 2022-10-13 14:01:39
|
||||||
* @LastEditTime : 2022-10-13 15:51:23
|
* @LastEditTime : 2022-10-17 17:28:33
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -25,7 +25,8 @@ class ContactState extends Equatable {
|
|||||||
|
|
||||||
List<ISuspensionBean> get indexedData {
|
List<ISuspensionBean> get indexedData {
|
||||||
var indexedList = contacts.map((e) => ContactModel(userInfo: e)).toList();
|
var indexedList = contacts.map((e) => ContactModel(userInfo: e)).toList();
|
||||||
SuspensionUtil.sortListBySuspensionTag(indexedList);
|
indexedList.sort((a, b) => a.getSuspensionTag().compareTo(b.getSuspensionTag()));
|
||||||
|
// SuspensionUtil.sortListBySuspensionTag(indexedList);
|
||||||
SuspensionUtil.setShowSuspensionStatus(indexedList);
|
SuspensionUtil.setShowSuspensionStatus(indexedList);
|
||||||
return indexedList;
|
return indexedList;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-13 15:34:08
|
* @Date : 2022-10-13 15:34:08
|
||||||
* @LastEditTime : 2022-10-13 15:41:24
|
* @LastEditTime : 2022-10-17 17:20:47
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ class ContactModel extends ISuspensionBean {
|
|||||||
@override
|
@override
|
||||||
String getSuspensionTag() {
|
String getSuspensionTag() {
|
||||||
var pinyin = PinyinHelper.getPinyinE(userInfo.userName);
|
var pinyin = PinyinHelper.getPinyinE(userInfo.userName);
|
||||||
var tag = pinyin.substring(0, 1);
|
var tag = pinyin.substring(0, 1).toUpperCase();
|
||||||
if(!RegExp('[A-Z]').hasMatch(tag)) {
|
if(!RegExp('[A-Z]').hasMatch(tag)) {
|
||||||
tag = '#';
|
tag = '#';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-13 14:02:00
|
* @Date : 2022-10-13 14:02:00
|
||||||
* @LastEditTime : 2022-10-14 11:59:48
|
* @LastEditTime : 2022-10-17 17:19:50
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -53,14 +53,18 @@ class ContactTile extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
UserAvatar(userid: userInfo.userID),
|
IgnorePointer(
|
||||||
|
child: UserAvatar(userid: userInfo.userID),
|
||||||
|
),
|
||||||
const SizedBox(width: 12,),
|
const SizedBox(width: 12,),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 12.0
|
vertical: 12.0
|
||||||
),
|
),
|
||||||
child: UserNameText(userid: userInfo.userID,)
|
child: IgnorePointer(
|
||||||
|
child: UserNameText(userid: userInfo.userID,)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-12 23:38:31
|
* @Date : 2022-10-12 23:38:31
|
||||||
* @LastEditTime : 2022-10-15 10:29:05
|
* @LastEditTime : 2022-10-17 17:15:12
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -12,6 +12,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
import 'package:tcp_client/home/view/message_page/cubit/msg_list_state.dart';
|
import 'package:tcp_client/home/view/message_page/cubit/msg_list_state.dart';
|
||||||
import 'package:tcp_client/home/view/message_page/models/message_info.dart';
|
import 'package:tcp_client/home/view/message_page/models/message_info.dart';
|
||||||
import 'package:tcp_client/repositories/local_service_repository/local_service_repository.dart';
|
import 'package:tcp_client/repositories/local_service_repository/local_service_repository.dart';
|
||||||
|
import 'package:tcp_client/repositories/tcp_repository/models/tcp_request.dart';
|
||||||
import 'package:tcp_client/repositories/tcp_repository/models/tcp_response.dart';
|
import 'package:tcp_client/repositories/tcp_repository/models/tcp_response.dart';
|
||||||
import 'package:tcp_client/repositories/tcp_repository/tcp_repository.dart';
|
import 'package:tcp_client/repositories/tcp_repository/tcp_repository.dart';
|
||||||
|
|
||||||
@ -21,18 +22,50 @@ class MessageListCubit extends Cubit<MessageListState> {
|
|||||||
required this.tcpRepository
|
required this.tcpRepository
|
||||||
}): super(MessageListState.empty()) {
|
}): super(MessageListState.empty()) {
|
||||||
subscription = tcpRepository.responseStreamBroadcast.listen(_onResponse);
|
subscription = tcpRepository.responseStreamBroadcast.listen(_onResponse);
|
||||||
|
Future<List<MessageInfo>>(() async {
|
||||||
|
var pref = await SharedPreferences.getInstance();
|
||||||
|
var userID = pref.getInt('userid');
|
||||||
|
var msgUserList = pref.getStringList('${userID}msg');
|
||||||
|
List<MessageInfo> msgList = [];
|
||||||
|
if(msgUserList != null) {
|
||||||
|
for(var user in msgUserList) {
|
||||||
|
var targetUserID = int.parse(user);
|
||||||
|
var history = await localServiceRepository.fetchMessageHistory(userID: targetUserID, position: 0, num: 1);
|
||||||
|
if(history.isEmpty) {
|
||||||
|
msgList.add(MessageInfo(targetUser: targetUserID));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msgList.add(MessageInfo(targetUser: targetUserID, message: history[0]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msgList;
|
||||||
|
}).then((msgList) => emit(state.updateWithList(orderedNewMessages: msgList)))
|
||||||
|
.then((_) async => tcpRepository.pushRequest(FetchMessageRequest(
|
||||||
|
token: (await SharedPreferences.getInstance()).getInt('token'))
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
final LocalServiceRepository localServiceRepository;
|
final LocalServiceRepository localServiceRepository;
|
||||||
final TCPRepository tcpRepository;
|
final TCPRepository tcpRepository;
|
||||||
late final StreamSubscription subscription;
|
late final StreamSubscription subscription;
|
||||||
|
|
||||||
void addEmptyMessageOf({required int targetUser}) {
|
void addEmptyMessageOf({required int targetUser}) async {
|
||||||
if(state.messageList.any((element) => element.targetUser == targetUser)) {
|
if(state.messageList.any((element) => element.targetUser == targetUser)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var newList = [MessageInfo(targetUser: targetUser)];
|
var newList = [MessageInfo(targetUser: targetUser), ...state.messageList];
|
||||||
emit(MessageListState(messageList: newList..addAll(state.messageList)));
|
emit(MessageListState(messageList: newList));
|
||||||
|
var pref = await SharedPreferences.getInstance();
|
||||||
|
var currentUserID = pref.getInt('userid');
|
||||||
|
var msgUserList = pref.getStringList('${currentUserID}msg') ?? [];
|
||||||
|
msgUserList.remove('$targetUser');
|
||||||
|
msgUserList.insert(0, '$targetUser');
|
||||||
|
pref.setStringList('${currentUserID}msg', msgUserList);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> refresh() async {
|
||||||
|
tcpRepository.pushRequest(FetchMessageRequest(token: (await SharedPreferences.getInstance()).getInt('token')));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onResponse(TCPResponse response) async {
|
Future<void> _onResponse(TCPResponse response) async {
|
||||||
@ -42,10 +75,12 @@ class MessageListCubit extends Cubit<MessageListState> {
|
|||||||
if(response.status == TCPResponseStatus.ok) {
|
if(response.status == TCPResponseStatus.ok) {
|
||||||
var message = await localServiceRepository.fetchMessage(msgmd5: response.md5encoded!);
|
var message = await localServiceRepository.fetchMessage(msgmd5: response.md5encoded!);
|
||||||
if(message != null) {
|
if(message != null) {
|
||||||
var curUser = (await SharedPreferences.getInstance()).getInt('userid');
|
var pref = await SharedPreferences.getInstance();
|
||||||
|
var currentUserID = pref.getInt('userid');
|
||||||
|
var targetUser = message.senderID == currentUserID ? message.recieverID : message.senderID;
|
||||||
emit(state.updateWithSingle(messageInfo: MessageInfo(
|
emit(state.updateWithSingle(messageInfo: MessageInfo(
|
||||||
message: message,
|
message: message,
|
||||||
targetUser: message.senderID == curUser ? message.recieverID : message.senderID
|
targetUser: targetUser
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,6 +111,9 @@ class MessageListCubit extends Cubit<MessageListState> {
|
|||||||
|
|
||||||
//Use the meessage list to create new state
|
//Use the meessage list to create new state
|
||||||
emit(state.updateWithList(orderedNewMessages: latestMessages));
|
emit(state.updateWithList(orderedNewMessages: latestMessages));
|
||||||
|
|
||||||
|
var msgUserList = state.messageList.map((e) => e.targetUser.toString()).toList();
|
||||||
|
pref.setStringList('${curUser}msg', msgUserList);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -93,6 +131,10 @@ class MessageListCubit extends Cubit<MessageListState> {
|
|||||||
message: response.message
|
message: response.message
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
|
var msgUserList = pref.getStringList('${curUser}msg') ?? [];
|
||||||
|
msgUserList.remove('$targetUser');
|
||||||
|
msgUserList.insert(0, '$targetUser');
|
||||||
|
pref.setStringList('${curUser}msg', msgUserList);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: break;
|
default: break;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-12 23:37:49
|
* @Date : 2022-10-12 23:37:49
|
||||||
* @LastEditTime : 2022-10-15 00:55:49
|
* @LastEditTime : 2022-10-17 13:10:49
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -78,12 +78,12 @@ class MessageListState extends Equatable {
|
|||||||
}
|
}
|
||||||
while(insertListIndex < orderedNewMessages.length) {
|
while(insertListIndex < orderedNewMessages.length) {
|
||||||
if(addedUsers.contains(orderedNewMessages[insertListIndex].targetUser)) {
|
if(addedUsers.contains(orderedNewMessages[insertListIndex].targetUser)) {
|
||||||
origListIndex += 1;
|
insertListIndex += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
newList.add(orderedNewMessages[insertListIndex]);
|
newList.add(orderedNewMessages[insertListIndex]);
|
||||||
addedUsers.add(orderedNewMessages[insertListIndex].targetUser);
|
addedUsers.add(orderedNewMessages[insertListIndex].targetUser);
|
||||||
origListIndex += 1;
|
insertListIndex += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,24 +1,34 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-11 11:05:18
|
* @Date : 2022-10-11 11:05:18
|
||||||
* @LastEditTime : 2022-10-15 10:20:43
|
* @LastEditTime : 2022-10-17 13:35:35
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart';
|
||||||
import 'package:tcp_client/home/view/message_page/cubit/msg_list_cubit.dart';
|
import 'package:tcp_client/home/view/message_page/cubit/msg_list_cubit.dart';
|
||||||
import 'package:tcp_client/home/view/message_page/cubit/msg_list_state.dart';
|
import 'package:tcp_client/home/view/message_page/cubit/msg_list_state.dart';
|
||||||
import 'package:tcp_client/home/view/message_page/view/message_tile.dart';
|
import 'package:tcp_client/home/view/message_page/view/message_tile.dart';
|
||||||
|
|
||||||
class MessagePage extends StatelessWidget {
|
class MessagePage extends StatelessWidget {
|
||||||
const MessagePage({super.key});
|
MessagePage({super.key});
|
||||||
|
|
||||||
|
final RefreshController _refreshController = RefreshController(initialRefresh: false);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocBuilder<MessageListCubit, MessageListState>(
|
return BlocBuilder<MessageListCubit, MessageListState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return ListView.separated(
|
return SmartRefresher(
|
||||||
|
controller: _refreshController,
|
||||||
|
onRefresh: () async {
|
||||||
|
await context.read<MessageListCubit>().refresh();
|
||||||
|
_refreshController.refreshCompleted();
|
||||||
|
},
|
||||||
|
child: ListView.separated(
|
||||||
|
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
return MessageTile(
|
return MessageTile(
|
||||||
userID: state.messageList[index].targetUser,
|
userID: state.messageList[index].targetUser,
|
||||||
@ -31,7 +41,8 @@ class MessagePage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
itemCount: state.messageList.length
|
itemCount: state.messageList.length
|
||||||
);
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-13 13:17:52
|
* @Date : 2022-10-13 13:17:52
|
||||||
* @LastEditTime : 2022-10-15 01:05:11
|
* @LastEditTime : 2022-10-17 17:18:22
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -53,7 +53,9 @@ class MessageTile extends StatelessWidget {
|
|||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
UserAvatar(userid: userID),
|
IgnorePointer(
|
||||||
|
child: UserAvatar(userid: userID),
|
||||||
|
),
|
||||||
// if(userInfo.avatarEncoded != null && userInfo.avatarEncoded!.isEmpty)
|
// if(userInfo.avatarEncoded != null && userInfo.avatarEncoded!.isEmpty)
|
||||||
// Container(
|
// Container(
|
||||||
// decoration: BoxDecoration(
|
// decoration: BoxDecoration(
|
||||||
@ -97,16 +99,20 @@ class MessageTile extends StatelessWidget {
|
|||||||
vertical: 2.0,
|
vertical: 2.0,
|
||||||
horizontal: 0
|
horizontal: 0
|
||||||
),
|
),
|
||||||
child: UserNameText(userid: userID, fontWeight: FontWeight.bold,)
|
child: IgnorePointer(
|
||||||
|
child: UserNameText(userid: userID, fontWeight: FontWeight.bold,),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 2.0
|
vertical: 2.0
|
||||||
),
|
),
|
||||||
child: Text(
|
child: IgnorePointer(
|
||||||
message?.contentDecoded ?? '',
|
child: Text(
|
||||||
style: const TextStyle(
|
message?.contentDecoded ?? '',
|
||||||
fontSize: 16,
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -122,8 +128,10 @@ class MessageTile extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
child: Align(
|
child: Align(
|
||||||
alignment: Alignment.topCenter,
|
alignment: Alignment.topCenter,
|
||||||
child: Text(
|
child: IgnorePointer(
|
||||||
getTimeStamp(message!.timeStamp)
|
child: Text(
|
||||||
|
getTimeStamp(message!.timeStamp)
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-10 08:04:53
|
* @Date : 2022-10-10 08:04:53
|
||||||
* @LastEditTime : 2022-10-14 11:45:01
|
* @LastEditTime : 2022-10-17 13:03:55
|
||||||
* @Description :
|
* @Description :
|
||||||
*/
|
*/
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-11 10:56:02
|
* @Date : 2022-10-11 10:56:02
|
||||||
* @LastEditTime : 2022-10-15 11:48:54
|
* @LastEditTime : 2022-10-17 13:01:35
|
||||||
* @Description : Local Service Repository
|
* @Description : Local Service Repository
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -15,8 +15,11 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
import 'package:tcp_client/repositories/common_models/message.dart';
|
import 'package:tcp_client/repositories/common_models/message.dart';
|
||||||
import 'package:tcp_client/repositories/common_models/userinfo.dart';
|
import 'package:tcp_client/repositories/common_models/userinfo.dart';
|
||||||
import 'package:tcp_client/repositories/local_service_repository/models/local_file.dart';
|
import 'package:tcp_client/repositories/local_service_repository/models/local_file.dart';
|
||||||
|
//Windows platform
|
||||||
import 'package:sqflite_common/sqlite_api.dart';
|
import 'package:sqflite_common/sqlite_api.dart';
|
||||||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||||
|
//Android platform
|
||||||
|
// import 'package:sqflite/sqflite.dart';
|
||||||
|
|
||||||
class LocalServiceRepository {
|
class LocalServiceRepository {
|
||||||
late final Database _database;
|
late final Database _database;
|
||||||
@ -26,13 +29,18 @@ class LocalServiceRepository {
|
|||||||
}): _database = database;
|
}): _database = database;
|
||||||
|
|
||||||
static FutureOr<void> _onDatabaseCreate(Database db, int version) async {
|
static FutureOr<void> _onDatabaseCreate(Database db, int version) async {
|
||||||
await db.execute(
|
await db.transaction((txn) async {
|
||||||
|
await txn.execute(
|
||||||
'''
|
'''
|
||||||
create table users (
|
create table users (
|
||||||
userid integer primary key,
|
userid integer primary key,
|
||||||
username text not null,
|
username text not null,
|
||||||
avatar text
|
avatar text
|
||||||
);
|
);
|
||||||
|
'''
|
||||||
|
);
|
||||||
|
await txn.execute(
|
||||||
|
'''
|
||||||
create table msgs (
|
create table msgs (
|
||||||
userid integer not null,
|
userid integer not null,
|
||||||
targetid integer not null,
|
targetid integer not null,
|
||||||
@ -42,18 +50,46 @@ class LocalServiceRepository {
|
|||||||
md5encoded text primary key,
|
md5encoded text primary key,
|
||||||
filemd5 text
|
filemd5 text
|
||||||
);
|
);
|
||||||
|
'''
|
||||||
|
);
|
||||||
|
await txn.execute(
|
||||||
|
'''
|
||||||
create table files (
|
create table files (
|
||||||
filemd5 text primary key,
|
filemd5 text primary key,
|
||||||
dir text not null
|
dir text not null
|
||||||
);
|
);
|
||||||
'''
|
'''
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
// await db.execute(
|
||||||
|
// '''
|
||||||
|
// create table msgs (
|
||||||
|
// userid integer not null,
|
||||||
|
// targetid integer not null,
|
||||||
|
// contenttype text not null,
|
||||||
|
// content text not null,
|
||||||
|
// timestamp int not null,
|
||||||
|
// md5encoded text primary key,
|
||||||
|
// filemd5 text
|
||||||
|
// );
|
||||||
|
// create table users (
|
||||||
|
// userid integer primary key,
|
||||||
|
// username text not null,
|
||||||
|
// avatar text
|
||||||
|
// );
|
||||||
|
// create table files (
|
||||||
|
// filemd5 text primary key,
|
||||||
|
// dir text not null
|
||||||
|
// );
|
||||||
|
// '''
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<LocalServiceRepository> create({
|
static Future<LocalServiceRepository> create({
|
||||||
UserInfo? currentUser,
|
UserInfo? currentUser,
|
||||||
required String databaseFilePath
|
required String databaseFilePath
|
||||||
}) async {
|
}) async {
|
||||||
|
//Windows platform
|
||||||
var database = await databaseFactoryFfi.openDatabase(
|
var database = await databaseFactoryFfi.openDatabase(
|
||||||
databaseFilePath,
|
databaseFilePath,
|
||||||
options: OpenDatabaseOptions(
|
options: OpenDatabaseOptions(
|
||||||
@ -61,6 +97,12 @@ class LocalServiceRepository {
|
|||||||
onCreate: _onDatabaseCreate
|
onCreate: _onDatabaseCreate
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
//Android platform
|
||||||
|
// var database = await openDatabase(
|
||||||
|
// databaseFilePath,
|
||||||
|
// version: 1,
|
||||||
|
// onCreate: _onDatabaseCreate
|
||||||
|
// );
|
||||||
return LocalServiceRepository._internal(database: database);
|
return LocalServiceRepository._internal(database: database);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* @Author : Linloir
|
* @Author : Linloir
|
||||||
* @Date : 2022-10-11 09:42:05
|
* @Date : 2022-10-11 09:42:05
|
||||||
* @LastEditTime : 2022-10-15 11:06:05
|
* @LastEditTime : 2022-10-15 16:50:16
|
||||||
* @Description : TCP repository
|
* @Description : TCP repository
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -12,6 +12,7 @@ import 'dart:io';
|
|||||||
import 'package:async/async.dart';
|
import 'package:async/async.dart';
|
||||||
import 'package:crypto/crypto.dart';
|
import 'package:crypto/crypto.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:tcp_client/repositories/common_models/message.dart';
|
import 'package:tcp_client/repositories/common_models/message.dart';
|
||||||
import 'package:tcp_client/repositories/local_service_repository/models/local_file.dart';
|
import 'package:tcp_client/repositories/local_service_repository/models/local_file.dart';
|
||||||
@ -138,15 +139,17 @@ class TCPRepository {
|
|||||||
payloadLength = Uint8List.fromList(buffer.sublist(4, 8)).buffer.asInt32List()[0];
|
payloadLength = Uint8List.fromList(buffer.sublist(4, 8)).buffer.asInt32List()[0];
|
||||||
//Clear the length indicator bytes
|
//Clear the length indicator bytes
|
||||||
buffer.removeRange(0, 8);
|
buffer.removeRange(0, 8);
|
||||||
//Create temp file to read payload (might be huge)
|
|
||||||
Directory('${Directory.current.path}/.tmp').createSync();
|
|
||||||
//Create a pull stream for payload file
|
//Create a pull stream for payload file
|
||||||
_payloadPullStreamController = StreamController();
|
_payloadPullStreamController = StreamController();
|
||||||
//Create a future that listens to the status of the payload transmission
|
//Create a future that listens to the status of the payload transmission
|
||||||
() {
|
() {
|
||||||
var payloadPullStream = _payloadPullStreamController.stream;
|
var payloadPullStream = _payloadPullStreamController.stream;
|
||||||
var tempFile = File('${Directory.current.path}/.tmp/${DateTime.now().microsecondsSinceEpoch}')..createSync();
|
|
||||||
Future(() async {
|
Future(() async {
|
||||||
|
var documentDirectory = await getApplicationDocumentsDirectory();
|
||||||
|
//Create temp file to read payload (might be huge)
|
||||||
|
Directory('${documentDirectory.path}/ChatClient').createSync();
|
||||||
|
Directory('${documentDirectory.path}/ChatClient/.tmp').createSync();
|
||||||
|
var tempFile = File('${documentDirectory.path}/ChatClient/.tmp/${DateTime.now().microsecondsSinceEpoch}')..createSync();
|
||||||
await for(var data in payloadPullStream) {
|
await for(var data in payloadPullStream) {
|
||||||
await tempFile.writeAsBytes(data, mode: FileMode.append, flush: true);
|
await tempFile.writeAsBytes(data, mode: FileMode.append, flush: true);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,8 +7,10 @@ import Foundation
|
|||||||
|
|
||||||
import path_provider_macos
|
import path_provider_macos
|
||||||
import shared_preferences_macos
|
import shared_preferences_macos
|
||||||
|
import sqflite
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
15
pubspec.lock
15
pubspec.lock
@ -312,13 +312,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.3"
|
version: "6.0.3"
|
||||||
pull_to_refresh:
|
pull_to_refresh_flutter3:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: pull_to_refresh
|
name: pull_to_refresh_flutter3
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
version: "2.0.1"
|
||||||
scrollable_positioned_list:
|
scrollable_positioned_list:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -394,6 +394,13 @@ packages:
|
|||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.0"
|
version: "1.9.0"
|
||||||
|
sqflite:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: sqflite
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0+1"
|
||||||
sqflite_common:
|
sqflite_common:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -494,4 +501,4 @@ packages:
|
|||||||
version: "0.2.0+2"
|
version: "0.2.0+2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=2.18.2 <3.0.0"
|
dart: ">=2.18.2 <3.0.0"
|
||||||
flutter: ">=3.0.0"
|
flutter: ">=3.3.0-0"
|
||||||
|
|||||||
@ -33,6 +33,7 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
path_provider: ^2.0.11
|
path_provider: ^2.0.11
|
||||||
|
sqflite: ^2.1.0+1
|
||||||
sqflite_common_ffi: ^2.1.1+1
|
sqflite_common_ffi: ^2.1.1+1
|
||||||
sqflite_common: ^2.3.0
|
sqflite_common: ^2.3.0
|
||||||
crypto: ^3.0.2
|
crypto: ^3.0.2
|
||||||
@ -50,7 +51,7 @@ dependencies:
|
|||||||
async: ^2.9.0
|
async: ^2.9.0
|
||||||
stream_transform: ^2.0.1
|
stream_transform: ^2.0.1
|
||||||
flutter_slidable: ^2.0.0
|
flutter_slidable: ^2.0.0
|
||||||
pull_to_refresh: ^2.0.0
|
pull_to_refresh_flutter3: ^2.0.1
|
||||||
azlistview: ^2.0.0
|
azlistview: ^2.0.0
|
||||||
lpinyin: ^2.0.3
|
lpinyin: ^2.0.3
|
||||||
easy_debounce: ^2.0.2+1
|
easy_debounce: ^2.0.2+1
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user