Browse Source

add a robot assistant

pull/6/head
Sun 4 years ago
parent
commit
1d4b1ab266
  1. BIN
      assets/logo/logo_esse.jpg
  2. 391
      lib/apps/assistant/message.dart
  3. 125
      lib/apps/assistant/models.dart
  4. 46
      lib/apps/assistant/page.dart
  5. 77
      lib/apps/assistant/provider.dart
  6. 31
      lib/apps/chat/provider.dart
  7. 2
      lib/apps/device/provider.dart
  8. 19
      lib/apps/service/list.dart
  9. 2
      lib/main.dart
  10. 0
      lib/pages/file.dart
  11. 1
      pubspec.yaml
  12. 11
      src/apps/assistant/migrate.rs
  13. 17
      src/apps/assistant/mod.rs
  14. 183
      src/apps/assistant/models.rs
  15. 85
      src/apps/assistant/rpc.rs
  16. 201
      src/migrate.rs
  17. 19
      src/migrate/account.rs
  18. 6
      src/migrate/consensus.rs
  19. 2
      src/migrate/file.rs
  20. 1
      src/migrate/service.rs
  21. 6
      src/migrate/session.rs
  22. 15
      src/storage.rs

BIN
assets/logo/logo_esse.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

391
lib/apps/assistant/message.dart

@ -0,0 +1,391 @@ @@ -0,0 +1,391 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:provider/provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:open_file/open_file.dart';
import 'package:esse/l10n/localizations.dart';
import 'package:esse/utils/adaptive.dart';
import 'package:esse/utils/file_image.dart';
import 'package:esse/utils/better_print.dart';
import 'package:esse/widgets/avatar.dart';
import 'package:esse/widgets/audio_player.dart';
import 'package:esse/widgets/shadow_dialog.dart';
import 'package:esse/global.dart';
import 'package:esse/apps/assistant/models.dart';
class AssistantMessage extends StatelessWidget {
final String name;
final Message message;
const AssistantMessage({Key key, this.name, this.message}): super(key: key);
Widget _showText(context, color, isDesktop, content, isMe) {
final width = MediaQuery.of(context).size.width * 0.6;
// text
return Container(
constraints: BoxConstraints(minWidth: 50, maxWidth: isDesktop ? width - 300.0 : width),
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 14.0),
decoration: BoxDecoration(
color: isMe ? color.primary : color.primaryVariant,
borderRadius: BorderRadius.circular(15.0),
),
child: Text(content,
style: TextStyle(
color: isMe ? Colors.white : Color(0xFF1C1939),
fontSize: 14.0)));
}
Widget _showImage(context, lang, color, content) {
// image
bool imageExsit = true;
var thumImage;
final imagePath = Global.imagePath + content;
final thumPath = Global.thumbPath + content;
if (FileSystemEntity.typeSync(thumPath) ==
FileSystemEntityType.notFound) {
imageExsit = false;
thumImage = AssetImage('assets/images/image_missing.png');
} else {
thumImage = FileImage(File(thumPath));
}
return GestureDetector(
onTap: imageExsit
? () => showShadowDialog(
context,
Icons.image_rounded,
lang.album,
Column(children: [
Image(image: FileImage(File(imagePath)), fit: BoxFit.cover),
SizedBox(height: 15.0),
if (Platform.isAndroid || Platform.isIOS)
InkWell(
onTap: () async {
Map<Permission, PermissionStatus> statuses = await [
Permission.storage,
].request();
if (statuses[Permission.storage] == PermissionStatus.granted) {
final result = await ImageGallerySaver.saveFile(imagePath);
print(result);
Navigator.pop(context);
}
},
hoverColor: Colors.transparent,
child: Container(
width: 200.0,
padding: const EdgeInsets.symmetric(vertical: 10.0),
decoration: BoxDecoration(
border: Border.all(color: color.primary),
borderRadius: BorderRadius.circular(10.0)),
child: Center(child: Text(lang.download,
style: TextStyle(fontSize: 14.0, color: color.primary))),
)
),
]))
: () => {},
child: Container(
width: imageExsit ? 120.0 : 60.0,
child: Image(image: thumImage, fit: BoxFit.cover),
));
}
Widget _showFile(context, lang, color, content) {
// file
bool fileExsit = true;
Widget fileImage;
final filePath = Global.filePath + content;
if (FileSystemEntity.typeSync(filePath) ==
FileSystemEntityType.notFound) {
fileExsit = false;
fileImage = Image(image: AssetImage('assets/images/image_missing.png'), fit: BoxFit.cover);
} else {
fileImage = fileIcon(content, 36.0);
}
return GestureDetector(
onTap: fileExsit
? () => showShadowDialog(
context,
Icons.folder_rounded,
lang.files,
Column(children: [
Text(content),
SizedBox(height: 15.0),
Container(
height: 100.0,
child: fileImage,
),
SizedBox(height: 15.0),
InkWell(
onTap: () => OpenFile.open(filePath),
hoverColor: Colors.transparent,
child: Container(
width: 200.0,
padding: const EdgeInsets.symmetric(vertical: 10.0),
decoration: BoxDecoration(
border: Border.all(color: color.primary),
borderRadius: BorderRadius.circular(10.0)),
child: Center(child: Text(lang.open,
style: TextStyle(fontSize: 14.0, color: color.primary))),
)
),
]))
: () => {},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0),
decoration: BoxDecoration(
color: const Color(0x40ADB0BB),
borderRadius: BorderRadius.circular(15.0),
),
child: Row(mainAxisSize: MainAxisSize.min, children: [
Container(
height: 36.0,
child: fileImage,
),
Container(
padding: const EdgeInsets.only(left: 5.0),
width: 120.0,
child: Text(content,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: fileExsit
? TextStyle(
color: color.onPrimary,
fontSize: 14.0,
)
: TextStyle(
color: color.onPrimary.withOpacity(0.5),
decoration: TextDecoration.lineThrough,
fontSize: 14.0,
)),
),
])));
}
Widget _showRecord(content) {
final raws = Message.showRecordTime(content);
// text
return Container(
width: 120.0,
padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
decoration: BoxDecoration(
color: const Color(0x40ADB0BB),
borderRadius: BorderRadius.circular(15.0),
),
child: RecordPlayer(path: Global.recordPath + raws[1], time: raws[0]),
);
}
Widget _showContact(context, lang, color, content) {
// contact [name, gid, addr, avatar]
final infos = Message.showContact(content);
final gid = 'EH' + infos[1].toUpperCase();
if (infos != null) {
return GestureDetector(
onTap: () => showShadowDialog(
context,
Icons.person_rounded,
lang.contact,
Column(children: [
Avatar(
width: 100.0,
name: infos[0],
avatarPath: infos[3],
needOnline: false
),
const SizedBox(height: 10.0),
Text(infos[0]),
const SizedBox(height: 10.0),
const Divider(height: 1.0, color: Color(0x40ADB0BB)),
const SizedBox(height: 10.0),
_infoListTooltip(Icons.person, color.primary, gid),
_infoListTooltip(Icons.location_on, color.primary, "0x" + infos[2]),
Container(
width: 300.0,
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: Row(
children: [
Icon(Icons.turned_in, size: 20.0, color: color.primary),
const SizedBox(width: 20.0),
Expanded(child: Text(lang.fromContactCard(name))),
]
),
),
const SizedBox(height: 20.0),
InkWell(
onTap: () => Navigator.pop(context),
hoverColor: Colors.transparent,
child: Container(
width: 200.0,
padding: const EdgeInsets.symmetric(vertical: 10.0),
decoration: BoxDecoration(
border: Border.all(color: color.primary),
borderRadius: BorderRadius.circular(10.0)),
child: Center(child: Text(lang.ok,
style: TextStyle(fontSize: 14.0, color: color.primary))),
)
),
]
)
),
child: Container(
padding: const EdgeInsets.symmetric(
vertical: 10.0, horizontal: 10.0),
width: 200.0,
decoration: BoxDecoration(
color: const Color(0x40ADB0BB),
borderRadius: BorderRadius.circular(15.0),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(children: [
Avatar(
width: 40.0,
name: infos[0],
avatarPath: infos[3],
needOnline: false),
Container(
width: 135.0,
padding: const EdgeInsets.only(left: 10.0),
child: Column(children: [
Text(infos[0],
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: color.onPrimary, fontSize: 16.0)),
SizedBox(height: 5.0),
Text(betterPrint(gid),
style: TextStyle(
color: Colors.grey, fontSize: 12.0)),
])),
]),
SizedBox(height: 5.0),
const Divider(height: 1.0, color: Color(0x40ADB0BB)),
SizedBox(height: 3.0),
Text(lang.contactCard,
style: TextStyle(color: Colors.grey, fontSize: 10.0)),
])));
} else {
return Container(
padding:
const EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
width: 200.0,
decoration: BoxDecoration(
color: const Color(0x40ADB0BB),
borderRadius: BorderRadius.circular(15.0),
),
child:
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Row(children: [
Container(
height: 35.0,
child: Image(
image: AssetImage('assets/images/image_missing.png'),
fit: BoxFit.cover),
),
Container(
width: 130.0,
padding: const EdgeInsets.only(left: 10.0),
child: Text(content,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: color.onPrimary.withOpacity(0.5),
decoration: TextDecoration.lineThrough,
fontSize: 16.0)),
),
]),
SizedBox(height: 5.0),
const Divider(height: 1.0, color: Color(0x40ADB0BB)),
SizedBox(height: 3.0),
Text(lang.contactCard,
style: TextStyle(color: Colors.grey, fontSize: 10.0)),
]));
}
}
Widget _infoListTooltip(icon, color, text) {
return Container(
width: 300.0,
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: Row(
children: [
Icon(icon, size: 20.0, color: color),
const SizedBox(width: 20.0),
Expanded(
child: Tooltip(
message: text,
child: Text(betterPrint(text)),
)
)
]
),
);
}
Widget _show(context, color, lang, isDesktop, type, content, isMe) {
if (type == MessageType.String) {
return _showText(context, color, isDesktop, content, isMe);
} else if (type == MessageType.Image) {
return _showImage(context, lang, color, content);
} else if (type == MessageType.File) {
return _showFile(context, lang, color, content);
} else if (type == MessageType.Contact) {
return _showContact(context, lang, color, content);
} else if (type == MessageType.Record) {
return _showRecord(content);
}
return _showText(context, color, isDesktop, content, isMe);
}
@override
Widget build(BuildContext context) {
final color = Theme.of(context).colorScheme;
final lang = AppLocalizations.of(context);
final isDesktop = isDisplayDesktop(context);
final qShow = _show(context, color, lang, isDesktop, message.q_type, message.q_content, true);
final aShow = _show(context, color, lang, isDesktop, message.a_type, message.a_content, false);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: Column(children: [
Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
Expanded(
child: Align(
alignment: Alignment.topRight,
child: qShow,
),
),
]),
Container(
padding: EdgeInsets.only(top: 10.0),
child: Row(children: [
Spacer(),
Text(message.time.toString(),
style: TextStyle(
color: color.onPrimary.withOpacity(0.5),
fontSize: 12.0)),
SizedBox(width: 4.0),
Icon(
message.a_content.length == 0 ? Icons.hourglass_top : Icons.done,
size: 12.0,
color: color.primary
),
])),
const SizedBox(height: 4.0),
Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
Expanded(
child: Align(
alignment: Alignment.topLeft,
child: aShow,
),
),
]),
]));
}
}

