Browse Source

move jarvis answer to rust code

pull/18/head
Sun 4 years ago
parent
commit
9da5ac7678
  1. 171
      assets/answers/chinese_simplified.txt
  2. 171
      assets/answers/english.txt
  3. 2048
      assets/mnemonic/chinese_simplified.txt
  4. 2048
      assets/mnemonic/chinese_traditional.txt
  5. 2048
      assets/mnemonic/english.txt
  6. 2048
      assets/mnemonic/french.txt
  7. 2048
      assets/mnemonic/italian.txt
  8. 2048
      assets/mnemonic/japanese.txt
  9. 2048
      assets/mnemonic/korean.txt
  10. 2048
      assets/mnemonic/spanish.txt
  11. 4
      lib/apps/chat/detail.dart
  12. 17
      lib/apps/group/detail.dart
  13. 133
      lib/apps/jarvis/detail.dart
  14. 348
      lib/apps/jarvis/message.dart
  15. 116
      lib/apps/jarvis/models.dart
  16. 469
      lib/apps/jarvis/page.dart
  17. 75
      lib/apps/jarvis/provider.dart
  18. 2
      lib/apps/service/models.dart
  19. 2
      lib/pages/home.dart
  20. 2
      lib/utils/emoji_picker.dart
  21. 9
      lib/widgets/chat_input.dart
  22. 9
      lib/widgets/emoji.dart
  23. 10
      pubspec.yaml
  24. 8
      src/account.rs
  25. 0
      src/apps/jarvis/layer.rs
  26. 3
      src/apps/jarvis/mod.rs
  27. 157
      src/apps/jarvis/models.rs
  28. 82
      src/apps/jarvis/rpc.rs
  29. 10
      src/migrate/jarvis.rs
  30. 3
      src/rpc.rs
  31. 1
      src/utils.rs
  32. 11
      src/utils/answer.rs
  33. 173
      src/utils/answer/english.rs
  34. 173
      src/utils/answer/simplified_chinese.rs

171
assets/answers/chinese_simplified.txt

@ -1,171 +0,0 @@ @@ -1,171 +0,0 @@
需要一个相当大的努力
从现在开始 一年也没有关系
绝对不是
接受改变你的日常行为
似乎已经是真实的了
采用一个冒险的态度
允许你选择先休息一下
小心谨慎的靠近
你还是问问爸爸吧
你还是问问妈妈吧
援助会让你成功进步
避免第一个解决方案
被人肯定
更慷慨
耐心点
实际一点吧
打赌
耐心的等待
合作将会是关键
考虑一下这个机会
数到10;再问
以后处理
当然
早点做到这一点
在这个时候不要问太多了
不要担心
不要迫于压力太快
别傻了
不要赌
不用怀疑了
不要忘记有乐趣
不要陷入你的感情
不要犹豫
不要忽视显而易见的
不要过分
不要等待
别浪费时间了
怀疑
尽情体验
要解决
探讨其俏皮的好奇心
完成其他事情的第一
专注你的家庭生活
遵循别人的引导
遵循专家的建议
履行你自己的义务
按照你的意愿
温柔的坚持就是胜利
获得更清晰的视野
把它写下来
给它你的一切
如果是,做的很好;如果不是,就不要这么做;
如果你按我说的做
如果你不反抗
调查研究,然后享受它!
这不可能失败
这可能是特别的
这是肯定的
这不是重要的
这是非常重要
这是你不会忘记的
这肯定会让事情变得有趣
这是不确定的
是值得的麻烦
这可能已经是木已成舟
这可能很难,但你会发现它的价值
这个似乎放心
这将影响别人看你
这将是一种享受
这会带来好运
这将会创造一个扰乱
这仍然是不可预测的
它会支持你的
这将更好地专注于自己的工作
这是不明智的
它会让你付出代价
这是一个不错的时间安排
会很棒的
这是不值得的斗争
是你走的时候
保持开放的心态
不要让别人知道
笑一下
离开旧的解决方案
过去的事就让它过去吧
仔细聆听;那么你就会知道
列出原因
为什么不列出原因
也许
不幸的是极有可能的
继续前行
从来没有
没有
不管是什么
如果你不孤单
现在你可以
只做一次
其他人将取决于你的选择
注重细节
也许,当你老了
为突发事件做好准备
按下关闭
须以较宽松的步伐
有时候选择太多,就代表着无从选择!
重新考虑你的方法
相关的问题可能的浮出水面
保持灵活
删除你自己的障碍物
重新确定优先次序什么是重要的
尊重规则
保存你的精力
寻找更多的选择
设定优先等级是一个必要的过程!
很快就会解决它
转移你的焦点
说出来吧
令人震惊的事件可能发生的
冒险一试
负责
花更多的时间来决定
告诉别人它对你意味着什么
那将是浪费钱
那是脱离你的控制
答案就在你的后院
答案可能会在另一种语言
最好的解决办法可能不是明显的
机会不会很快再来
情况将很快发生改变
结果会是好的
情况不明
与另一种情况有潜在的联系
有充分的理由保持乐观
没有保证
会有障碍要克服
这是一个很好的时机,来制定新计划
为了确保能作出最佳决策,需要保持冷静
相信自己的直觉
相信你的原始思想
尝试一种更不太可能的解决方案
不宜在这个时候
毫无疑问
无论如何你可以提升
运用你的想象力
等一等
等待一个更好的机会
看看会发生什么
注意你的节奏
不管你做什么,结果将持久
可以
是的,但不要强迫
你一定有支持
你太近的看了
你可能觉得自己无法妥协
你真的不在乎
你知道现在比以前更好
你可能会反对
你可能会放弃其他的东西
你必须
你现在必须行动
你会发现一切你需要知道的
你将需要适应
你不会失望的
你会很高兴你做了
你会得到最后的决定
你将不得不妥协
你将不得不补回来
您需要了解更多信息
你需要考虑其他办法
你需要主动出击
你会后悔的
你的行动将会改善

171
assets/answers/english.txt

