Skip to content

Commit 7e0b6d8

Browse files
authored
Merge pull request #5 from hatemragab/group_chat
Group chat v1
2 parents 7ff8d69 + 8aaec91 commit 7e0b6d8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+785
-259
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,8 @@
4848

4949
## 0.5.0
5050

51-
* hug update on backend service v 1.0.1 must use with vchat v 0.5.0
51+
* hug update on backend service v 1.0.1 must use with vchat v 0.5.0
52+
53+
## 0.6.0
54+
55+
* start implement group chat v1 still need more improvements

example/android/app/src/main/AndroidManifest.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@
66
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
77
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
88

9+
<!-- Don't add this in your app this for url launcher -->
10+
<queries>
11+
<intent>
12+
<action android:name="android.intent.action.VIEW" />
13+
<data android:scheme="https" />
14+
</intent>
15+
</queries>
16+
917
<application
1018
android:icon="@mipmap/ic_launcher"
1119
android:label="v_chat_sdk_example"

example/lib/main.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ void main() async {
2424

2525
await GetStorage.init();
2626
// http://170.178.195.150:81/
27+
//10.0.2.2:3000
2728
await VChatController.instance.init(
2829
baseUrl: Uri.parse("http://170.178.195.150:81"),
2930
appName: "test_v_chat",
@@ -32,7 +33,6 @@ void main() async {
3233
enableLogger: true,
3334
maxMediaUploadSize: 50 * 1000 * 1000,
3435
passwordHashKey: "passwordHashKey",
35-
3636
);
3737

3838
// add support new language

example/lib/models/user.dart

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ class User {
44
final String email;
55
final String name;
66
final String accessToken;
7+
final bool isSelected;
78

89
//<editor-fold desc="Data Methods">
910

@@ -13,27 +14,9 @@ class User {
1314
required this.email,
1415
required this.name,
1516
required this.accessToken,
17+
required this.isSelected,
1618
});
1719

18-
@override
19-
bool operator ==(Object other) =>
20-
identical(this, other) ||
21-
(other is User &&
22-
runtimeType == other.runtimeType &&
23-
id == other.id &&
24-
imageThumb == other.imageThumb &&
25-
email == other.email &&
26-
name == other.name &&
27-
accessToken == other.accessToken);
28-
29-
@override
30-
int get hashCode =>
31-
id.hashCode ^
32-
imageThumb.hashCode ^
33-
email.hashCode ^
34-
name.hashCode ^
35-
accessToken.hashCode;
36-
3720
@override
3821
String toString() {
3922
return 'User{'
@@ -42,6 +25,7 @@ class User {
4225
' email: $email,'
4326
' name: $name,'
4427
' accessToken: $accessToken,'
28+
' isSelect: $isSelected,'
4529
'}';
4630
}
4731

@@ -51,12 +35,14 @@ class User {
5135
String? email,
5236
String? name,
5337
String? accessToken,
38+
bool? isSelected,
5439
}) {
5540
return User(
5641
id: id ?? this.id,
5742
imageThumb: imageThumb ?? this.imageThumb,
5843
email: email ?? this.email,
5944
name: name ?? this.name,
45+
isSelected: isSelected ?? this.isSelected,
6046
accessToken: accessToken ?? this.accessToken,
6147
);
6248
}
@@ -67,6 +53,7 @@ class User {
6753
'imageThumb': imageThumb,
6854
'email': email,
6955
'name': name,
56+
'isSelected': isSelected,
7057
'accessToken': accessToken,
7158
};
7259
}
@@ -77,6 +64,7 @@ class User {
7764
imageThumb: map['imageThumb'] as String,
7865
email: map['email'] as String,
7966
name: map['name'] as String,
67+
isSelected: false,
8068
accessToken: map['accessToken'] ?? "",
8169
);
8270
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import 'package:example/models/user.dart';
2+
import 'package:example/screens/create_group/create_group_page.dart';
3+
import 'package:example/utils/custom_alert.dart';
4+
import 'package:example/utils/custom_dio.dart';
5+
import 'package:example/utils/load_more_type.dart';
6+
import 'package:flutter/cupertino.dart';
7+
import 'package:flutter/material.dart';
8+
import 'package:get_storage/get_storage.dart';
9+
10+
class ChooseGroupMembersController extends ChangeNotifier {
11+
late BuildContext context;
12+
final users = <User>[];
13+
LoadMoreStatus loadingStatus = LoadMoreStatus.loaded;
14+
15+
final scrollController = ScrollController();
16+
final myModel = GetStorage().read("myModel");
17+
18+
ChooseGroupMembersController({
19+
required this.context,
20+
}) {
21+
getUsers();
22+
scrollController.addListener(_scrollListener);
23+
}
24+
25+
void updateScreen() {
26+
notifyListeners();
27+
}
28+
29+
void _scrollListener() async {
30+
if (scrollController.offset >=
31+
scrollController.position.maxScrollExtent / 2 &&
32+
!scrollController.position.outOfRange &&
33+
loadingStatus != LoadMoreStatus.loading &&
34+
loadingStatus != LoadMoreStatus.completed) {
35+
loadingStatus = LoadMoreStatus.loading;
36+
loadMore();
37+
}
38+
}
39+
40+
Future<void> loadMore() async {
41+
final loadedRooms = await loadMoreApi(users.last.id);
42+
loadingStatus = LoadMoreStatus.loaded;
43+
if (loadedRooms.isEmpty) {
44+
loadingStatus = LoadMoreStatus.completed;
45+
}
46+
users.addAll(loadedRooms);
47+
updateScreen();
48+
}
49+
50+
Future<List<User>> loadMoreApi(String lastId) async {
51+
final roomsMaps = (await CustomDio()
52+
.send(reqMethod: "get", path: "user", query: {"lastId": lastId}))
53+
.data['data'] as List;
54+
return roomsMaps.map((e) => User.fromMap(e)).toList();
55+
}
56+
57+
Future<void> getUsers() async {
58+
try {
59+
final res = (await CustomDio()
60+
.send(reqMethod: "get", path: "user", query: {"lastId": "first"}))
61+
.data['data'] as List;
62+
users.addAll(res.map((e) => User.fromMap(e)).toList());
63+
updateScreen();
64+
} catch (err) {
65+
CustomAlert.showError(context: context, err: err.toString());
66+
rethrow;
67+
}
68+
}
69+
70+
void setSelectedUser(User user) {
71+
final index = users.indexWhere((element) => element.id == user.id);
72+
if (index != -1) {
73+
final newUser =
74+
users[index].copyWith(isSelected: !users[index].isSelected);
75+
print(newUser.toString());
76+
users.removeAt(index);
77+
users.insert(index, newUser);
78+
updateScreen();
79+
}
80+
}
81+
82+
void next() {
83+
try {
84+
isThereUserSelected();
85+
final seletedUsers = <User>[];
86+
for (final u in users) {
87+
if (u.isSelected) {
88+
seletedUsers.add(u);
89+
}
90+
}
91+
Navigator.of(context).push(MaterialPageRoute(
92+
builder: (context) => CreateGroupPage(
93+
users: seletedUsers,
94+
),
95+
));
96+
} catch (err) {
97+
CustomAlert.showError(context: context, err: err.toString());
98+
}
99+
}
100+
101+
void isThereUserSelected() {
102+
bool isThereSelection = false;
103+
for (final u in users) {
104+
if (u.isSelected) {
105+
if (u.id == myModel['_id']) {
106+
throw "un select your self from the list";
107+
}
108+
isThereSelection = true;
109+
}
110+
}
111+
if (isThereSelection == false) {
112+
throw "select at lest one user ";
113+
}
114+
}
115+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import 'package:example/utils/config.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:provider/provider.dart';
4+
import 'package:textless/textless.dart';
5+
import 'choose_group_members_controller.dart';
6+
7+
class ChooseGroupMembersPage extends StatelessWidget {
8+
const ChooseGroupMembersPage({Key? key}) : super(key: key);
9+
10+
@override
11+
Widget build(BuildContext context) {
12+
return ChangeNotifierProvider(
13+
create: (BuildContext context) =>
14+
ChooseGroupMembersController(context: context),
15+
builder: (context, child) => ChooseGroupMembersScreen(),
16+
);
17+
}
18+
}
19+
20+
class ChooseGroupMembersScreen extends StatelessWidget {
21+
const ChooseGroupMembersScreen({Key? key}) : super(key: key);
22+
23+
@override
24+
Widget build(BuildContext context) {
25+
final controller = Provider.of<ChooseGroupMembersController>(context);
26+
final usersList = controller.users;
27+
return Scaffold(
28+
floatingActionButton: FloatingActionButton(
29+
elevation: 0,
30+
onPressed: controller.next,
31+
child: Icon(Icons.arrow_right),
32+
),
33+
appBar: AppBar(
34+
title: "choose members".text,
35+
centerTitle: true,
36+
elevation: 0,
37+
),
38+
body: Scrollbar(
39+
child: ListView.separated(
40+
controller: controller.scrollController,
41+
padding: const EdgeInsets.all(10),
42+
itemBuilder: (context, index) => ListTile(
43+
title: usersList[index].name.text,
44+
onTap: () => controller.setSelectedUser(usersList[index]),
45+
leading: Image.network(baseImgUrl + usersList[index].imageThumb),
46+
selected: usersList[index].isSelected),
47+
separatorBuilder: (context, index) => const Divider(),
48+
itemCount: usersList.length,
49+
),
50+
),
51+
);
52+
}
53+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import 'dart:io';
2+
3+
import 'package:example/models/user.dart';
4+
import 'package:example/utils/custom_alert.dart';
5+
import 'package:flutter/cupertino.dart';
6+
import 'package:flutter/material.dart';
7+
import 'package:image_picker/image_picker.dart';
8+
import 'package:v_chat_sdk/v_chat_sdk.dart';
9+
10+
import '../home.dart';
11+
12+
class CreateGroupController extends ChangeNotifier {
13+
final BuildContext context;
14+
final List<User> users;
15+
final textEditingController = TextEditingController();
16+
String? imagePath;
17+
18+
CreateGroupController({
19+
required this.context,
20+
required this.users,
21+
});
22+
23+
void createGroup() async {
24+
try {
25+
CustomAlert.customLoadingDialog(context: context);
26+
if (textEditingController.text.isEmpty) {
27+
throw "Enter title";
28+
}
29+
if (imagePath == null) {
30+
throw "Choose group image";
31+
}
32+
await VChatController.instance.createGroupChat(
33+
context: context,
34+
createGroupRoomDto: CreateGroupRoomDto(
35+
groupImage: File(imagePath!),
36+
groupTitle: textEditingController.text,
37+
usersEmails: users.map((e) => e.email).toList(),
38+
),
39+
);
40+
// Navigator.pop(context);
41+
// Navigator.pop(context);
42+
// Navigator.pop(context);
43+
Navigator.of(context).pushAndRemoveUntil(
44+
MaterialPageRoute(builder: (context) => const Home()),
45+
(Route<dynamic> route) => false);
46+
} catch (err) {
47+
Navigator.pop(context);
48+
CustomAlert.showError(context: context, err: err.toString());
49+
}
50+
}
51+
52+
void pickGroupImage() async {
53+
final picker = ImagePicker();
54+
final img = await picker.pickImage(source: ImageSource.gallery);
55+
if (img != null) {
56+
if (File(img.path).lengthSync() > 1024 * 1024 * 20) {
57+
CustomAlert.showError(
58+
context: context, err: "image size must be less than 20 Mb");
59+
throw "image size must be less than 20 Mb";
60+
}
61+
imagePath = img.path;
62+
CustomAlert.showSuccess(
63+
context: context, err: "image has been selected ");
64+
}
65+
}
66+
}

0 commit comments

Comments
 (0)