Skip to content

Commit

Permalink
Merge pull request #5 from hatemragab/group_chat
Browse files Browse the repository at this point in the history
Group chat v1
  • Loading branch information
hatemragab authored Dec 9, 2021
2 parents 7ff8d69 + 8aaec91 commit 7e0b6d8
Show file tree
Hide file tree
Showing 46 changed files with 785 additions and 259 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,8 @@

## 0.5.0

* hug update on backend service v 1.0.1 must use with vchat v 0.5.0
* hug update on backend service v 1.0.1 must use with vchat v 0.5.0

## 0.6.0

* start implement group chat v1 still need more improvements
8 changes: 8 additions & 0 deletions example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<!-- Don't add this in your app this for url launcher -->
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
</queries>

<application
android:icon="@mipmap/ic_launcher"
android:label="v_chat_sdk_example"
Expand Down
2 changes: 1 addition & 1 deletion example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ void main() async {

await GetStorage.init();
// http://170.178.195.150:81/
//10.0.2.2:3000
await VChatController.instance.init(
baseUrl: Uri.parse("http://170.178.195.150:81"),
appName: "test_v_chat",
Expand All @@ -32,7 +33,6 @@ void main() async {
enableLogger: true,
maxMediaUploadSize: 50 * 1000 * 1000,
passwordHashKey: "passwordHashKey",

);

// add support new language
Expand Down
26 changes: 7 additions & 19 deletions example/lib/models/user.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ class User {
final String email;
final String name;
final String accessToken;
final bool isSelected;

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

Expand All @@ -13,27 +14,9 @@ class User {
required this.email,
required this.name,
required this.accessToken,
required this.isSelected,
});

@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is User &&
runtimeType == other.runtimeType &&
id == other.id &&
imageThumb == other.imageThumb &&
email == other.email &&
name == other.name &&
accessToken == other.accessToken);

@override
int get hashCode =>
id.hashCode ^
imageThumb.hashCode ^
email.hashCode ^
name.hashCode ^
accessToken.hashCode;

@override
String toString() {
return 'User{'
Expand All @@ -42,6 +25,7 @@ class User {
' email: $email,'
' name: $name,'
' accessToken: $accessToken,'
' isSelect: $isSelected,'
'}';
}

Expand All @@ -51,12 +35,14 @@ class User {
String? email,
String? name,
String? accessToken,
bool? isSelected,
}) {
return User(
id: id ?? this.id,
imageThumb: imageThumb ?? this.imageThumb,
email: email ?? this.email,
name: name ?? this.name,
isSelected: isSelected ?? this.isSelected,
accessToken: accessToken ?? this.accessToken,
);
}
Expand All @@ -67,6 +53,7 @@ class User {
'imageThumb': imageThumb,
'email': email,
'name': name,
'isSelected': isSelected,
'accessToken': accessToken,
};
}
Expand All @@ -77,6 +64,7 @@ class User {
imageThumb: map['imageThumb'] as String,
email: map['email'] as String,
name: map['name'] as String,
isSelected: false,
accessToken: map['accessToken'] ?? "",
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import 'package:example/models/user.dart';
import 'package:example/screens/create_group/create_group_page.dart';
import 'package:example/utils/custom_alert.dart';
import 'package:example/utils/custom_dio.dart';
import 'package:example/utils/load_more_type.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get_storage/get_storage.dart';

class ChooseGroupMembersController extends ChangeNotifier {
late BuildContext context;
final users = <User>[];
LoadMoreStatus loadingStatus = LoadMoreStatus.loaded;

final scrollController = ScrollController();
final myModel = GetStorage().read("myModel");

ChooseGroupMembersController({
required this.context,
}) {
getUsers();
scrollController.addListener(_scrollListener);
}

void updateScreen() {
notifyListeners();
}

void _scrollListener() async {
if (scrollController.offset >=
scrollController.position.maxScrollExtent / 2 &&
!scrollController.position.outOfRange &&
loadingStatus != LoadMoreStatus.loading &&
loadingStatus != LoadMoreStatus.completed) {
loadingStatus = LoadMoreStatus.loading;
loadMore();
}
}

Future<void> loadMore() async {
final loadedRooms = await loadMoreApi(users.last.id);
loadingStatus = LoadMoreStatus.loaded;
if (loadedRooms.isEmpty) {
loadingStatus = LoadMoreStatus.completed;
}
users.addAll(loadedRooms);
updateScreen();
}

Future<List<User>> loadMoreApi(String lastId) async {
final roomsMaps = (await CustomDio()
.send(reqMethod: "get", path: "user", query: {"lastId": lastId}))
.data['data'] as List;
return roomsMaps.map((e) => User.fromMap(e)).toList();
}

Future<void> getUsers() async {
try {
final res = (await CustomDio()
.send(reqMethod: "get", path: "user", query: {"lastId": "first"}))
.data['data'] as List;
users.addAll(res.map((e) => User.fromMap(e)).toList());
updateScreen();
} catch (err) {
CustomAlert.showError(context: context, err: err.toString());
rethrow;
}
}

void setSelectedUser(User user) {
final index = users.indexWhere((element) => element.id == user.id);
if (index != -1) {
final newUser =
users[index].copyWith(isSelected: !users[index].isSelected);
print(newUser.toString());
users.removeAt(index);
users.insert(index, newUser);
updateScreen();
}
}

void next() {
try {
isThereUserSelected();
final seletedUsers = <User>[];
for (final u in users) {
if (u.isSelected) {
seletedUsers.add(u);
}
}
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => CreateGroupPage(
users: seletedUsers,
),
));
} catch (err) {
CustomAlert.showError(context: context, err: err.toString());
}
}