@ -1,171 +0,0 @@ @@ -1,171 +0,0 @@
A Substantial Effort Will Be Required
A Year From Now It Won't Matter
Absolutely Not
Accept A Change To Your Routine
Act As Though It Is Already Real
Adopt An Adventurous Attitude
Allow Yoursele To Rest First
Approach Cautiously
Ask Your Father
Ask Your Mother
Assistance Would Make Your Progress A Success
Avoid The First Solution
Be Delightfully Sure Of It
Be More Generous
Be Patient
Be Practical
Bet On It
Better To Wait
Collaboration Will Be The Key
Consider It An Opportunity
Count To 10; Ask Again
Deal With It Later
Definitely
Do It Early
Don't Ask For Any More At This Time
Don't Be Concerned
Don't Be Pressured Into Acting Too Quickly
Don't Be Ridiculous
Don't Bet On It
Don't Doubt It
Don't Forget To Have Fun
Don't Get Caught Up In Your Emotions
Don't Hesitate
Don't Ignore The Obvious
Don't Overdo It
Don't Wait
Don't Waste Your Time
Doubt It
Enjoy The Experience
Expect To Settle
Explore It With Playful Curiosity
Finish Something Else First
Focus On Your Home Life
Follow Someone Else's Lead
Follow The Advice Of Experts
Follow Through On Your Obligations
Follow Through With Your Good Intentions
Gentle Persistence Will Pay Off
Get A Clearer View
Get It In Writing
Give It All You've Got
If It's Done Well; If Not, Don't Do It At All
If You Do As You're Told
If You Don't Resist
Investigate And Then Enjoy It
It Cannot Fail
It Could Be Extraordinary
It Is Certain
It Is Not Significant
It Is Significant
It Is Something You Won't Forget
It Is Sure To Make Things Interesting
It Is Uncertain
It Is Worth The Trouble
It May Already Be A Done Deal
It May Be Difficult But You Will Find Value In It
It Seems Assured
It Will Affect How Others See You
It Will Be A Pleasure
It Will Bring Good Luck
It Will Create A Stir
It Will Remain Unpredictable
It Will Sustain You
It Would Be Better To Focus On Your Work
It Would Be Inadvisable
It'll Cost You
It's A Good Time To Make Plans
It's Gonna Be Great
It's Not Worth A Struggle
It's Time For You To Go
Keep An Open Mind
Keep It To Yourself
Laugh About It
Leave Behind Old Solutions
Let It Go
Listen More Carefully; Then You Will Know
Make A List Of Why
Make A List Of Why Not
Maybe
Mishaps Are Highly Probable
Move On
Never
No
No Matter What
Not If You're Alone
Now You Can
Only Do It Once
Others Will Depend On Your Choices
Pay Attention To The Details
Perhaps, When You're Older
Prepare For The Unexpected
Press For Closure
Proceed At A More Relaxed Pace
Realize That Too Many Choices Is As Difficult As Too Few
Reconsider Your Approach
Related Issues May Surface
Remain Flexible
Remove Your Own Obstacles
Reprioritize What Is Important
Respect The Rules
Save Your Energy
Seek Out More Options
Setting Priorities Will Be A Necessary Part Of The Process
Settle It Soon
Shift Your Focus
Speak Up About It
Startling Events May Occur As A Result
Take A Chance
Take Charge
Take More Time To Decide
Tell Someone What It Means To You
That Would Be A Waste Of Money
That's Out Of Your Control
The Answer Is In Your Backyard
The Answer May Come To You In Another Language
The Best Solution May Not Be The Obvious One
The Chance Will Not Come Again Soon
The Circumstances Will Change Very Quickly
The Outcome Will Be Positive
The Situation Is Unclear
There Is A Substantial Link To Another Situation
There Is Good Reason To Be Optimistic
There Is No Guarantee
There Will Be Obstacles To Overcome
This Is A Good Time To Make A New Plan
To Ensure The Best Decision, Be Calm
Trust Your Intuition
Trust Your Original Thought
Try A More Unlikely Solution
Unfavorable At This Time
Unquestionably
Upgrade Any Way You Can
Use Your Imagination
Wait
Wait For A Better Offer
Watch And See What Happens
Watch Your Step As You Go
Whatever You Do The Results Will Be Lasting
Yes
Yes,But Don't Force It
You Are Sure To Have Support
You Are Too Close To See
You Could Find Yourself Unable To Compromise
You Don't Really Care
You Know Better Now Than Ever Before
You May Have Opposition
You May Have To Drop Other Things
You Must
You Must Act Now
You Will Find Out Everything You'll Need To Know
You Will Need To Accommodate
You Will Not Be Disappointed
You'll Be Happy You Did
You'll Get The Final Word
You'll Have To Compromise
You'll Have To Make It Up As You Go
You'll Need More Information
You'll Need To Consider Other Ways
You'll Need To Take The Initiative
You'll Regret It
Your Actions Will Improve Thin

2048
assets/mnemonic/chinese_simplified.txt

File diff suppressed because it is too large Load Diff

2048
assets/mnemonic/chinese_traditional.txt

File diff suppressed because it is too large Load Diff

2048
assets/mnemonic/english.txt

File diff suppressed because it is too large Load Diff

2048
assets/mnemonic/french.txt

File diff suppressed because it is too large Load Diff

2048
assets/mnemonic/italian.txt

File diff suppressed because it is too large Load Diff

2048
assets/mnemonic/japanese.txt

File diff suppressed because it is too large Load Diff

2048
assets/mnemonic/korean.txt

File diff suppressed because it is too large Load Diff

2048
assets/mnemonic/spanish.txt

File diff suppressed because it is too large Load Diff

4
lib/apps/chat/detail.dart

@ -7,12 +7,12 @@ import 'package:esse/l10n/localizations.dart'; @@ -7,12 +7,12 @@ import 'package:esse/l10n/localizations.dart';
import 'package:esse/widgets/shadow_dialog.dart';
import 'package:esse/widgets/user_info.dart';
import 'package:esse/widgets/chat_message.dart';
import 'package:esse/widgets/chat_input.dart';
import 'package:esse/provider.dart';
import 'package:esse/session.dart' show SessionType, Session, OnlineType;
import 'package:esse/rpc.dart';
import 'package:esse/apps/primitives.dart';
import 'package:esse/apps/chat_input.dart';
import 'package:esse/apps/chat/models.dart';
import 'package:esse/apps/group/add.dart';
@ -107,7 +107,7 @@ class _ChatDetailState extends State<ChatDetail> { @@ -107,7 +107,7 @@ class _ChatDetailState extends State<ChatDetail> {
@override
void deactivate() {
if (!isDisplayDesktop(context)) {
context.read<AccountProvider>().clearActivedSession(SessionType.Group);
context.read<AccountProvider>().clearActivedSession(SessionType.Chat);
}
super.deactivate();
}

17
lib/apps/group/detail.dart

@ -12,6 +12,7 @@ import 'package:esse/widgets/shadow_dialog.dart'; @@ -12,6 +12,7 @@ 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/widgets/chat_input.dart';
import 'package:esse/widgets/show_contact.dart';
import 'package:esse/rpc.dart';
import 'package:esse/global.dart';
@ -19,7 +20,6 @@ import 'package:esse/provider.dart'; @@ -19,7 +20,6 @@ import 'package:esse/provider.dart';
import 'package:esse/session.dart' show SessionType, Session;
import 'package:esse/apps/primitives.dart';
import 'package:esse/apps/chat_input.dart';
import 'package:esse/apps/group/models.dart';
class GroupChatDetail extends StatefulWidget {
@ -152,21 +152,22 @@ class _GroupChatDetailState extends State<GroupChatDetail> { @@ -152,21 +152,22 @@ class _GroupChatDetailState extends State<GroupChatDetail> {
child: isDesktop
? Row(children: [
Expanded(child: _MainScreen(
group: this._group, members: this._members, messages: this._messages)),
isDesktop: isDesktop, group: this._group, members: this._members, messages: this._messages)),
_MemberScreen(members: this._members),
])
: _MainScreen(group: this._group, members: this._members, messages: this._messages)
: _MainScreen(isDesktop: isDesktop, group: this._group, members: this._members, messages: this._messages)
)
);
}
}
class _MainScreen extends StatefulWidget {
final bool isDesktop;
final GroupChat group;
final List<Member> members;
final List<Message> messages;
_MainScreen({Key? key, required this.group, required this.members, required this.messages}) : super(key: key);
_MainScreen({Key? key, required this.isDesktop, required this.group, required this.members, required this.messages}) : super(key: key);
@override
_MainScreenState createState() => _MainScreenState();
@ -198,7 +199,13 @@ class _MainScreenState extends State<_MainScreen> { @@ -198,7 +199,13 @@ class _MainScreenState extends State<_MainScreen> {
return Container();
}
)),
ChatInput(sid: 0, online: true, callback: _send, hasTransfer: false),
ChatInput(
sid: 0,
online: true,
callback: _send,
hasTransfer: false,
emojiWidth: widget.isDesktop,
),
]
);
}

133
lib/apps/jarvis/detail.dart

