-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' of https://github.com/flowbehappy/moyubie
- Loading branch information
Showing
7 changed files
with
465 additions
and
156 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,38 @@ | ||
Moyubie | ||
==== | ||
# Moyubie | ||
|
||
Moyubie is a cross-platform AI chat APP which stores AI chat records on DBaaS so users can access them from any device. Moyubie uses users' AI chat records to analyze and make guesses about which information the user might like, then it fetches that information from the internet. Moyubie is powered by AI technology and stores data on serverless database, e.g. [TiDB Cloud Serverless](https://tidbcloud.com/free-trial). There is no backend server for Moyubie, so users have full control of their personal data. | ||
Moyubie is a cross-platform AI powered IM (Instant Messaging) APP. | ||
|
||
* It is a convenient and powerful AI chat client. Chat messages are synchronized between different devices. | ||
* Users can chat to anyone in a chat room in a private environment | ||
* There is no server side required. Chat messages are secure and completely owned by users. | ||
* A news list powered by AI | ||
* Supported platforms | ||
* iOS, Android, MacOS, Windows, Linux, Web | ||
|
||
Thank list | ||
==== | ||
* This project's UI part is based on [flutter_chat_box](https://github.com/bravekingzhang/flutter_chat_box). | ||
## Powered By | ||
|
||
* [TiDB Cloud Serverless](https://tidbcloud.com/free-trial) | ||
* AGI services. Currently supports | ||
* ChatGPT | ||
|
||
## Development | ||
|
||
* You can learn how to prepare flutter development environment, compile and test a [flutter](https://flutter.dev) app from [flutter.dev](https://flutter.dev/). | ||
* After all required tools are installed, you can compile and debug the app by | ||
``` | ||
git clone [email protected]:flowbehappy/moyubie.git | ||
cd moyubie | ||
flutter doctor # Make sure everything is fine | ||
flutter devices # See what devices or simulator you have | ||
flutter run # Run the app. You can also run the app in a specific device/simulator by: flutter run -d <device_id> | ||
``` | ||
|
||
## Downloads | ||
* iOS (TODO) | ||
* Android (TODO) | ||
* MacOS (TODO) | ||
* Windows (TODO) | ||
* Linux (TODO) | ||
* Web (TODO) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
import 'package:dual_screen/dual_screen.dart'; | ||
import 'package:flutter/material.dart'; | ||
import 'package:get/get.dart'; | ||
import 'package:get_storage/get_storage.dart'; | ||
import 'package:moyubie/components/chat.dart'; | ||
import 'package:moyubie/repository/chat_room.dart' as repo; | ||
import 'package:moyubie/controller/chat_room.dart' as comp; | ||
import 'package:uuid/uuid.dart'; | ||
|
||
enum ChatRoomType { | ||
tablet, | ||
phone, | ||
} | ||
|
||
class ChatRoom extends StatefulWidget { | ||
const ChatRoom({ | ||
super.key, | ||
required this.restorationId, | ||
required this.type, | ||
}); | ||
|
||
final String restorationId; | ||
final ChatRoomType type; | ||
|
||
@override | ||
State<ChatRoom> createState() => _ChatRoomState(); | ||
} | ||
|
||
class _ChatRoomState extends State<ChatRoom> { | ||
@override | ||
Widget build(BuildContext context) { | ||
return GetX<comp.ChatRoomController>(builder: (controller) { | ||
var panePriority = TwoPanePriority.both; | ||
if (widget.type == ChatRoomType.phone) { | ||
panePriority = controller.currentRoomIndex.value.value == -1 | ||
? TwoPanePriority.start | ||
: TwoPanePriority.end; | ||
} | ||
return TwoPane( | ||
paneProportion: 0.3, | ||
panePriority: panePriority, | ||
startPane: ListPane( | ||
selectedIndex: controller.currentRoomIndex.value.value, | ||
onSelect: _selectRoom, | ||
), | ||
endPane: DetailsPane( | ||
selectedIndex: controller.currentRoomIndex.value.value, | ||
onClose: widget.type == ChatRoomType.phone | ||
? () { | ||
_selectRoom(-1); | ||
} | ||
: null, | ||
), | ||
); | ||
}); | ||
} | ||
|
||
void _selectRoom(int index) { | ||
final comp.ChatRoomController chatRoomController = Get.find(); | ||
chatRoomController.setCurrentRoomIndex(index); | ||
} | ||
} | ||
|
||
class ListPane extends StatelessWidget { | ||
final ValueChanged<int> onSelect; | ||
final int selectedIndex; | ||
final _scrollController = ScrollController(); | ||
|
||
ListPane({ | ||
super.key, | ||
required this.onSelect, | ||
required this.selectedIndex, | ||
}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return GetX<comp.ChatRoomController>(builder: (controller) { | ||
return Scaffold( | ||
appBar: AppBar( | ||
automaticallyImplyLeading: false, | ||
title: const Text("Chat Room"), | ||
actions: const [NewChatButton()]), | ||
body: Scrollbar( | ||
controller: _scrollController, | ||
child: ListView( | ||
controller: _scrollController, | ||
restorationId: 'list_demo_list_view', | ||
padding: const EdgeInsets.symmetric(vertical: 8), | ||
children: controller.roomList | ||
.asMap() | ||
.map((index, room) { | ||
return MapEntry( | ||
index, | ||
ListTile( | ||
onTap: () { | ||
onSelect(index); | ||
}, | ||
selected: selectedIndex == index, | ||
leading: ExcludeSemantics( | ||
child: CircleAvatar(child: Text('$index')), | ||
), | ||
title: Text( | ||
'chat room $index', | ||
), | ||
)); | ||
}) | ||
.values | ||
.toList(), | ||
), | ||
), | ||
); | ||
}); | ||
} | ||
} | ||
|
||
class DetailsPane extends StatelessWidget { | ||
final VoidCallback? onClose; | ||
final int selectedIndex; | ||
|
||
const DetailsPane({ | ||
super.key, | ||
required this.selectedIndex, | ||
this.onClose, | ||
}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return Scaffold( | ||
appBar: AppBar( | ||
automaticallyImplyLeading: false, | ||
leading: onClose == null | ||
? null | ||
: IconButton(icon: const Icon(Icons.close), onPressed: onClose), | ||
title: Text( | ||
selectedIndex == -1 ? "" : "Chat Room $selectedIndex", | ||
), | ||
), | ||
body: const ChatWindow(), | ||
); | ||
} | ||
} | ||
|
||
class NewChatButton extends StatelessWidget { | ||
const NewChatButton({ | ||
super.key, | ||
}); | ||
|
||
@override | ||
Widget build(BuildContext context) { | ||
return PopupMenuButton<Text>( | ||
padding: const EdgeInsets.only(right: 32), | ||
icon: const Icon(Icons.add), | ||
itemBuilder: (context) { | ||
return [ | ||
PopupMenuItem( | ||
child: ListTile( | ||
leading: const Icon(Icons.add), | ||
title: const Align( | ||
alignment: Alignment(-1.2, 0), | ||
child: Text("New Chat Room"), | ||
), | ||
onTap: _addNewChatRoom, | ||
), | ||
), | ||
const PopupMenuItem( | ||
child: ListTile( | ||
leading: Icon(Icons.group_add), | ||
title: Align( | ||
alignment: Alignment(-1.2, 0), | ||
child: Text("Join Chat Room"), | ||
), | ||
), | ||
), | ||
]; | ||
}, | ||
); | ||
} | ||
|
||
_addNewChatRoom() { | ||
final comp.ChatRoomController chatRoomController = Get.find(); | ||
var uuid = const Uuid().toString(); | ||
var createTime = DateTime.now(); | ||
repo.ChatRoom chatRoom = repo.ChatRoom( | ||
uuid: uuid, | ||
name: "New Chat Room", | ||
createTime: createTime, | ||
connectionToken: ""); | ||
chatRoomController.addChatRoom(chatRoom); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.