void isThereUserSelected() {
bool isThereSelection = false;
for (final u in users) {
if (u.isSelected) {
if (u.id == myModel['_id']) {
throw "un select your self from the list";
}
isThereSelection = true;
}
}
if (isThereSelection == false) {
throw "select at lest one user ";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import 'package:example/utils/config.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:textless/textless.dart';
import 'choose_group_members_controller.dart';

class ChooseGroupMembersPage extends StatelessWidget {
const ChooseGroupMembersPage({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) =>
ChooseGroupMembersController(context: context),
builder: (context, child) => ChooseGroupMembersScreen(),
);
}
}

class ChooseGroupMembersScreen extends StatelessWidget {
const ChooseGroupMembersScreen({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
final controller = Provider.of<ChooseGroupMembersController>(context);
final usersList = controller.users;
return Scaffold(
floatingActionButton: FloatingActionButton(
elevation: 0,
onPressed: controller.next,
child: Icon(Icons.arrow_right),
),
appBar: AppBar(
title: "choose members".text,
centerTitle: true,
elevation: 0,
),
body: Scrollbar(
child: ListView.separated(
controller: controller.scrollController,
padding: const EdgeInsets.all(10),
itemBuilder: (context, index) => ListTile(
title: usersList[index].name.text,
onTap: () => controller.setSelectedUser(usersList[index]),
leading: Image.network(baseImgUrl + usersList[index].imageThumb),
selected: usersList[index].isSelected),
separatorBuilder: (context, index) => const Divider(),
itemCount: usersList.length,
),
),
);
}
}
66 changes: 66 additions & 0 deletions example/lib/screens/create_group/create_group_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import 'dart:io';

import 'package:example/models/user.dart';
import 'package:example/utils/custom_alert.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:v_chat_sdk/v_chat_sdk.dart';

import '../home.dart';

class CreateGroupController extends ChangeNotifier {
final BuildContext context;
final List<User> users;
final textEditingController = TextEditingController();
String? imagePath;

CreateGroupController({
required this.context,
required this.users,
});

void createGroup() async {
try {
CustomAlert.customLoadingDialog(context: context);
if (textEditingController.text.isEmpty) {
throw "Enter title";
}
if (imagePath == null) {
throw "Choose group image";
}
await VChatController.instance.createGroupChat(
context: context,
createGroupRoomDto: CreateGroupRoomDto(
groupImage: File(imagePath!),
groupTitle: textEditingController.text,
usersEmails: users.map((e) => e.email).toList(),
),
);
// Navigator.pop(context);
// Navigator.pop(context);
// Navigator.pop(context);
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (context) => const Home()),
(Route<dynamic> route) => false);
} catch (err) {
Navigator.pop(context);
CustomAlert.showError(context: context, err: err.toString());
}
}

void pickGroupImage() async {
final picker = ImagePicker();
final img = await picker.pickImage(source: ImageSource.gallery);
if (img != null) {
if (File(img.path).lengthSync() > 1024 * 1024 * 20) {
CustomAlert.showError(
context: context, err: "image size must be less than 20 Mb");
throw "image size must be less than 20 Mb";
}
imagePath = img.path;
CustomAlert.showSuccess(
context: context, err: "image has been selected ");
}
}
}
Loading

0 comments on commit 7e0b6d8

Please sign in to comment.