@ -0,0 +1,133 @@ @@ -0,0 +1,133 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:esse/utils/adaptive.dart';
import 'package:esse/utils/relative_time.dart';
import 'package:esse/l10n/localizations.dart';
import 'package:esse/widgets/chat_message.dart';
import 'package:esse/widgets/chat_input.dart';
import 'package:esse/options.dart';
import 'package:esse/account.dart' show Language, LanguageExtension;
import 'package:esse/rpc.dart';
import 'package:esse/apps/primitives.dart';
class Message extends BaseMessage {
Message.fromList(List params) {
this.id = params[0];
this.isMe = params[1];
this.type = MessageTypeExtension.fromInt(params[2]);
this.content = params[3];
this.time = RelativeTime.fromInt(params[4]);
this.isDelivery = true;
}
}
class JarvisDetail extends StatefulWidget {
JarvisDetail({Key? key}) : super(key: key);
@override
_JarvisDetailState createState() => _JarvisDetailState();
}
class _JarvisDetailState extends State<JarvisDetail> {
TextEditingController textController = TextEditingController();
FocusNode textFocus = FocusNode();
bool _emojiShow = false;
bool _sendShow = false;
bool _menuShow = false;
bool _recordShow = false;
String _recordName = '';
Language _language = Language.English;
List<String> _answers = [];
Map<int, Message> _messages = {};
@override
initState() {
super.initState();
rpc.addListener('jarvis-create', _create, false);
textFocus.addListener(() {
if (textFocus.hasFocus) {
setState(() {
_emojiShow = false;
_menuShow = false;
_recordShow = false;
});
}
});
Future.delayed(Duration.zero, () async {
this._language = LanguageExtension.fromLocale(context.read<Options>().locale);
_load();
});
}
_load() async {
this._messages.clear();
final res = await httpPost('jarvis-list', []);
if (res.isOk) {
_list(res.params);
} else {
print(res.error);
}
}
/// list message with friend.
_list(List params) {
params.forEach((param) {
this._messages[param[0]] = Message.fromList(param);
});
setState(() {});
}
/// friend send message to me.
_create(List params) {
final msg = Message.fromList(params);
this._messages[msg.id] = msg;
setState(() {});
}
_send(MessageType mtype, String raw) {
rpc.send('jarvis-create', [this._language.toInt(), mtype.toInt(), raw]);
}
@override
Widget build(BuildContext context) {
final color = Theme.of(context).colorScheme;
final lang = AppLocalizations.of(context);
final isDesktop = isDisplayDesktop(context);
final recentMessageKeys = this._messages.keys.toList().reversed.toList();
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: false,
leading: isDesktop ? null : IconButton(icon: Icon(Icons.arrow_back),
onPressed: () => Navigator.pop(context)),
title: Text(lang.jarvis,
maxLines: 1, overflow: TextOverflow.ellipsis),
bottom: isDesktop ? PreferredSize(
child: Container(color: const Color(0x40ADB0BB), height: 1.0),
preferredSize: Size.fromHeight(1.0)): null,
),
body: Column(
children: [
Expanded(
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 20.0),
itemCount: recentMessageKeys.length,
reverse: true,
itemBuilder: (BuildContext context, index) => ChatMessage(
fgid: '',
name: lang.jarvis,
message: this._messages[recentMessageKeys[index]]!,
)
)),
ChatInput(sid: 0, online: true, callback: _send, hasTransfer: false),
]
)
);
}
}

348
lib/apps/jarvis/message.dart

@ -1,348 +0,0 @@ @@ -1,348 +0,0 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_save/image_save.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/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/file/models.dart' show FileType, FileTypeExtension, parseFileType;
import 'package:esse/apps/jarvis/models.dart';
class JarvisMessage extends StatelessWidget {
final String name;
final Message message;
final List<String> answers;
const JarvisMessage({Key? key, required this.name, required this.message, required this.answers}): 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(0xFF6174FF) : 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) {
// Save to album.
final data = await File(imagePath).readAsBytes();
final bool? success = await ImageSave.saveImage(data, content, albumName: "ESSE");
print(success);
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 {
final params = parseFileType(content).params();
fileImage = Icon(params[0], color: params[1], size: 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();
return GestureDetector(
onTap: () => showShadowDialog(
context,
Icons.person_rounded,
lang.contact,
Column(children: [
Avatar(width: 100.0, name: infos[0], avatarPath: infos[3]),
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, gidText(gid), gidPrint(gid)),
_infoListTooltip(Icons.location_on, color.primary, addrText(infos[2]), addrPrint(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]),
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(gidPrint(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)),
])));
}
Widget _infoListTooltip(icon, color, text, short) {
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(short),
)
)
]
),
);
}
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);
} else if (type == MessageType.Answer) {
final index = int.parse(content);
final value = this.answers.length > index ? this.answers[index] : content;
return _showText(context, color, isDesktop, value, isMe);
}
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.qType, message.qContent, true);
final aShow = _show(context, color, lang, isDesktop, message.aType, message.aContent, 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.aContent.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,
),
),
]),
]));
}
}

116
lib/apps/jarvis/models.dart

@ -1,116 +0,0 @@ @@ -1,116 +0,0 @@
import 'package:esse/utils/relative_time.dart';
import 'package:esse/global.dart';
enum MessageType {
String,
Image,
File,
Contact,
Emoji,
Record,
Answer,
}
// 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;
case MessageType.Answer:
return 6;
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;
case 6:
return MessageType.Answer;
default:
return MessageType.String;
}
}
}
class Message {
int id = 0;
MessageType qType = MessageType.String;
String qContent = '';
MessageType aType = MessageType.String;
String aContent = '';
RelativeTime time = RelativeTime();
Message(this.qType, this.qContent);
static List showContact(String content) {
var name = '';
var did = '';
var addr = '';
var iName = content.indexOf(';;');
if (iName > 0) {
name = content.substring(0, iName).replaceAll('-;', ';');
}
var raw = content.substring(iName + 2);
var iDid = raw.indexOf(';;');
if (iDid > 0) {
did = raw.substring(0, iDid);
}
addr = raw.substring(iDid + 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];
}
}
Message.fromList(List params):
this.id = params[0],
this.qType = MessageTypeExtension.fromInt(params[1]),
this.qContent = params[2],
this.aType = MessageTypeExtension.fromInt(params[3]),
this.aContent = params[4],
this.time = RelativeTime.fromInt(params[5]);
update(List params) {
// params[0] is id.
this.aType = MessageTypeExtension.fromInt(params[1]);
this.aContent = params[2];
}
}

469
lib/apps/jarvis/page.dart