125
lib/apps/assistant/models.dart

@ -0,0 +1,125 @@ @@ -0,0 +1,125 @@
import 'package:esse/utils/relative_time.dart';
import 'package:esse/global.dart';
enum MessageType {
String,
Image,
File,
Contact,
Emoji,
Record,
}
// use 00-99
extension MessageTypeExtension on MessageType {
int toInt() {
switch (this) {
case MessageType.String:
return 0;
case MessageType.Image:
return 1;
case MessageType.File:
return 2;
case MessageType.Contact:
return 3;
case MessageType.Emoji:
return 4;
case MessageType.Record:
return 5;
default:
return 0;
}
}
static MessageType fromInt(int s) {
switch (s) {
case 0:
return MessageType.String;
case 1:
return MessageType.Image;
case 2:
return MessageType.File;
case 3:
return MessageType.Contact;
case 4:
return MessageType.Emoji;
case 5:
return MessageType.Record;
default:
return MessageType.String;
}
}
}
class Message {
int id;
MessageType q_type;
String q_content;
MessageType a_type;
String a_content;
RelativeTime time = RelativeTime();
Message(this.q_type, this.q_content);
static List showContact(String content) {
var name = '';
var did = '';
var addr = '';
var i_name = content.indexOf(';;');
if (i_name > 0) {
name = content.substring(0, i_name).replaceAll('-;', ';');
}
var raw = content.substring(i_name + 2);
var i_did = raw.indexOf(';;');
if (i_did > 0) {
did = raw.substring(0, i_did);
}
addr = raw.substring(i_did + 2);
return [name, did, addr, Global.avatarPath + did + '.png'];
}
static String rawRecordName(int time, String name) {
return time.toString() + '-' + name;
}
static List showRecordTime(String content) {
final len = content.indexOf('-');
if (len > 0) {
final time = int.parse(content.substring(0, len));
final path = content.substring(len + 1);
return [time, path];
} else {
return [0, content];
}
}
String shortShow() {
switch (this.q_type) {
case MessageType.Image:
return '[IMAGE]';
case MessageType.Record:
return '[RECORD]';
case MessageType.Contact:
return '[CONTACT CARD]';
default:
return this.q_content;
}
}
Message.fromList(List params) {
this.id = params[0];
this.q_type = MessageTypeExtension.fromInt(params[1]);
this.q_content = params[2];
this.a_type = MessageTypeExtension.fromInt(params[3]);
this.a_content = params[4];
this.time = RelativeTime.fromInt(params[5]);
}
update(List params) {
// params[0] is id.
this.a_type = MessageTypeExtension.fromInt(params[1]);
this.a_content = params[2];
}
}

