More Functions

- message list persistence
- bug fix for contact list tag
This commit is contained in:
Linloir 2022-10-17 17:47:17 +08:00
parent c3d0a91c48
commit e7f690159c
No known key found for this signature in database
GPG Key ID: 58EEB209A0F2C366
19 changed files with 255 additions and 108 deletions

View File

@ -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 {

View File

@ -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>

View File

@ -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)]);

View File

@ -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
);
} }
} }

View File

@ -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))
),
)
); );
} }
} }

View File

@ -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,

View File

@ -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;
} }

View File

@ -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 = '#';
} }

View File

@ -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,)
),
) )
), ),
], ],

View File

@ -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;

View File

@ -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;
} }

View File

@ -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
); ),
);
} }
); );
} }

View File

@ -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)
),
), ),
), ),
), ),

View File

@ -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';

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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"))
} }

View File

@ -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"

View File

@ -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