@ -1,469 +0,0 @@ @@ -1,469 +0,0 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:esse/utils/adaptive.dart';
import 'package:esse/utils/pick_image.dart';
import 'package:esse/utils/pick_file.dart';
import 'package:esse/l10n/localizations.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/show_contact.dart';
import 'package:esse/widgets/transfer.dart';
import 'package:esse/global.dart';
import 'package:esse/options.dart';
import 'package:esse/apps/jarvis/models.dart';
import 'package:esse/apps/jarvis/provider.dart';
import 'package:esse/apps/jarvis/message.dart';
import 'package:esse/apps/jarvis/answer.dart';
class JarvisDetail extends StatefulWidget {
const JarvisDetail({Key? key}) : super(key: key);
@override
_JarvisDetailState createState() => _JarvisDetailState();
}
class _JarvisDetailState extends State<JarvisDetail> {
TextEditingController textController = TextEditingController();
FocusNode textFocus = FocusNode();
bool emojiShow = false;
bool sendShow = false;
bool menuShow = false;
bool recordShow = false;
String _recordName = '';
List<String> answers = [];
@override
initState() {
super.initState();
Future.delayed(Duration.zero, () async {
Provider.of<JarvisProvider>(context, listen: false).actived();
final options = context.read<Options>();
this.answers = await loadAnswers(options.locale);
setState(() {});
});
textFocus.addListener(() {
if (textFocus.hasFocus) {
setState(() {
emojiShow = false;
menuShow = false;
recordShow = false;
});
}
});
}
@override
void deactivate() {
Provider.of<JarvisProvider>(context, listen: false).inactived();
super.deactivate();
}
_generateRecordPath() {
this._recordName = DateTime.now().millisecondsSinceEpoch.toString() + '_jarvis.m4a';
}
void _sendMessage() async {
if (textController.text.length < 1) {
return;
}
final value = textController.text.trim();
final aType = (value.endsWith('?') || value.endsWith('')) ? MessageType.Answer : MessageType.String;
context.read<JarvisProvider>().create(aType, textController.text);
setState(() {
textController.text = '';
textFocus.requestFocus();
emojiShow = false;
sendShow = false;
menuShow = false;
recordShow = false;
});
}
void _selectEmoji(value) {
textController.text += value;
}
void _sendImage() async {
final image = await pickImage();
if (image != null) {
context.read<JarvisProvider>().create(MessageType.Image, image);
}
setState(() {
textFocus.requestFocus();
emojiShow = false;
sendShow = false;
menuShow = false;
recordShow = false;
});
}
void _sendFile() async {
final file = await pickFile();
if (file != null) {
context.read<JarvisProvider>().create(MessageType.File, file);
}
setState(() {
textFocus.requestFocus();
emojiShow = false;
sendShow = false;
menuShow = false;
recordShow = false;
});
}
void _sendRecord(int time) async {
final raw = Message.rawRecordName(time, _recordName);
context.read<JarvisProvider>().create(MessageType.Record, raw);
setState(() {
textFocus.requestFocus();
emojiShow = false;
sendShow = false;
menuShow = false;
recordShow = false;
});
}
_callback(int id) {
context.read<JarvisProvider>().create(MessageType.Contact, id.toString());
setState(() {
textFocus.requestFocus();
emojiShow = false;
sendShow = false;
menuShow = false;
recordShow = false;
});
}
void _sendContact(ColorScheme color, AppLocalizations lang) {
showShadowDialog(
context,
Icons.person_rounded,
lang.contact,
ContactList(callback: _callback, multiple: false)
);
}
_tokenCallback(String hash, String to, String amount, String name) {
//context.read<JarvisProvider>().create(MessageType.Transfer, "");
setState(() {
textFocus.requestFocus();
emojiShow = false;
sendShow = false;
menuShow = false;
recordShow = false;
});
}
// TEST CODE.
void _sendToken(ColorScheme color, AppLocalizations lang) {
return;
showShadowDialog(
context,
Icons.paid,
lang.transfer,
Transfer(callback: _tokenCallback, to: "0xdac17f958d2ee523a2206206994597c13d831ec7"),
0.0
);
}
@override
Widget build(BuildContext context) {
final color = Theme.of(context).colorScheme;
final lang = AppLocalizations.of(context);
final isDesktop = isDisplayDesktop(context);
final recentMessages = context.watch<JarvisProvider>().messages;
final recentMessageKeys = recentMessages.keys.toList().reversed.toList();
return Scaffold(
body: SafeArea(
child: Column(
children: [
Container(
padding: EdgeInsets.only(left: 20.0, right: 20.0, top: 10.0, bottom: 6.0),
child: Row(
children: [
if (!isDesktop)
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Container(
width: 20.0,
child:
Icon(Icons.arrow_back, color: color.primary)),
),
SizedBox(width: 15.0),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
height: 20.0,
child: Text('Jarvis',
style: TextStyle(fontWeight: FontWeight.bold),
)),
SizedBox(height: 5.0),
Container(
height: 15.0,
child: Text(lang.jarvisBio,
style: TextStyle(color: color.onPrimary.withOpacity(0.5), fontSize: 12.0))
)
],
),
),
SizedBox(width: 20.0),
GestureDetector(
onTap: () {},
child: Container(
width: 20.0,
child: Icon(Icons.phone_rounded,
color: Color(0x26ADB0BB))),
),
SizedBox(width: 20.0),
GestureDetector(
onTap: () {},
child: Container(
width: 20.0,
child: Icon(Icons.videocam_rounded,
color: Color(0x26ADB0BB))),
),
SizedBox(width: 20.0),
// PopupMenuButton<int>(
// shape: RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(15)
// ),
// color: const Color(0xFFEDEDED),
// child: Icon(Icons.more_vert_rounded, color: color.primary),
// onSelected: (int value) {
// if (value == 1) {
// // TODO set top
// }
// },
// itemBuilder: (context) {
// return <PopupMenuEntry<int>>[
// _menuItem(Color(0xFF6174FF), 1, Icons.vertical_align_top_rounded, lang.cancelTop),
// ];
// },
// )
]
),
),
const Divider(height: 1.0, color: Color(0x40ADB0BB)),
Expanded(
child: ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 20.0),
itemCount: recentMessageKeys.length,
reverse: true,
itemBuilder: (BuildContext context, index) => JarvisMessage(
name: 'Jarvis',
message: recentMessages[recentMessageKeys[index]]!,
answers: this.answers,
)
)),
Container(
padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 10.0),
child: Row(
children: [
GestureDetector(
onTap: () async {
if (recordShow) {
recordShow = false;
textFocus.requestFocus();
} else {
_generateRecordPath();
setState(() {
menuShow = false;
emojiShow = false;
recordShow = true;
textFocus.unfocus();
});
}
},
child: Container(width: 20.0,
child: Icon(Icons.mic_rounded, color: color.primary)),
),
SizedBox(width: 10.0),
Expanded(
child: Container(
height: 40,
decoration: BoxDecoration(
color: color.surface,
borderRadius: BorderRadius.circular(15.0),
),
child: TextField(
style: TextStyle(fontSize: 14.0),
textInputAction: TextInputAction.send,
onChanged: (value) {
if (value.length == 0 && sendShow) {
setState(() {
sendShow = false;
});
} else {
if (!sendShow) {
setState(() {
sendShow = true;
});
}
}
},
onSubmitted: (_v) => _sendMessage(),
decoration: InputDecoration(
hintText: 'Aa',
border: InputBorder.none,
contentPadding: EdgeInsets.only(
left: 15.0, right: 15.0, bottom: 7.0),
),
controller: textController,
focusNode: textFocus,
),
),
),
SizedBox(width: 10.0),
GestureDetector(
onTap: () {
if (emojiShow) {
textFocus.requestFocus();
} else {
setState(() {
menuShow = false;
recordShow = false;
emojiShow = true;
textFocus.unfocus();
});
}
},
child: Container(
width: 20.0,
child: Icon(
emojiShow
? Icons.keyboard_rounded
: Icons.emoji_emotions_rounded,
color: color.primary)),
),
SizedBox(width: 10.0),
sendShow
? GestureDetector(
onTap: _sendMessage,
child: Container(
width: 50.0,
height: 30.0,
decoration: BoxDecoration(
color: Color(0xFF6174FF),
borderRadius: BorderRadius.circular(10.0),
),
child: Center(
child: Icon(Icons.send, color: Colors.white, size: 20.0))),
)
: GestureDetector(
onTap: () {
if (menuShow) {
textFocus.requestFocus();
} else {
setState(() {
emojiShow = false;
recordShow = false;
menuShow = true;
textFocus.unfocus();
});
}
},
child: Container(
width: 20.0,
child: Icon(Icons.add_circle_rounded, color: color.primary)),
),
],
),
),
if (emojiShow) Emoji(action: _selectEmoji),
if (recordShow)
Container(
height: 100.0,
child: AudioRecorder(
path: Global.recordPath + _recordName, onStop: _sendRecord),
),
if (menuShow)
Container(
height: 100.0,
child: Wrap(
spacing: 20.0,
runSpacing: 20.0,
alignment: WrapAlignment.center,
children: <Widget>[
ExtensionButton(
icon: Icons.image_rounded,
text: lang.album,
action: _sendImage,
bgColor: color.surface,
iconColor: color.primary),
ExtensionButton(
icon: Icons.folder_rounded,
text: lang.file,
action: _sendFile,
bgColor: color.surface,
iconColor: color.primary),
ExtensionButton(
icon: Icons.person_rounded,
text: lang.contact,
action: () => _sendContact(color, lang),
bgColor: color.surface,
iconColor: color.primary),
ExtensionButton(
icon: Icons.paid_rounded,
text: lang.transfer,
action: () => _sendToken(color, lang),
bgColor: color.surface,
iconColor: color.primary),
],
),
)
],
)
)
);
}
}
class ExtensionButton extends StatelessWidget {
final String text;
final IconData icon;
final VoidCallback action;
final Color bgColor;
final Color iconColor;
const ExtensionButton({
Key? key,
required this.icon,
required this.text,
required this.action,
required this.bgColor,
required this.iconColor,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: action,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
padding: const EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(15.0),
),
child: Icon(icon, color: iconColor, size: 36.0)),
SizedBox(height: 5.0),
Text(text, style: TextStyle(fontSize: 14.0)),
],
));
}
}