46
lib/apps/assistant/pages.dart → lib/apps/assistant/page.dart

@ -6,16 +6,17 @@ import 'package:esse/utils/toast.dart'; @@ -6,16 +6,17 @@ import 'package:esse/utils/toast.dart';
import 'package:esse/utils/pick_image.dart';
import 'package:esse/utils/pick_file.dart';
import 'package:esse/l10n/localizations.dart';
import 'package:esse/models/friend.dart';
import 'package:esse/models/message.dart';
import 'package:esse/widgets/emoji.dart';
import 'package:esse/widgets/shadow_dialog.dart';
import 'package:esse/widgets/audio_recorder.dart';
import 'package:esse/widgets/user_info.dart';
import 'package:esse/widgets/chat_message.dart';
import 'package:esse/provider/account.dart';
import 'package:esse/global.dart';
import 'package:esse/apps/chat/provider.dart';
import 'package:esse/apps/assistant/models.dart';
import 'package:esse/apps/assistant/provider.dart';
import 'package:esse/apps/assistant/message.dart';
class AssistantPage extends StatefulWidget {
const AssistantPage({Key key}) : super(key: key);
@ -35,6 +36,9 @@ class _AssistantPageState extends State<AssistantPage> { @@ -35,6 +36,9 @@ class _AssistantPageState extends State<AssistantPage> {
@override
initState() {
super.initState();
Future.delayed(Duration.zero, () {
Provider.of<AssistantProvider>(context, listen: false).actived();
});
textFocus.addListener(() {
if (textFocus.hasFocus) {
setState(() {
@ -46,6 +50,12 @@ class _AssistantPageState extends State<AssistantPage> { @@ -46,6 +50,12 @@ class _AssistantPageState extends State<AssistantPage> {
});
}
@override
void deactivate() {
Provider.of<AssistantProvider>(context, listen: false).inactived();
super.deactivate();
}
_generateRecordPath() {
this._recordName = DateTime.now().millisecondsSinceEpoch.toString() + '_assistant.m4a';
}
@ -55,7 +65,7 @@ class _AssistantPageState extends State<AssistantPage> { @@ -55,7 +65,7 @@ class _AssistantPageState extends State<AssistantPage> {
return;
}
// TODO send
context.read<AssistantProvider>().create(MessageType.String, textController.text);
setState(() {
textController.text = '';
@ -75,7 +85,7 @@ class _AssistantPageState extends State<AssistantPage> { @@ -75,7 +85,7 @@ class _AssistantPageState extends State<AssistantPage> {
void _sendImage() async {
final image = await pickImage();
if (image != null) {
// TODO send
context.read<AssistantProvider>().create(MessageType.Image, image);
}
setState(() {
textFocus.requestFocus();
@ -89,7 +99,7 @@ class _AssistantPageState extends State<AssistantPage> { @@ -89,7 +99,7 @@ class _AssistantPageState extends State<AssistantPage> {
void _sendFile() async {
final file = await pickFile();
if (file != null) {
// TODO send
context.read<AssistantProvider>().create(MessageType.File, file);
}
setState(() {
textFocus.requestFocus();
@ -102,7 +112,7 @@ class _AssistantPageState extends State<AssistantPage> { @@ -102,7 +112,7 @@ class _AssistantPageState extends State<AssistantPage> {
void _sendRecord(int time) async {
final raw = Message.rawRecordName(time, _recordName);
// TODO send
context.read<AssistantProvider>().create(MessageType.Record, raw);
setState(() {
textFocus.requestFocus();
@ -147,7 +157,7 @@ class _AssistantPageState extends State<AssistantPage> { @@ -147,7 +157,7 @@ class _AssistantPageState extends State<AssistantPage> {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () async {
// TODO send
context.read<AssistantProvider>().create(MessageType.Contact, "${contact.id}");
Navigator.of(context).pop();
setState(() {
textFocus.requestFocus();
@ -178,8 +188,7 @@ class _AssistantPageState extends State<AssistantPage> { @@ -178,8 +188,7 @@ class _AssistantPageState extends State<AssistantPage> {
final color = Theme.of(context).colorScheme;
final lang = AppLocalizations.of(context);
final isDesktop = isDisplayDesktop(context);
final recentMessages = {};
final recentMessages = context.watch<AssistantProvider>().messages;
final recentMessageKeys = recentMessages.keys.toList().reversed.toList();
return Column(
@ -238,22 +247,11 @@ class _AssistantPageState extends State<AssistantPage> { @@ -238,22 +247,11 @@ class _AssistantPageState extends State<AssistantPage> {
onSelected: (int value) {
if (value == 1) {
// TODO set top
} else if (value == 2) {
showShadowDialog(
context,
Icons.info,
lang.friendInfo,
UserInfo(
id: 'ES0000000000000000000000000000000000000000000000000000000000000000',
name: 'esse',
addr: '0x0000000000000000000000000000000000000000000000000000000000000000')
);
}
},
itemBuilder: (context) {
return <PopupMenuEntry<int>>[
_menuItem(color.primary, 1, Icons.vertical_align_top_rounded, lang.cancelTop),
_menuItem(color.primary, 2, Icons.qr_code_rounded, lang.friendInfo),
];
},
)
@ -266,7 +264,7 @@ class _AssistantPageState extends State<AssistantPage> { @@ -266,7 +264,7 @@ class _AssistantPageState extends State<AssistantPage> {
padding: EdgeInsets.symmetric(horizontal: 20.0),
itemCount: recentMessageKeys.length,
reverse: true,
itemBuilder: (BuildContext context, index) => ChatMessage(
itemBuilder: (BuildContext context, index) => AssistantMessage(
name: 'esse',
message: recentMessages[recentMessageKeys[index]],
)
@ -416,7 +414,7 @@ class _AssistantPageState extends State<AssistantPage> { @@ -416,7 +414,7 @@ class _AssistantPageState extends State<AssistantPage> {
icon: Icons.person_rounded,
text: lang.contact,
action: () => _sendContact(color, lang,
context.read<AccountProvider>().friends.values),
context.read<ChatProvider>().friends.values),
bgColor: color.surface,
iconColor: color.primary),
],

77
lib/apps/assistant/provider.dart

@ -0,0 +1,77 @@ @@ -0,0 +1,77 @@
import "dart:collection";
import 'package:flutter/material.dart';
import 'package:esse/rpc.dart';
import 'package:esse/apps/assistant/models.dart';
class AssistantProvider extends ChangeNotifier {
bool isActived = false;
SplayTreeMap<int, Message> messages = SplayTreeMap();
AssistantProvider() {
// rpc.
rpc.addListener('assistant-list', _list, false);
rpc.addListener('assistant-create', _create, false);
rpc.addListener('assistant-update', _update, false);
rpc.addListener('assistant-delete', _delete, false);
}
actived() {
this.isActived = true;
rpc.send('assistant-list', []);
}
inactived() {
this.messages.clear();
this.isActived = false;
print("============");
}
create(MessageType q_type, String q_content) {
rpc.send('assistant-create', [q_type.toInt(), q_content]);
notifyListeners();
}
/// delete a message.
delete(int id) {
this.messages.remove(id);
rpc.send('assistant-delete', [id]);
notifyListeners();
}
/// list message with friend.
_list(List params) {
if (this.isActived) {
params.forEach((param) {
this.messages[param[0]] = Message.fromList(param);
});
notifyListeners();
}
}
/// friend send message to me.
_create(List params) {
if (this.isActived) {
final msg = Message.fromList(params);
this.messages[msg.id] = msg;
notifyListeners();
}
}
_update(List params) {
if (this.isActived) {
final id = params[0];
if (this.messages.containsKey(id)) {
this.messages[id].update(params);
notifyListeners();
}
}
}
_delete(List params) {
if (this.isActived) {
this.messages.remove(params[0]);
notifyListeners();
}
}
}

31
lib/apps/chat/provider.dart

@ -4,7 +4,6 @@ import 'dart:typed_data'; @@ -4,7 +4,6 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:esse/utils/relative_time.dart';
import 'package:esse/global.dart';
import 'package:esse/rpc.dart';
import 'package:esse/apps/chat/models.dart';
@ -186,7 +185,7 @@ class ChatProvider extends ChangeNotifier { @@ -186,7 +185,7 @@ class ChatProvider extends ChangeNotifier {
}
/// list all friends.
_friendList(List params) async {
_friendList(List params) {
this.orderKeys.clear();
this.friends.clear();
@ -201,7 +200,7 @@ class ChatProvider extends ChangeNotifier { @@ -201,7 +200,7 @@ class ChatProvider extends ChangeNotifier {
notifyListeners();
}
_friendOnline(List params) async {
_friendOnline(List params) {
final id = params[0];
if (this.friends.containsKey(id)) {
this.friends[id].online = true;
@ -210,7 +209,7 @@ class ChatProvider extends ChangeNotifier { @@ -210,7 +209,7 @@ class ChatProvider extends ChangeNotifier {
}
}
_friendOffline(List params) async {
_friendOffline(List params) {
final id = params[0];
if (this.friends.containsKey(id)) {
this.friends[id].online = false;
@ -218,7 +217,7 @@ class ChatProvider extends ChangeNotifier { @@ -218,7 +217,7 @@ class ChatProvider extends ChangeNotifier {
}
}
_friendInfo(List params) async {
_friendInfo(List params) {
final id = params[0];
this.friends[id] = Friend.fromList(params);
if (this.friends[id].isTop) {
@ -227,7 +226,7 @@ class ChatProvider extends ChangeNotifier { @@ -227,7 +226,7 @@ class ChatProvider extends ChangeNotifier {
notifyListeners();
}
_friendUpdate(List params) async {
_friendUpdate(List params) {
final id = params[0];
if (this.friends.containsKey(id)) {
this.friends[id].isTop = params[1];
@ -240,7 +239,7 @@ class ChatProvider extends ChangeNotifier { @@ -240,7 +239,7 @@ class ChatProvider extends ChangeNotifier {
}
}
_friendClose(List params) async {
_friendClose(List params) {
final id = params[0];
if (this.friends.containsKey(id)) {
this.friends[id].isClosed = true;
@ -250,7 +249,7 @@ class ChatProvider extends ChangeNotifier { @@ -250,7 +249,7 @@ class ChatProvider extends ChangeNotifier {
}
/// list requests for friend.
_requestList(List params) async {
_requestList(List params) {
this.requests.clear();
params.forEach((param) {
if (param.length == 10) {
@ -261,13 +260,13 @@ class ChatProvider extends ChangeNotifier { @@ -261,13 +260,13 @@ class ChatProvider extends ChangeNotifier {
}
/// receive a request for friend.
_requestCreate(List params) async {
_requestCreate(List params) {
this.requests[params[0]] = Request.fromList(params);
notifyListeners();
}
/// created request had delivery.
_requestDelivery(List params) async {
_requestDelivery(List params) {
final id = params[0];
final isDelivery = params[1];
if (this.requests.containsKey(id)) {
@ -277,7 +276,7 @@ class ChatProvider extends ChangeNotifier { @@ -277,7 +276,7 @@ class ChatProvider extends ChangeNotifier {
}
/// request for friend receive agree.
_requestAgree(List params) async {
_requestAgree(List params) {
final id = params[0]; // request's id.
if (this.requests.containsKey(id)) {
this.requests[id].overIt(true);
@ -289,7 +288,7 @@ class ChatProvider extends ChangeNotifier { @@ -289,7 +288,7 @@ class ChatProvider extends ChangeNotifier {
}
/// request for friend receive reject.
_requestReject(List params) async {
_requestReject(List params) {
final id = params[0];
if (this.requests.containsKey(id)) {
this.requests[id].overIt(false);
@ -297,13 +296,13 @@ class ChatProvider extends ChangeNotifier { @@ -297,13 +296,13 @@ class ChatProvider extends ChangeNotifier {
}
}
_requestDelete(List params) async {
_requestDelete(List params) {
this.requests.remove(params[0]);
notifyListeners();
}
/// list message with friend.
_messageList(List params) async {
_messageList(List params) {
params.forEach((param) {
if (param.length == 8) {
this.activedMessages[param[0]] = Message.fromList(param);
@ -313,7 +312,7 @@ class ChatProvider extends ChangeNotifier { @@ -313,7 +312,7 @@ class ChatProvider extends ChangeNotifier {
}
/// friend send message to me.
_messageCreate(List params) async {
_messageCreate(List params) {
final msg = Message.fromList(params);
if (msg.fid == this.activedFriendId) {
if (!msg.isDelivery) {
@ -337,7 +336,7 @@ class ChatProvider extends ChangeNotifier { @@ -337,7 +336,7 @@ class ChatProvider extends ChangeNotifier {
}
/// created message had delivery.
_messageDelivery(List params) async {
_messageDelivery(List params) {
final id = params[0];
final isDelivery = params[1];
if (this.activedMessages.containsKey(id)) {

2
lib/apps/device/provider.dart

@ -1,8 +1,6 @@ @@ -1,8 +1,6 @@
import 'package:flutter/material.dart';
import 'package:esse/global.dart';
import 'package:esse/rpc.dart';
import 'package:esse/apps/device/models.dart';
class DeviceProvider extends ChangeNotifier {

19
lib/apps/service/list.dart

@ -5,6 +5,9 @@ import 'package:esse/utils/adaptive.dart'; @@ -5,6 +5,9 @@ import 'package:esse/utils/adaptive.dart';
import 'package:esse/l10n/localizations.dart';
import 'package:esse/provider.dart';
import 'package:esse/apps/assistant/page.dart';
import 'package:esse/apps/assistant/provider.dart';
class ServiceList extends StatefulWidget {
const ServiceList({Key key}) : super(key: key);
@ -15,7 +18,7 @@ class ServiceList extends StatefulWidget { @@ -15,7 +18,7 @@ class ServiceList extends StatefulWidget {
class _ServiceListState extends State<ServiceList> {
@override
Widget build(BuildContext context) {
final serviceKeys = [];
final serviceKeys = [1];
final services = {};
return Expanded(
@ -38,12 +41,12 @@ class _ListService extends StatelessWidget { @@ -38,12 +41,12 @@ class _ListService extends StatelessWidget {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
// final widget = AssistantPage();
// if (isDesktop) {
// Provider.of<AccountProvider>(context, listen: false).updateActivedApp(widget);
// } else {
// Navigator.push(context, MaterialPageRoute(builder: (_) => widget));
// }
final widget = AssistantPage();
if (isDesktop) {
Provider.of<AccountProvider>(context, listen: false).updateActivedApp(widget);
} else {
Navigator.push(context, MaterialPageRoute(builder: (_) => widget));
}
},
child: Container(
height: 55.0,
@ -55,7 +58,7 @@ class _ListService extends StatelessWidget { @@ -55,7 +58,7 @@ class _ListService extends StatelessWidget {
margin: const EdgeInsets.only(left: 20.0, right: 15.0),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/logo/logo_light.png'),
image: AssetImage('assets/logo/logo_esse.jpg'),
fit: BoxFit.cover,
),
borderRadius: BorderRadius.circular(15.0)

2
lib/main.dart

@ -13,6 +13,7 @@ import 'package:esse/rpc.dart'; @@ -13,6 +13,7 @@ import 'package:esse/rpc.dart';
import 'package:esse/provider.dart';
import 'package:esse/apps/device/provider.dart';
import 'package:esse/apps/chat/provider.dart';
import 'package:esse/apps/assistant/provider.dart';
void coreServer() async {
final path = await homeDir();
@ -40,6 +41,7 @@ void main() { @@ -40,6 +41,7 @@ void main() {
ChangeNotifierProvider(create: (_) => AccountProvider()),
ChangeNotifierProvider(create: (_) => DeviceProvider()),
ChangeNotifierProvider(create: (_) => ChatProvider()),
ChangeNotifierProvider(create: (_) => AssistantProvider()),
],
child: MyApp(),
));

0
lib/pages/file.dart

1
pubspec.yaml

@ -70,6 +70,7 @@ flutter: @@ -70,6 +70,7 @@ flutter:
- assets/logo/logo_dark.png
- assets/logo/logo_light.png
- assets/logo/logo_40.jpg
- assets/logo/logo_esse.jpg
- assets/images/background_light.jpg
- assets/images/background_dark.jpg
- assets/images/image_missing.png

11
src/apps/assistant/migrate.rs

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
#[rustfmt::skip]
pub(crate) const ASSISTANT_VERSIONS: [&str; 1] = [
"CREATE TABLE IF NOT EXISTS messages(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
q_type INTEGER NOT NULL,
q_content TEXT NOT NULL,
a_type INTEGER NOT NULL,
a_content TEXT NOT NULL,
datetime INTEGER NOT NULL,
is_deleted INTEGER NOT NULL);",
];

17
src/apps/assistant/mod.rs

@ -1,12 +1,7 @@ @@ -1,12 +1,7 @@
use tdn::types::{
primitive::HandleResult,
rpc::{json, RpcHandler},
};
mod migrate;
mod models;
pub(crate) mod rpc;
use crate::rpc::RpcState;
pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
handler.add_method("assistant-echo", |_, params, _| async move {
Ok(HandleResult::rpc(json!(params)))
});
}
pub(crate) use migrate::ASSISTANT_VERSIONS;
pub(crate) use models::{Message, MessageType};
pub(crate) use rpc::new_rpc_handler;

183
src/apps/assistant/models.rs

@ -0,0 +1,183 @@ @@ -0,0 +1,183 @@
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
use tdn::types::{
group::GroupId,
primitive::{new_io_error, Result},
rpc::{json, RpcParam},
};
use tdn_storage::local::{DStorage, DsValue};
use crate::apps::chat::Friend;
use crate::storage::{read_file, session_db, write_file, write_image};
#[derive(Eq, PartialEq, Clone)]
pub(crate) enum MessageType {
String,
Image,
File,
Contact,
Emoji,
Record,
}
impl MessageType {
pub fn to_int(&self) -> i64 {
match self {
MessageType::String => 0,
MessageType::Image => 1,
MessageType::File => 2,
MessageType::Contact => 3,
MessageType::Emoji => 4,
MessageType::Record => 5,
}
}
pub fn from_int(i: i64) -> MessageType {
match i {
0 => MessageType::String,
1 => MessageType::Image,
2 => MessageType::File,
3 => MessageType::Contact,
4 => MessageType::Emoji,
5 => MessageType::Record,
_ => MessageType::String,
}
}
pub async fn handle(
&self,
base: &PathBuf,
mgid: &GroupId,
content: String,
) -> std::result::Result<String, tdn::types::rpc::RpcError> {
match self {
MessageType::String => Ok(content),
MessageType::Image => {
let bytes = read_file(&PathBuf::from(content)).await?;
let image_name = write_image(base, &mgid, &bytes).await?;
Ok(image_name)
}
MessageType::File => {
let file_path = PathBuf::from(content);
let bytes = read_file(&file_path).await?;
let old_name = file_path.file_name()?.to_str()?;
let filename = write_file(base, mgid, old_name, &bytes).await?;
Ok(filename)
}
MessageType::Contact => {
let cid: i64 = content.parse().map_err(|_e| new_io_error("id error"))?;
let db = session_db(base, mgid)?;
let contact = Friend::get_id(&db, cid)??;
db.close()?;
let tmp_name = contact.name.replace(";", "-;");
Ok(format!(
"{};;{};;{}",
tmp_name,
contact.gid.to_hex(),
contact.addr.to_hex()
))
}
MessageType::Record => Ok(content),
MessageType::Emoji => Ok(content),
}
}
}
pub(crate) struct Message {
pub id: i64,
pub q_type: MessageType,
pub q_content: String,
pub a_type: MessageType,
pub a_content: String,
pub datetime: i64,
}
impl Message {
pub fn new(
q_type: MessageType,
q_content: String,
a_type: MessageType,
a_content: String,
) -> Message {
let start = SystemTime::now();
let datetime = start
.duration_since(UNIX_EPOCH)
.map(|s| s.as_secs())
.unwrap_or(0) as i64; // safe for all life.
Message {
id: 0,
q_type,
q_content,
a_type,
a_content,
datetime,
}
}
/// here is zero-copy and unwrap is safe. checked.
fn from_values(mut v: Vec<DsValue>) -> Message {
Message {
datetime: v.pop().unwrap().as_i64(),
a_content: v.pop().unwrap().as_string(),
a_type: MessageType::from_int(v.pop().unwrap().as_i64()),
q_content: v.pop().unwrap().as_string(),
q_type: MessageType::from_int(v.pop().unwrap().as_i64()),
id: v.pop().unwrap().as_i64(),
}
}
pub fn to_rpc(&self) -> RpcParam {
json!([
self.id,
self.q_type.to_int(),
self.q_content,
self.a_type.to_int(),
self.a_content,
self.datetime,
])
}
pub fn all(db: &DStorage) -> Result<Vec<Message>> {
let sql =
format!("SELECT id, q_type, q_content, a_type, a_content, datetime FROM messages where is_deleted = false");
let matrix = db.query(&sql)?;
let mut messages = vec![];
for values in matrix {
messages.push(Message::from_values(values));
}
Ok(messages)
}
pub fn _get(db: &DStorage, id: &i64) -> Result<Option<Message>> {
let sql = format!(
"SELECT id, q_type, q_content, a_type, a_content, datetime FROM messages WHERE id = {}",
id
);
let mut matrix = db.query(&sql)?;
if matrix.len() > 0 {
Ok(Some(Message::from_values(matrix.pop().unwrap())))
} else {
Ok(None)
}
}
pub fn insert(&mut self, db: &DStorage) -> Result<()> {
let sql = format!(
"INSERT INTO messages (q_type, q_content, a_type, a_content, datetime, is_deleted) VALUES ({},'{}',{},'{}',{},false)",
self.q_type.to_int(),
self.q_content,
self.a_type.to_int(),
self.a_content,
self.datetime,
);
self.id = db.insert(&sql)?;
Ok(())
}
pub fn delete(db: &DStorage, id: i64) -> Result<usize> {
let sql = format!("UPDATE messages SET is_deleted = true WHERE id = {}", id);
db.delete(&sql)
}
}

85
src/apps/assistant/rpc.rs

@ -0,0 +1,85 @@ @@ -0,0 +1,85 @@
use std::sync::Arc;
use tdn::types::{
group::GroupId,
primitive::HandleResult,
rpc::{json, rpc_response, RpcHandler, RpcParam},
};
use crate::rpc::RpcState;
use crate::storage::assistant_db;
use super::{Message, MessageType};
#[inline]
pub(crate) fn _assistant_create(mgid: GroupId, device: &Message) -> RpcParam {
rpc_response(0, "assistant-create", json!(device.to_rpc()), mgid)
}
#[inline]
pub(crate) fn _assistant_delete(mgid: GroupId, id: i64) -> RpcParam {
rpc_response(0, "assistant-delete", json!([id]), mgid)
}
#[inline]
pub(crate) fn _assistant_update(mgid: GroupId, id: i64, message: &Message) -> RpcParam {
rpc_response(
0,
"assistant-update",
json!([id, message.a_type.to_int(), message.a_content]),
mgid,
)
}
pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
handler.add_method("assistant-echo", |_, params, _| async move {
Ok(HandleResult::rpc(json!(params)))
});
handler.add_method(
"assistant-list",
|gid: GroupId, _params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let db = assistant_db(state.layer.read().await.base(), &gid)?;
let devices = Message::all(&db)?;
db.close()?;
let mut results = vec![];
for device in devices {
results.push(device.to_rpc());
}
Ok(HandleResult::rpc(json!(results)))
},
);
handler.add_method(
"assistant-create",
|gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let q_type = MessageType::from_int(params[0].as_i64()?);
let q_content = params[1].as_str()?.to_string();
let base = state.layer.read().await.base().clone();
let q_raw = q_type.handle(&base, &gid, q_content).await?;
// echo
let a_type = q_type.clone();
let a_content = q_raw.clone();
let mut msg = Message::new(q_type, q_raw, a_type, a_content);
let db = assistant_db(state.layer.read().await.base(), &gid)?;
msg.insert(&db)?;
db.close()?;
let results = HandleResult::rpc(json!(msg.to_rpc()));
Ok(results)
},
);
handler.add_method(
"assistant-delete",
|gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let id = params[0].as_i64()?;
let db = assistant_db(state.layer.read().await.base(), &gid)?;
Message::delete(&db, id)?;
db.close()?;
Ok(HandleResult::new())
},
);
}

201
src/migrate.rs

@ -14,6 +14,8 @@ use file::FILE_VERSIONS; @@ -14,6 +14,8 @@ use file::FILE_VERSIONS;
use service::SERVICE_VERSIONS;
use session::SESSION_VERSIONS;
use crate::apps::assistant::ASSISTANT_VERSIONS;
// Account's main database name.
pub(crate) const ACCOUNT_DB: &'static str = "account.db";
@ -29,111 +31,83 @@ pub(crate) const FILE_DB: &'static str = "file.db"; @@ -29,111 +31,83 @@ pub(crate) const FILE_DB: &'static str = "file.db";
/// Account's service database name
pub(crate) const SERVICE_DB: &'static str = "service.db";
/// Account's assistant database name
pub(crate) const ASSISTANT_DB: &'static str = "assistant.db";
pub(crate) fn main_migrate(path: &PathBuf) -> std::io::Result<()> {
let mut db_path = path.clone();
db_path.push(ACCOUNT_DB);
if db_path.exists() {
let db = DStorage::open(db_path)?;
// 1. get current version.
let mut matrix = db.query("select account_version, consensus_version, session_version, file_version, service_version from versions")?;
let mut values = matrix.pop().unwrap();
let current_service = values.pop().unwrap().as_i64() as usize;
let current_file = values.pop().unwrap().as_i64() as usize;
let current_session = values.pop().unwrap().as_i64() as usize;
let current_consensus = values.pop().unwrap().as_i64() as usize;
let current_account = values.pop().unwrap().as_i64() as usize;
if current_account != ACCOUNT_VERSIONS.len() {
// 1. get current version.
let first_matrix =
db.query("SELECT name FROM sqlite_master WHERE type='table' AND name='migrates'")?;
if first_matrix.len() == 0 {
// 2. migrate.
for i in &ACCOUNT_VERSIONS[current_account..] {
for i in &ACCOUNT_VERSIONS[1..] {
db.execute(i)?;
}
db.update(&format!(
"UPDATE versions SET account_version = {}",
ACCOUNT_VERSIONS.len()
))?;
}
if current_consensus != CONSENSUS_VERSIONS.len() {
let mut matrix = db.query("select gid from accounts")?;
while matrix.len() > 0 {
let mut account_path = path.clone();
account_path.push(matrix.pop().unwrap().pop().unwrap().as_str());
account_path.push(CONSENSUS_DB);
let account_db = DStorage::open(account_path)?;
// migrate
for i in &CONSENSUS_VERSIONS[current_consensus..] {
account_db.execute(i)?;
}
account_db.close()?;
}
db.update(&format!(
"UPDATE versions SET consensus_version = {}",
CONSENSUS_VERSIONS.len()
"UPDATE migrates SET version = {} where db_name = '{}'",
ACCOUNT_VERSIONS.len(),
ACCOUNT_DB,
))?;
}
if current_session != SESSION_VERSIONS.len() {
let mut matrix = db.query("select gid from accounts")?;
while matrix.len() > 0 {
let mut account_path = path.clone();
account_path.push(matrix.pop().unwrap().pop().unwrap().as_str());
account_path.push(SESSION_DB);
let account_db = DStorage::open(account_path)?;
// migrate
for i in &SESSION_VERSIONS[current_session..] {
account_db.execute(i)?;
let matrix = db.query("select db_name, version from migrates")?;
for mut values in matrix {
let db_version = values.pop().unwrap().as_i64() as usize;
let db_name = values.pop().unwrap().as_string();
let current_versions = match db_name.as_str() {
ACCOUNT_DB => {
if db_version != ACCOUNT_VERSIONS.len() {
// 2. migrate.
for i in &ACCOUNT_VERSIONS[db_version..] {
db.execute(i)?;
}
db.update(&format!(
"UPDATE migrates SET version = {} where db_name = '{}'",
ACCOUNT_VERSIONS.len(),
db_name,
))?;
}
continue;
}
account_db.close()?;
}
db.update(&format!(
"UPDATE versions SET session_version = {}",
SESSION_VERSIONS.len()
))?;
}
if current_file != FILE_VERSIONS.len() {
let mut matrix = db.query("select gid from accounts")?;
while matrix.len() > 0 {
let mut account_path = path.clone();
account_path.push(matrix.pop().unwrap().pop().unwrap().as_str());
account_path.push(FILE_DB);
let account_db = DStorage::open(account_path)?;
// migrate.
for i in &FILE_VERSIONS[current_file..] {
account_db.execute(i)?;
CONSENSUS_DB => CONSENSUS_VERSIONS.as_ref(),
SESSION_DB => SESSION_VERSIONS.as_ref(),
FILE_DB => FILE_VERSIONS.as_ref(),
SERVICE_DB => SERVICE_VERSIONS.as_ref(),
ASSISTANT_DB => ASSISTANT_VERSIONS.as_ref(),
_ => {
continue;
}
account_db.close()?;
}
db.update(&format!(
"UPDATE versions SET file_version = {}",
FILE_VERSIONS.len()
))?;
}
if current_service != SERVICE_VERSIONS.len() {
let mut matrix = db.query("select gid from accounts")?;
while matrix.len() > 0 {
let mut account_path = path.clone();
account_path.push(matrix.pop().unwrap().pop().unwrap().as_str());
account_path.push(SERVICE_DB);
let account_db = DStorage::open(account_path)?;
// 2. migrate.
for i in &SERVICE_VERSIONS[current_service..] {
account_db.execute(i)?;
};
if db_version != current_versions.len() {
let mut matrix = db.query("select gid from accounts")?;
while matrix.len() > 0 {
let mut account_path = path.clone();
account_path.push(matrix.pop().unwrap().pop().unwrap().as_str());
account_path.push(&db_name);
let account_db = DStorage::open(account_path)?;
// migrate
for i in &current_versions[db_version..] {
account_db.execute(i)?;
}
account_db.close()?;
}
account_db.close()?;
}
db.update(&format!(
"UPDATE versions SET service_version = {}",
SERVICE_VERSIONS.len()
))?;
db.update(&format!(
"UPDATE migrates SET version = {} where db_name = '{}'",
current_versions.len(),
db_name,
))?;
}
}
db.close()?;
@ -143,56 +117,87 @@ pub(crate) fn main_migrate(path: &PathBuf) -> std::io::Result<()> { @@ -143,56 +117,87 @@ pub(crate) fn main_migrate(path: &PathBuf) -> std::io::Result<()> {
for i in ACCOUNT_VERSIONS.iter() {
db.execute(i)?;
}
db.insert(&format!(
"INSERT INTO versions (account_version, consensus_version, session_version, file_version, service_version) VALUES ({}, {}, {}, {}, {})",
db.update(&format!(
"UPDATE migrates SET version = {} where db_name = '{}'",
ACCOUNT_VERSIONS.len(),
ACCOUNT_DB,
))?;
db.update(&format!(
"UPDATE migrates SET version = {} where db_name = '{}'",
CONSENSUS_VERSIONS.len(),
CONSENSUS_DB,
))?;
db.update(&format!(
"UPDATE migrates SET version = {} where db_name = '{}'",
SESSION_VERSIONS.len(),
SESSION_DB,
))?;
db.update(&format!(
"UPDATE migrates SET version = {} where db_name = '{}'",
FILE_VERSIONS.len(),
FILE_DB,
))?;
db.update(&format!(
"UPDATE migrates SET version = {} where db_name = '{}'",
SERVICE_VERSIONS.len(),
SERVICE_DB,
))?;
db.update(&format!(
"UPDATE migrates SET version = {} where db_name = '{}'",
ASSISTANT_VERSIONS.len(),
ASSISTANT_DB,
))?;
db.close()?;
}
Ok(())
}
pub(crate) fn consensus_migrate(path: &PathBuf) -> std::io::Result<()> {
pub(crate) fn account_init_migrate(path: &PathBuf) -> std::io::Result<()> {
let mut db_path = path.clone();
db_path.push(CONSENSUS_DB);
let db = DStorage::open(db_path)?;
for i in &CONSENSUS_VERSIONS {
db.execute(i)?;
}
db.close()
}
db.close()?;
pub(crate) fn session_migrate(path: &PathBuf) -> std::io::Result<()> {
let mut db_path = path.clone();
db_path.push(SESSION_DB);
let db = DStorage::open(db_path)?;
for i in &SESSION_VERSIONS {
db.execute(i)?;
}
db.close()
}
db.close()?;
pub(crate) fn file_migrate(path: &PathBuf) -> std::io::Result<()> {
let mut db_path = path.clone();
db_path.push(FILE_DB);
let db = DStorage::open(db_path)?;
for i in &FILE_VERSIONS {
db.execute(i)?;
}
db.close()
}
db.close()?;
pub(crate) fn service_migrate(path: &PathBuf) -> std::io::Result<()> {
let mut db_path = path.clone();
db_path.push(SERVICE_DB);
let db = DStorage::open(db_path)?;
for i in &SERVICE_VERSIONS {
db.execute(i)?;
}
db.close()?;
let mut db_path = path.clone();
db_path.push(ASSISTANT_DB);
let db = DStorage::open(db_path)?;
for i in &ASSISTANT_VERSIONS {
db.execute(i)?;
}
db.close()
}

19
src/migrate/account.rs

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
#[rustfmt::skip]
pub(super) const ACCOUNT_VERSIONS: [&str; 2] = [
"CREATE TABLE accounts(
pub(super) const ACCOUNT_VERSIONS: [&str; 7] = [
"CREATE TABLE IF NOT EXISTS accounts(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
gid TEXT NOT NULL,
name TEXT NOT NULL,
@ -11,11 +11,12 @@ pub(super) const ACCOUNT_VERSIONS: [&str; 2] = [ @@ -11,11 +11,12 @@ pub(super) const ACCOUNT_VERSIONS: [&str; 2] = [
height INTEGER NOT NULL,
event TEXT NOT NULL,
datetime INTEGER NOT NULL);",
"CREATE TABLE versions(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
account_version INTEGER NOT NULL,
consensus_version INTEGER NOT NULL,
session_version INTEGER NOT NULL,
file_version INTEGER NOT NULL,
service_version INTEGER NOT NULL);",
"CREATE TABLE IF NOT EXISTS migrates(
db_name TEXT NOT NULL,
version INTEGER NOT NULL);",
"INSERT INTO migrates (db_name, version) values ('account.db', 1)",
"INSERT INTO migrates (db_name, version) values ('consensus.db', 9)",
"INSERT INTO migrates (db_name, version) values ('session.db', 3)",
"INSERT INTO migrates (db_name, version) values ('file.db', 1)",
"INSERT INTO migrates (db_name, version) values ('assistant.db', 0)",
];

6
src/migrate/consensus.rs

@ -6,18 +6,18 @@ pub(crate) const FILE_TABLE_PATH: i64 = 4; @@ -6,18 +6,18 @@ pub(crate) const FILE_TABLE_PATH: i64 = 4;
#[rustfmt::skip]
pub(super) const CONSENSUS_VERSIONS: [&str; 9] = [
"CREATE TABLE devices(
"CREATE TABLE IF NOT EXISTS devices(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name TEXT NOT NULL,
info TEXT NOT NULL,
addr TEXT NOT NULL,
lasttime INTEGER NOT NULL,
is_deleted INTEGER NOT NULL);",
"CREATE TABLE db_tables(
"CREATE TABLE IF NOT EXISTS db_tables(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
db_name TEXT NOT NULL,
table_name TEXT NOT NULL);",
"CREATE TABLE events(
"CREATE TABLE IF NOT EXISTS events(
id INTEGER NOT NULL,
hash TEXT NOT NULL,
db_table INTEGER NOT NULL,

2
src/migrate/file.rs

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
#[rustfmt::skip]
pub(super) const FILE_VERSIONS: [&str; 1] = [
"CREATE TABLE files(
"CREATE TABLE IF NOT EXISTS files(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
parent INTEGER NOT NULL,
f_type INTEGER NOT NULL,

1
src/migrate/service.rs

@ -1,4 +1,3 @@ @@ -1,4 +1,3 @@
#[rustfmt::skip]
pub(super) const SERVICE_VERSIONS: [&str; 0] = [
];

6
src/migrate/session.rs

@ -1,6 +1,6 @@ @@ -1,6 +1,6 @@
#[rustfmt::skip]
pub(super) const SESSION_VERSIONS: [&str; 3] = [
"CREATE TABLE friends(
"CREATE TABLE IF NOT EXISTS friends(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
gid TEXT NOT NULL,
addr TEXT NOT NULL,
@ -12,7 +12,7 @@ pub(super) const SESSION_VERSIONS: [&str; 3] = [ @@ -12,7 +12,7 @@ pub(super) const SESSION_VERSIONS: [&str; 3] = [
last_message_content TEXT,
last_message_readed INTEGER,
is_deleted INTEGER NOT NULL);",
"CREATE TABLE requests(
"CREATE TABLE IF NOT EXISTS requests(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
gid TEXT NOT NULL,
addr TEXT NOT NULL,
@ -24,7 +24,7 @@ pub(super) const SESSION_VERSIONS: [&str; 3] = [ @@ -24,7 +24,7 @@ pub(super) const SESSION_VERSIONS: [&str; 3] = [
is_delivery INTEGER NOT NULL,
datetime INTEGER NOT NULL,
is_deleted INTEGER NOT NULL);",
"CREATE TABLE messages(
"CREATE TABLE IF NOT EXISTS messages(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
hash TEXT NOT NULL,
fid INTEGER NOT NULL,

15
src/storage.rs

@ -11,8 +11,7 @@ use tdn::types::{ @@ -11,8 +11,7 @@ use tdn::types::{
use tdn_storage::local::DStorage;
use crate::migrate::{
consensus_migrate, file_migrate, service_migrate, session_migrate, ACCOUNT_DB, CONSENSUS_DB,
FILE_DB, SERVICE_DB, SESSION_DB,
account_init_migrate, ACCOUNT_DB, ASSISTANT_DB, CONSENSUS_DB, FILE_DB, SERVICE_DB, SESSION_DB,
};
const FILES_DIR: &'static str = "files";
@ -338,6 +337,13 @@ pub(crate) fn _service_db(base: &PathBuf, gid: &GroupId) -> Result<DStorage> { @@ -338,6 +337,13 @@ pub(crate) fn _service_db(base: &PathBuf, gid: &GroupId) -> Result<DStorage> {
DStorage::open(db_path)
}
pub(crate) fn assistant_db(base: &PathBuf, gid: &GroupId) -> Result<DStorage> {
let mut db_path = base.clone();
db_path.push(gid.to_hex());
db_path.push(ASSISTANT_DB);
DStorage::open(db_path)
}
/// account independent db and storage directory.
pub(crate) async fn account_init(base: &PathBuf, gid: &GroupId) -> Result<()> {
let mut db_path = base.clone();
@ -345,8 +351,5 @@ pub(crate) async fn account_init(base: &PathBuf, gid: &GroupId) -> Result<()> { @@ -345,8 +351,5 @@ pub(crate) async fn account_init(base: &PathBuf, gid: &GroupId) -> Result<()> {
init_local_files(&db_path).await?;
// Inner Database.
consensus_migrate(&db_path)?;
session_migrate(&db_path)?;
file_migrate(&db_path)?;
service_migrate(&db_path)
account_init_migrate(&db_path)
}

Loading…
Cancel
Save