75
lib/apps/jarvis/provider.dart

@ -1,75 +0,0 @@ @@ -1,75 +0,0 @@
import "dart:collection";
import 'package:flutter/material.dart';
import 'package:esse/rpc.dart';
import 'package:esse/apps/jarvis/models.dart';
class JarvisProvider extends ChangeNotifier {
bool isActived = false;
SplayTreeMap<int, Message> messages = SplayTreeMap();
JarvisProvider() {
// rpc.
rpc.addListener('jarvis-list', _list, false);
rpc.addListener('jarvis-create', _create, false);
rpc.addListener('jarvis-update', _update, false);
rpc.addListener('jarvis-delete', _delete, false);
}
actived() {
this.isActived = true;
rpc.send('jarvis-list', []);
}
inactived() {
this.messages.clear();
this.isActived = false;
}
create(MessageType qType, String qContent) {
rpc.send('jarvis-create', [qType.toInt(), qContent]);
}
/// delete a message.
delete(int id) {
this.messages.remove(id);
rpc.send('jarvis-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();
}
}
}

2
lib/apps/service/models.dart

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:esse/l10n/localizations.dart';
import 'package:esse/apps/jarvis/page.dart';
import 'package:esse/apps/jarvis/detail.dart';
import 'package:esse/apps/file/list.dart';
import 'package:esse/apps/group/list.dart';
import 'package:esse/apps/domain/page.dart';

2
lib/pages/home.dart

@ -32,7 +32,7 @@ import 'package:esse/apps/chat/add.dart'; @@ -32,7 +32,7 @@ import 'package:esse/apps/chat/add.dart';
import 'package:esse/apps/file/models.dart';
import 'package:esse/apps/file/list.dart';
import 'package:esse/apps/service/models.dart';
import 'package:esse/apps/jarvis/page.dart';
import 'package:esse/apps/jarvis/detail.dart';
import 'package:esse/apps/group/detail.dart';
class HomePage extends StatelessWidget {

2
lib/utils/emoji_picker.dart

@ -629,7 +629,7 @@ class _EmojiPickerState extends State<EmojiPicker> { @@ -629,7 +629,7 @@ class _EmojiPickerState extends State<EmojiPicker> {
return Column(
children: <Widget>[
SizedBox(
height: 110.0,
height: (widget.maxWidth / widget.columns) * widget.rows,
width: widget.maxWidth,
child: PageView(
children: pages,

9
lib/apps/chat_input.dart → lib/widgets/chat_input.dart

@ -21,13 +21,15 @@ class ChatInput extends StatefulWidget { @@ -21,13 +21,15 @@ class ChatInput extends StatefulWidget {
final Function callback;
final bool hasTransfer;
final String transferTo;
final bool emojiWidth;
ChatInput({Key? key,
required this.sid,
required this.online,
required this.callback,
this.hasTransfer = true,
this.waiting = false,
this.transferTo = ''
this.transferTo = '',
this.emojiWidth = false
}) : super(key: key);
@override
@ -83,7 +85,8 @@ class ChatInputState extends State<ChatInput> { @@ -83,7 +85,8 @@ class ChatInputState extends State<ChatInput> {
context,
Icons.person_rounded,
lang.contact,
ContactList(callback: _contactCallback, multiple: false)
ContactList(callback: _contactCallback, multiple: false),
0.0
);
}
@ -261,7 +264,7 @@ class ChatInputState extends State<ChatInput> { @@ -261,7 +264,7 @@ class ChatInputState extends State<ChatInput> {
],
),
),
if (_emojiShow) Emoji(action: _emoji),
if (_emojiShow) Emoji(action: _emoji, emojiWidth: widget.emojiWidth),
if (_recordShow)
Container(height: 100.0,
child: AudioRecorder(

9
lib/widgets/emoji.dart

@ -6,10 +6,12 @@ import 'package:esse/utils/adaptive.dart'; @@ -6,10 +6,12 @@ import 'package:esse/utils/adaptive.dart';
/// common button with text in center.
class Emoji extends StatelessWidget {
final Function action;
final bool emojiWidth;
const Emoji({
Key? key,
required this.action,
this.emojiWidth = false,
}) : super(key: key);
@override
@ -17,11 +19,14 @@ class Emoji extends StatelessWidget { @@ -17,11 +19,14 @@ class Emoji extends StatelessWidget {
final color = Theme.of(context).colorScheme;
double maxWidth = MediaQuery.of(context).size.width;
if (isDisplayDesktop(context)) {
maxWidth -= 520.0;
if (this.emojiWidth) {
maxWidth -= 520.0;
} else {
maxWidth -= 320;
}
}
return Container(
height: 156.0,
child: SingleChildScrollView(
child: EmojiPicker(
rows: 3,

10
pubspec.yaml

@ -101,16 +101,6 @@ flutter: @@ -101,16 +101,6 @@ flutter:
- assets/images/file_video.png
- assets/images/dir_folder.png
- assets/images/dir_favorites.png
- assets/mnemonic/chinese_simplified.txt
- assets/mnemonic/chinese_traditional.txt
- assets/mnemonic/english.txt
- assets/mnemonic/french.txt
- assets/mnemonic/italian.txt
- assets/mnemonic/japanese.txt
- assets/mnemonic/korean.txt
- assets/mnemonic/spanish.txt
- assets/answers/chinese_simplified.txt
- assets/answers/english.txt
# fonts:
# - family: 'Noto'
# fonts:

8
src/account.rs

@ -12,7 +12,7 @@ use crate::utils::crypto::{ @@ -12,7 +12,7 @@ use crate::utils::crypto::{
check_pin, decrypt, decrypt_key, encrypt_key, encrypt_multiple, hash_pin,
};
fn _mnemonic_lang_to_i64(lang: Language) -> i64 {
fn _lang_to_i64(lang: Language) -> i64 {
match lang {
Language::English => 0,
Language::SimplifiedChinese => 1,
@ -27,7 +27,7 @@ fn _mnemonic_lang_to_i64(lang: Language) -> i64 { @@ -27,7 +27,7 @@ fn _mnemonic_lang_to_i64(lang: Language) -> i64 {
}
}
pub fn mnemonic_lang_from_i64(u: i64) -> Language {
pub fn lang_from_i64(u: i64) -> Language {
match u {
0 => Language::English,
1 => Language::SimplifiedChinese,
@ -98,7 +98,7 @@ impl Account { @@ -98,7 +98,7 @@ impl Account {
}
pub fn lang(&self) -> Language {
mnemonic_lang_from_i64(self.lang)
lang_from_i64(self.lang)
}
pub fn generate(
@ -112,7 +112,7 @@ impl Account { @@ -112,7 +112,7 @@ impl Account {
avatar: Vec<u8>,
) -> Result<(Account, Keypair)> {
let (gid, sk) = generate_id(
mnemonic_lang_from_i64(lang),
lang_from_i64(lang),
mnemonic,
index,
0, // account default multiple address index is 0.

0
src/apps/jarvis/layer.rs

3
src/apps/jarvis/mod.rs

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
mod models;
pub(crate) mod rpc;
mod rpc;
pub(crate) use models::{Message, MessageType};
pub(crate) use rpc::new_rpc_handler;

157
src/apps/jarvis/models.rs

@ -1,117 +1,22 @@ @@ -1,117 +1,22 @@
use rand::Rng;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};
use tdn::types::{
group::GroupId,
primitive::Result,
rpc::{json, RpcError, RpcParam},
rpc::{json, RpcParam},
};
use tdn_storage::local::{DStorage, DsValue};
use crate::apps::chat::Friend;
use crate::storage::{chat_db, read_file, write_file, write_image};
#[derive(Eq, PartialEq, Clone)]
pub(crate) enum MessageType {
String,
Image,
File,
Contact,
Emoji,
Record,
Answer,
}
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,
MessageType::Answer => 6,
}
}
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,
6 => MessageType::Answer,
_ => MessageType::String,
}
}
pub async fn handle(
self,
base: &PathBuf,
mgid: &GroupId,
content: String,
) -> std::result::Result<Message, tdn::types::rpc::RpcError> {
let (q_type, q_raw, a_type, a_raw) = match self {
MessageType::Image => {
let bytes = read_file(&PathBuf::from(content)).await?;
let image_name = write_image(base, &mgid, &bytes).await?;
(self, image_name.clone(), MessageType::Image, image_name)
}
MessageType::File => {
let file_path = PathBuf::from(content);
let bytes = read_file(&file_path).await?;
let old_name = file_path
.file_name()
.ok_or(RpcError::ParseError)?
.to_str()
.ok_or(RpcError::ParseError)?;
let filename = write_file(base, mgid, old_name, &bytes).await?;
(self, filename.clone(), MessageType::File, filename)
}
MessageType::Contact => {
let cid: i64 = content.parse().map_err(|_| anyhow!("parse i64 failure!"))?;
let db = chat_db(base, mgid)?;
let contact = Friend::get_id(&db, cid)?.ok_or(RpcError::ParseError)?;
db.close()?;
let tmp_name = contact.name.replace(";", "-;");
let raw = format!(
"{};;{};;{}",
tmp_name,
contact.gid.to_hex(),
contact.addr.to_hex()
);
(self, raw.clone(), MessageType::Contact, raw)
}
MessageType::Answer => {
let a_raw = format!("{}", rand::thread_rng().gen_range(0..171));
(MessageType::String, content, MessageType::Answer, a_raw)
}
_ => (self.clone(), content.clone(), self, content),
};
Ok(Message::new(q_type, q_raw, a_type, a_raw))
}
}
use chat_types::MessageType;
pub(crate) struct Message {
pub id: i64,
pub q_type: MessageType,
pub q_content: String,
pub a_type: MessageType,
pub a_content: String,
pub is_me: bool,
pub m_type: MessageType,
pub content: String,
pub datetime: i64,
}
impl Message {
pub fn new(
q_type: MessageType,
q_content: String,
a_type: MessageType,
a_content: String,
) -> Message {
pub fn new(m_type: MessageType, content: String, is_me: bool) -> Message {
let start = SystemTime::now();
let datetime = start
.duration_since(UNIX_EPOCH)
@ -120,10 +25,9 @@ impl Message { @@ -120,10 +25,9 @@ impl Message {
Message {
id: 0,
q_type,
q_content,
a_type,
a_content,
is_me,
m_type,
content,
datetime,
}
}
@ -132,10 +36,9 @@ impl Message { @@ -132,10 +36,9 @@ impl Message {
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()),
content: v.pop().unwrap().as_string(),
m_type: MessageType::from_int(v.pop().unwrap().as_i64()),
is_me: v.pop().unwrap().as_bool(),
id: v.pop().unwrap().as_i64(),
}
}
@ -143,17 +46,15 @@ impl Message { @@ -143,17 +46,15 @@ impl Message {
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.is_me,
self.m_type.to_int(),
self.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");
pub fn list(db: &DStorage) -> Result<Vec<Message>> {
let sql = format!("SELECT id, is_me, m_type, content, datetime FROM messages");
let matrix = db.query(&sql)?;
let mut messages = vec![];
for values in matrix {
@ -163,26 +64,12 @@ impl Message { @@ -163,26 +64,12 @@ impl Message {
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,
"INSERT INTO messages (is_me, m_type, content, datetime) VALUES ({}, {}, '{}',{})",
self.is_me,
self.m_type.to_int(),
self.content,
self.datetime,
);
self.id = db.insert(&sql)?;
@ -190,7 +77,7 @@ impl Message { @@ -190,7 +77,7 @@ impl Message {
}
pub fn delete(db: &DStorage, id: i64) -> Result<usize> {
let sql = format!("UPDATE messages SET is_deleted = true WHERE id = {}", id);
let sql = format!("DELETE FROM messages WHERE id = {}", id);
db.delete(&sql)
}
}

82
src/apps/jarvis/rpc.rs

@ -1,45 +1,60 @@ @@ -1,45 +1,60 @@
use rand::Rng;
use std::sync::Arc;
use tdn::types::{
group::GroupId,
primitive::HandleResult,
message::SendMessage,
primitive::{HandleResult, Result},
rpc::{json, rpc_response, RpcError, RpcHandler, RpcParam},
};
use tdn_did::Language;
use tdn_storage::local::DStorage;
use tokio::sync::mpsc::Sender;
use chat_types::MessageType;
use crate::account::lang_from_i64;
use crate::apps::chat::raw_to_network_message;
use crate::rpc::RpcState;
use crate::storage::jarvis_db;
use crate::utils::answer::load_answer;
use super::{Message, MessageType};
use super::models::Message;
#[inline]
pub(crate) fn _jarvis_create(mgid: GroupId, device: &Message) -> RpcParam {
rpc_response(0, "jarvis-create", json!(device.to_rpc()), mgid)
}
async fn reply(
sender: Sender<SendMessage>,
db: DStorage,
gid: GroupId,
lang: Language,
msg: Message,
) -> Result<()> {
// analyse questions.
let content = if msg.m_type == MessageType::String {
if msg.content.ends_with("?") || msg.content.ends_with("?") {
// answer book. ascill ? and SBC case.
let answer = rand::thread_rng().gen_range(0..171);
load_answer(&lang, answer)
} else {
msg.content
}
} else {
// save
msg.content
};
#[inline]
pub(crate) fn _jarvis_delete(mgid: GroupId, id: i64) -> RpcParam {
rpc_response(0, "jarvis-delete", json!([id]), mgid)
}
let mut reply = Message::new(msg.m_type, content, false);
reply.insert(&db)?;
#[inline]
pub(crate) fn _jarvis_update(mgid: GroupId, id: i64, message: &Message) -> RpcParam {
rpc_response(
0,
"jarvis-update",
json!([id, message.a_type.to_int(), message.a_content]),
mgid,
)
let res = rpc_response(0, "jarvis-create", reply.to_rpc(), gid);
sender.send(SendMessage::Rpc(0, res, true)).await?;
Ok(())
}
pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
handler.add_method("jarvis-echo", |_, params, _| async move {
Ok(HandleResult::rpc(json!(params)))
});
handler.add_method(
"jarvis-list",
|gid: GroupId, _params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let db = jarvis_db(state.layer.read().await.base(), &gid)?;
let devices = Message::all(&db)?;
let devices = Message::list(&db)?;
db.close()?;
let mut results = vec![];
for device in devices {
@ -52,16 +67,23 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) { @@ -52,16 +67,23 @@ pub(crate) fn new_rpc_handler(handler: &mut RpcHandler<RpcState>) {
handler.add_method(
"jarvis-create",
|gid: GroupId, params: Vec<RpcParam>, state: Arc<RpcState>| async move {
let q_type = MessageType::from_int(params[0].as_i64().ok_or(RpcError::ParseError)?);
let q_content = params[1].as_str().ok_or(RpcError::ParseError)?.to_string();
let lang = lang_from_i64(params[0].as_i64().ok_or(RpcError::ParseError)?);
let m_type = MessageType::from_int(params[1].as_i64().ok_or(RpcError::ParseError)?);
let content = params[2].as_str().ok_or(RpcError::ParseError)?;
let base = state.layer.read().await.base().clone();
let mut msg = q_type.handle(&base, &gid, q_content).await?;
let db = jarvis_db(state.layer.read().await.base(), &gid)?;
let group_lock = state.group.read().await;
let base = group_lock.base().clone();
let sender = group_lock.sender();
drop(group_lock);
let (_, raw) = raw_to_network_message(&base, &gid, &m_type, content).await?;
let mut msg = Message::new(m_type, raw, true);
let db = jarvis_db(&base, &gid)?;
msg.insert(&db)?;
db.close()?;
let results = HandleResult::rpc(json!(msg.to_rpc()));
let results = HandleResult::rpc(msg.to_rpc());
tokio::spawn(reply(sender, db, gid, lang, msg));
Ok(results)
},
);

10
src/migrate/jarvis.rs

@ -2,10 +2,8 @@ @@ -2,10 +2,8 @@
pub(crate) const JARVIS_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);",
is_me INTEGER NOT NULL,
m_type INTEGER NOT NULL,
content TEXT NOT NULL,
datetime INTEGER NOT NULL);",
];

3
src/rpc.rs

@ -13,6 +13,7 @@ use tokio::sync::{ @@ -13,6 +13,7 @@ use tokio::sync::{
RwLock,
};
use crate::account::lang_from_i64;
use crate::apps::app_rpc_inject;
use crate::apps::chat::chat_conn;
use crate::apps::group::{add_layer, group_conn, GroupChat, Member};
@ -252,7 +253,7 @@ fn new_rpc_handler( @@ -252,7 +253,7 @@ fn new_rpc_handler(
"account-generate",
|_gid, params: Vec<RpcParam>, _state: Arc<RpcState>| async move {
let lang = params[0].as_i64().ok_or(RpcError::ParseError)?;
let language = crate::account::mnemonic_lang_from_i64(lang);
let language = lang_from_i64(lang);
let words = generate_mnemonic(language, Count::Words12);
Ok(HandleResult::rpc(json!([words])))
},

1
src/utils.rs

@ -1,2 +1,3 @@ @@ -1,2 +1,3 @@
pub(crate) mod answer;
pub(crate) mod crypto;
pub(crate) mod device_status;

11
src/utils/answer.rs

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
use tdn_did::Language;
mod english;
mod simplified_chinese;
pub(crate) fn load_answer(lang: &Language, index: usize) -> String {
match lang {
Language::SimplifiedChinese => simplified_chinese::WORDS[index].to_owned(),
_ => english::WORDS[index].to_owned(),
}
}

173
src/utils/answer/english.rs

@ -0,0 +1,173 @@ @@ -0,0 +1,173 @@
pub const WORDS: [&str; 171] = [
"A Substantial Effort Will Be Required",
"A Year From Now It Won't Matter",
"Absolutely Not",
"Accept A Change To Your Routine",
"Act As Though It Is Already Real",
"Adopt An Adventurous Attitude",
"Allow Yoursele To Rest First",
"Approach Cautiously",
"Ask Your Father",
"Ask Your Mother",
"Assistance Would Make Your Progress A Success",
"Avoid The First Solution",
"Be Delightfully Sure Of It",
"Be More Generous",
"Be Patient",
"Be Practical",
"Bet On It",
"Better To Wait",
"Collaboration Will Be The Key",
"Consider It An Opportunity",
"Count To 10; Ask Again",
"Deal With It Later",
"Definitely",
"Do It Early",
"Don't Ask For Any More At This Time",
"Don't Be Concerned",
"Don't Be Pressured Into Acting Too Quickly",
"Don't Be Ridiculous",
"Don't Bet On It",
"Don't Doubt It",
"Don't Forget To Have Fun",
"Don't Get Caught Up In Your Emotions",
"Don't Hesitate",
"Don't Ignore The Obvious",
"Don't Overdo It",
"Don't Wait",
"Don't Waste Your Time",
"Doubt It",
"Enjoy The Experience",
"Expect To Settle",
"Explore It With Playful Curiosity",
"Finish Something Else First",
"Focus On Your Home Life",
"Follow Someone Else's Lead",
"Follow The Advice Of Experts",
"Follow Through On Your Obligations",
"Follow Through With Your Good Intentions",
"Gentle Persistence Will Pay Off",
"Get A Clearer View",
"Get It In Writing",
"Give It All You've Got",
"If It's Done Well; If Not, Don't Do It At All",
"If You Do As You're Told",
"If You Don't Resist",
"Investigate And Then Enjoy It",
"It Cannot Fail",
"It Could Be Extraordinary",
"It Is Certain",
"It Is Not Significant",
"It Is Significant",
"It Is Something You Won't Forget",
"It Is Sure To Make Things Interesting",
"It Is Uncertain",
"It Is Worth The Trouble",
"It May Already Be A Done Deal",
"It May Be Difficult But You Will Find Value In It",
"It Seems Assured",
"It Will Affect How Others See You",
"It Will Be A Pleasure",
"It Will Bring Good Luck",
"It Will Create A Stir",
"It Will Remain Unpredictable",
"It Will Sustain You",
"It Would Be Better To Focus On Your Work",
"It Would Be Inadvisable",
"It'll Cost You",
"It's A Good Time To Make Plans",
"It's Gonna Be Great",
"It's Not Worth A Struggle",
"It's Time For You To Go",
"Keep An Open Mind",
"Keep It To Yourself",
"Laugh About It",
"Leave Behind Old Solutions",
"Let It Go",
"Listen More Carefully; Then You Will Know",
"Make A List Of Why",
"Make A List Of Why Not",
"Maybe",
"Mishaps Are Highly Probable",
"Move On",
"Never",
"No",
"No Matter What",
"Not If You're Alone",
"Now You Can",
"Only Do It Once",
"Others Will Depend On Your Choices",
"Pay Attention To The Details",
"Perhaps, When You're Older",
"Prepare For The Unexpected",
"Press For Closure",
"Proceed At A More Relaxed Pace",
"Realize That Too Many Choices Is As Difficult As Too Few",
"Reconsider Your Approach",
"Related Issues May Surface",
"Remain Flexible",
"Remove Your Own Obstacles",
"Reprioritize What Is Important",
"Respect The Rules",
"Save Your Energy",
"Seek Out More Options",
"Setting Priorities Will Be A Necessary Part Of The Process",
"Settle It Soon",
"Shift Your Focus",
"Speak Up About It",
"Startling Events May Occur As A Result",
"Take A Chance",
"Take Charge",
"Take More Time To Decide",
"Tell Someone What It Means To You",
"That Would Be A Waste Of Money",
"That's Out Of Your Control",
"The Answer Is In Your Backyard",
"The Answer May Come To You In Another Language",
"The Best Solution May Not Be The Obvious One",
"The Chance Will Not Come Again Soon",
"The Circumstances Will Change Very Quickly",
"The Outcome Will Be Positive",
"The Situation Is Unclear",
"There Is A Substantial Link To Another Situation",
"There Is Good Reason To Be Optimistic",
"There Is No Guarantee",
"There Will Be Obstacles To Overcome",
"This Is A Good Time To Make A New Plan",
"To Ensure The Best Decision, Be Calm",
"Trust Your Intuition",
"Trust Your Original Thought",
"Try A More Unlikely Solution",
"Unfavorable At This Time",
"Unquestionably",
"Upgrade Any Way You Can",
"Use Your Imagination",
"Wait",
"Wait For A Better Offer",
"Watch And See What Happens",
"Watch Your Step As You Go",
"Whatever You Do The Results Will Be Lasting",
"Yes",
"Yes,But Don't Force It",
"You Are Sure To Have Support",
"You Are Too Close To See",
"You Could Find Yourself Unable To Compromise",
"You Don't Really Care",
"You Know Better Now Than Ever Before",
"You May Have Opposition",
"You May Have To Drop Other Things",
"You Must",
"You Must Act Now",
"You Will Find Out Everything You'll Need To Know",
"You Will Need To Accommodate",
"You Will Not Be Disappointed",
"You'll Be Happy You Did",
"You'll Get The Final Word",
"You'll Have To Compromise",
"You'll Have To Make It Up As You Go",
"You'll Need More Information",
"You'll Need To Consider Other Ways",
"You'll Need To Take The Initiative",
"You'll Regret It",
"Your Actions Will Improve Thin",
];

173
src/utils/answer/simplified_chinese.rs

@ -0,0 +1,173 @@ @@ -0,0 +1,173 @@
pub const WORDS: [&str; 171] = [
"需要一个相当大的努力",
"从现在开始 一年也没有关系",
"绝对不是",
"接受改变你的日常行为",
"似乎已经是真实的了",
"采用一个冒险的态度",
"允许你选择先休息一下",
"小心谨慎的靠近",
"你还是问问爸爸吧",
"你还是问问妈妈吧",
"援助会让你成功进步",
"避免第一个解决方案",
"被人肯定",
"更慷慨",
"耐心点",
"实际一点吧",
"打赌",
"耐心的等待",
"合作将会是关键",
"考虑一下这个机会",
"数到10;再问",
"以后处理",
"当然",
"早点做到这一点",
"在这个时候不要问太多了",
"不要担心",
"不要迫于压力太快",
"别傻了",
"不要赌",
"不用怀疑了",
"不要忘记有乐趣",
"不要陷入你的感情",
"不要犹豫",
"不要忽视显而易见的",
"不要过分",
"不要等待",
"别浪费时间了",
"怀疑",
"尽情体验",
"要解决",
"探讨其俏皮的好奇心",
"完成其他事情的第一",
"专注你的家庭生活",
"遵循别人的引导",
"遵循专家的建议",
"履行你自己的义务",
"按照你的意愿",
"温柔的坚持就是胜利",
"获得更清晰的视野",
"把它写下来",
"给它你的一切",
"如果是,做的很好;如果不是,就不要这么做;",
"如果你按我说的做",
"如果你不反抗",
"调查研究,然后享受它!",
"这不可能失败",
"这可能是特别的",
"这是肯定的",
"这不是重要的",
"这是非常重要",
"这是你不会忘记的",
"这肯定会让事情变得有趣",
"这是不确定的",
"是值得的麻烦",
"这可能已经是木已成舟",
"这可能很难,但你会发现它的价值",
"这个似乎放心",
"这将影响别人看你",
"这将是一种享受",
"这会带来好运",
"这将会创造一个扰乱",
"这仍然是不可预测的",
"它会支持你的",
"这将更好地专注于自己的工作",
"这是不明智的",
"它会让你付出代价",
"这是一个不错的时间安排",
"会很棒的",
"这是不值得的斗争",
"是你走的时候",
"保持开放的心态",
"不要让别人知道",
"笑一下",
"离开旧的解决方案",
"过去的事就让它过去吧",
"仔细聆听;那么你就会知道",
"列出原因",
"为什么不列出原因",
"也许",
"不幸的是极有可能的",
"继续前行",
"从来没有",
"没有",
"不管是什么",
"如果你不孤单",
"现在你可以",
"只做一次",
"其他人将取决于你的选择",
"注重细节",
"也许,当你老了",
"为突发事件做好准备",
"按下关闭",
"须以较宽松的步伐",
"有时候选择太多,就代表着无从选择!",
"重新考虑你的方法",
"相关的问题可能的浮出水面",
"保持灵活",
"删除你自己的障碍物",
"重新确定优先次序什么是重要的",
"尊重规则",
"保存你的精力",
"寻找更多的选择",
"设定优先等级是一个必要的过程!",
"很快就会解决它",
"转移你的焦点",
"说出来吧",
"令人震惊的事件可能发生的",
"冒险一试",
"负责",
"花更多的时间来决定",
"告诉别人它对你意味着什么",
"那将是浪费钱",
"那是脱离你的控制",
"答案就在你的后院",
"答案可能会在另一种语言",
"最好的解决办法可能不是明显的",
"机会不会很快再来",
"情况将很快发生改变",
"结果会是好的",
"情况不明",
"与另一种情况有潜在的联系",
"有充分的理由保持乐观",
"没有保证",
"会有障碍要克服",
"这是一个很好的时机,来制定新计划",
"为了确保能作出最佳决策,需要保持冷静",
"相信自己的直觉",
"相信你的原始思想",
"尝试一种更不太可能的解决方案",
"不宜在这个时候",
"毫无疑问",
"无论如何你可以提升",
"运用你的想象力",
"等一等",
"等待一个更好的机会",
"看看会发生什么",
"注意你的节奏",
"不管你做什么,结果将持久",
"可以",
"是的,但不要强迫",
"你一定有支持",
"你太近的看了",
"你可能觉得自己无法妥协",
"你真的不在乎",
"你知道现在比以前更好",
"你可能会反对",
"你可能会放弃其他的东西",
"你必须",
"你现在必须行动",
"你会发现一切你需要知道的",
"你将需要适应",
"你不会失望的",
"你会很高兴你做了",
"你会得到最后的决定",
"你将不得不妥协",
"你将不得不补回来",
"您需要了解更多信息",
"你需要考虑其他办法",
"你需要主动出击",
"你会后悔的",
"你的行动将会改善",
];
Loading…
Cancel
Save