Commit 801f6506 authored by Inomjon's avatar Inomjon

Chat ishlatib ko'rildi

parent cb19bef2
...@@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; ...@@ -5,6 +5,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:vmeeting/blocs/edit_profile_bloc/edit_profile_bloc.dart'; import 'package:vmeeting/blocs/edit_profile_bloc/edit_profile_bloc.dart';
import 'package:vmeeting/blocs/user_sign_up_bloc/user_sign_up_bloc.dart'; import 'package:vmeeting/blocs/user_sign_up_bloc/user_sign_up_bloc.dart';
import 'package:vmeeting/service/routes/routes_name.dart'; import 'package:vmeeting/service/routes/routes_name.dart';
import 'package:vmeeting/srcchat/chat_dialog_screen.dart';
import 'package:vmeeting/views/main_view/main_page.dart'; import 'package:vmeeting/views/main_view/main_page.dart';
import '../../blocs/user_login_bloc/user_log_in_bloc.dart'; import '../../blocs/user_login_bloc/user_log_in_bloc.dart';
import '../../src/controllers/enter_number_cont.dart'; import '../../src/controllers/enter_number_cont.dart';
...@@ -50,6 +51,9 @@ class MainNavigator extends StatelessWidget { ...@@ -50,6 +51,9 @@ class MainNavigator extends StatelessWidget {
case MainRoutes.reset_password_page: case MainRoutes.reset_password_page:
builder = (BuildContext _) => ResetPasswordPage(controller: controller); builder = (BuildContext _) => ResetPasswordPage(controller: controller);
break; break;
// case MainRoutes.chat_dialog:
// builder = (BuildContext _) => ChatDialogScreen(args![USER_ARG_NAME], args[DIALOG_ARG_NAME]);
// break;
case MainRoutes.user_signup_page: case MainRoutes.user_signup_page:
builder = (BuildContext _) => MultiBlocProvider( builder = (BuildContext _) => MultiBlocProvider(
providers: [ providers: [
......
...@@ -8,4 +8,5 @@ class MainRoutes { ...@@ -8,4 +8,5 @@ class MainRoutes {
static const String old_sign_in = "old_sign_in"; static const String old_sign_in = "old_sign_in";
static const String edit_profile_page = "edit_profile_page"; static const String edit_profile_page = "edit_profile_page";
static const String reset_password_page = "reset_password_page"; static const String reset_password_page = "reset_password_page";
static const String chat_dialog = "chat_dialog";
} }
\ No newline at end of file
import 'dart:async';
import 'package:connectycube_sdk/connectycube_sdk.dart';
class ChatManager {
static ChatManager? _instance;
ChatManager._();
static ChatManager get instance => _instance ??= ChatManager._();
StreamController<CubeMessage> sentMessagesController =
StreamController.broadcast();
Stream<CubeMessage> get sentMessagesStream {
return sentMessagesController.stream;
}
StreamController<MessageStatus> readMessagesController =
StreamController.broadcast();
Stream<MessageStatus> get readMessagesStream {
return readMessagesController.stream;
}
}
...@@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; ...@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:vmeeting/src/constants/colors_const.dart'; import 'package:vmeeting/src/constants/colors_const.dart';
import '../../app_models/app_users_model/app_users_model.dart'; import '../../app_models/app_users_model/app_users_model.dart';
import '../widgets/custom_loading/customloading.dart';
class AppUtils { class AppUtils {
static Widget buttonLoader = SizedBox( static Widget buttonLoader = SizedBox(
...@@ -36,5 +37,9 @@ class AppUtils { ...@@ -36,5 +37,9 @@ class AppUtils {
} }
static UserModel userModel = UserModel(); static UserModel userModel = UserModel();
static CubeUser cubeUser = CubeUser(); static CubeUser cubeUser = CubeUser();
static Widget buildLoading() {
return Center(child: CustomLoading());
}
} }
import 'package:connectycube_sdk/connectycube_chat.dart';
import 'package:flutter/material.dart';
import 'package:vmeeting/srcchat/utils/consts.dart';
import 'package:vmeeting/srcchat/widgets/common.dart';
class AddOccupantScreen extends StatefulWidget {
final CubeUser _cubeUser;
@override
State<StatefulWidget> createState() {
return _AddOccupantScreenState(_cubeUser);
}
AddOccupantScreen(this._cubeUser);
}
class _AddOccupantScreenState extends State<AddOccupantScreen> {
static const String TAG = "_AddOccupantScreenState";
final CubeUser currentUser;
_AddOccupantScreenState(this.currentUser);
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => _onBackPressed(context),
child: Scaffold(
appBar: AppBar(
automaticallyImplyLeading: true,
title: Text(
'Contacts',
),
),
body: BodyLayout(currentUser),
),
);
}
Future<bool> _onBackPressed(BuildContext context) {
Navigator.pop(context);
return Future.value(false);
}
}
class BodyLayout extends StatefulWidget {
final CubeUser currentUser;
BodyLayout(this.currentUser);
@override
State<StatefulWidget> createState() {
return _BodyLayoutState(currentUser);
}
}
class _BodyLayoutState extends State<BodyLayout> {
static const String TAG = "_BodyLayoutState";
final CubeUser currentUser;
List<CubeUser> userList = [];
Set<int> _selectedUsers = {};
var _isUsersContinues = false;
String? userToSearch;
String userMsg = " ";
_BodyLayoutState(this.currentUser);
_searchUser(value) {
log("searchUser _user= $value");
if (value != null)
setState(() {
userToSearch = value;
_isUsersContinues = true;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.only(left: 16, right: 16, bottom: 16),
child: Column(
children: [
_buildTextFields(),
Container(
margin: EdgeInsets.only(left: 8),
child: Visibility(
maintainSize: false,
maintainAnimation: false,
maintainState: false,
visible: _isUsersContinues,
child: CircularProgressIndicator(
strokeWidth: 2,
),
),
),
Expanded(
child: _getUsersList(context),
),
],
)),
floatingActionButton: new Visibility(
visible: _selectedUsers.isNotEmpty,
child: FloatingActionButton(
heroTag: "Update dialog",
child: Icon(
Icons.check,
color: Colors.white,
),
backgroundColor: Colors.blue,
onPressed: () => _updateDialog(context, _selectedUsers.toList()),
),
),
);
}
Widget _buildTextFields() {
return new Container(
child: new Column(
children: <Widget>[
new Container(
child: new TextField(
autofocus: true,
textInputAction: TextInputAction.search,
decoration: new InputDecoration(labelText: 'Search users'),
onSubmitted: (value) {
_searchUser(value.trim());
}),
),
],
),
);
}
Widget _getUsersList(BuildContext context) {
clearValues() {
_isUsersContinues = false;
userToSearch = null;
userMsg = " ";
userList.clear();
}
if (_isUsersContinues) {
if (userToSearch != null && userToSearch!.isNotEmpty) {
getUsersByFullName(userToSearch!).then((users) {
log("getusers: $users", TAG);
setState(() {
clearValues();
userList.addAll(users!.items);
if (users.items.isEmpty) {
userMsg = "Couldn't find user";
}
});
}).catchError(
(onError) {
log("getusers catchError: $onError", TAG);
setState(() {
clearValues();
userMsg = "Couldn't find user";
});
},
);
}
}
if (userList.isEmpty)
return Center(
child: Text(
userMsg,
style: TextStyle(fontSize: 20),
),
);
else
return ListView.builder(
itemCount: userList.length,
itemBuilder: _getListItemTile,
);
}
Widget _getListItemTile(BuildContext context, int index) {
return Container(
child: TextButton(
child: Row(
children: <Widget>[
getUserAvatarWidget(userList[index], 30),
Flexible(
child: Container(
child: Column(
children: <Widget>[
Container(
child: Text(
'Name: ${userList[index].fullName}',
style: TextStyle(color: primaryColor),
),
alignment: Alignment.centerLeft,
margin: EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 5.0),
),
],
),
margin: EdgeInsets.only(left: 20.0),
),
),
Container(
child: Checkbox(
value: _selectedUsers.contains(userList[index].id),
onChanged: ((checked) {
setState(() {
if (checked!) {
_selectedUsers.add(userList[index].id!);
} else {
_selectedUsers.remove(userList[index].id);
}
});
}),
),
),
],
),
onPressed: () {
setState(() {
if (_selectedUsers.contains(userList[index].id)) {
_selectedUsers.remove(userList[index].id);
} else {
_selectedUsers.add(userList[index].id!);
}
});
},
// color: greyColor2,
// padding: EdgeInsets.fromLTRB(25.0, 10.0, 25.0, 10.0),
// shape:
// RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.0)),
),
margin: EdgeInsets.only(bottom: 10.0, left: 5.0, right: 5.0),
);
}
void _updateDialog(BuildContext context, List<int> users) async {
log("_updateDialog with users= $users");
Navigator.pop(context, users);
}
}
This diff is collapsed.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:connectycube_sdk/connectycube_sdk.dart';
import 'package:vmeeting/srcchat/select_dialog_screen.dart';
import 'chat_dialog_screen.dart';
const double DIALOGS_LIST_WIDTH = 300;
const double MIN_SCREEN_SIZE = 800;
const double DIVIDER_WIDTH = 1;
class ChatDialogResizableScreen extends StatelessWidget {
static const String TAG = "SelectDialogScreen";
final CubeUser currentUser;
final CubeDialog? selectedDialog;
ChatDialogResizableScreen(this.currentUser, this.selectedDialog);
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () => _onBackPressed(),
child: Scaffold(
body: BodyLayout(currentUser, selectedDialog),
),
);
}
Future<bool> _onBackPressed() {
return Future.value(true);
}
}
class BodyLayout extends StatefulWidget {
final CubeUser currentUser;
final CubeDialog? selectedDialog;
BodyLayout(this.currentUser, this.selectedDialog);
@override
State<StatefulWidget> createState() {
return _BodyLayoutState(currentUser, selectedDialog);
}
}
class _BodyLayoutState extends State<BodyLayout> {
static const String TAG = "_BodyLayoutState";
final CubeUser currentUser;
CubeDialog? selectedDialog;
_BodyLayoutState(this.currentUser, CubeDialog? selectedDialog) {
this.selectedDialog = selectedDialog;
}
@override
Widget build(BuildContext context) {
return Container(
height: MediaQuery.of(context).size.height,
child: Row(
children: [
if (isBigScreen || selectedDialog == null)
LayoutBuilder(builder: (context, constraint) {
var width = MediaQuery.of(context).size.width;
return SizedBox(
width: isBigScreen
? width / 4 <= DIALOGS_LIST_WIDTH
? DIALOGS_LIST_WIDTH
: width / 4
: width,
child: SelectDialogScreen(selectedDialog,
(selectedDialog) {
setState(() {
this.selectedDialog = null;
Future.delayed(Duration(milliseconds: 50), () {
setState(() {
this.selectedDialog = selectedDialog;
});
});
});
}),
);
}),
Visibility(
visible: isBigScreen,
child: const VerticalDivider(
width: DIVIDER_WIDTH,
),
),
getSelectedDialog()
],
),
);
}
Widget getSelectedDialog() {
if (selectedDialog != null) {
return Flexible(
child: Stack(
children: [
ChatDialogScreen(currentUser, selectedDialog!),
Align(
alignment: Alignment.topCenter,
child: SizedBox(
height: AppBar().preferredSize.height,
child: AppBar(
elevation: 0,
automaticallyImplyLeading: false,
leading: !isBigScreen && selectedDialog != null
? IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () {
Navigator.pop(context);
},
)
: null,
title: Text(
selectedDialog?.name ?? '',
),
centerTitle: false,
actions: <Widget>[
IconButton(
onPressed: () => showChatDetails(
context, currentUser, selectedDialog!),
icon: const Icon(
Icons.info_outline,
color: Colors.white,
),
),
],
),
),
),
],
),
);
} else if (isBigScreen) {
return Expanded(
child: Container(
margin: EdgeInsets.only(top: AppBar().preferredSize.height),
child: const Center(
child: Text(
'No dialog selected',
style: TextStyle(fontSize: 20),
),
),
),
);
} else {
return Container();
}
}
get isBigScreen => MediaQuery.of(context).size.width >= MIN_SCREEN_SIZE;
}
This diff is collapsed.
import 'package:connectycube_sdk/src/core/users/models/cube_user.dart';
import 'package:flutter/material.dart';
import 'package:vmeeting/srcchat/utils/consts.dart';
import 'new_dialog_screen.dart';
import 'new_group_dialog_screen.dart';
import 'utils/route_utils.dart';
class CreateDialog extends StatelessWidget {
final CubeUser currentUser;
CreateDialog(this.currentUser);
@override
Widget build(BuildContext context) {
return Navigator(
key: Navigation.createDialogNavigation,
initialRoute: 'search_users',
onGenerateRoute: (RouteSettings settings) {
Map<String, dynamic>? args =
settings.arguments as Map<String, dynamic>?;
Widget page;
switch (settings.name) {
case 'search_users':
page = CreateChatScreen(currentUser);
break;
case 'configure_group_dialog':
page = NewGroupDialogScreen(
args![USER_ARG_NAME],
args[DIALOG_ARG_NAME],
args[SELECTED_USERS_ARG_NAME],
);
break;
default:
page = CreateChatScreen(args![USER_ARG_NAME]);
break;
}
return PageRouteBuilder(
pageBuilder: (_, __, ___) => page,
);
});
}
}
This diff is collapsed.
import 'dart:async';
import 'package:connectycube_sdk/connectycube_sdk.dart';
class ChatManager {
static ChatManager? _instance;
ChatManager._();
static ChatManager get instance => _instance ??= ChatManager._();
StreamController<CubeMessage> sentMessagesController =
StreamController.broadcast();
Stream<CubeMessage> get sentMessagesStream {
return sentMessagesController.stream;
}
StreamController<MessageStatus> readMessagesController =
StreamController.broadcast();
Stream<MessageStatus> get readMessagesStream {
return readMessagesController.stream;
}
}
import 'dart:async';
import 'dart:convert';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:universal_io/io.dart';
import 'package:uuid/uuid.dart';
import 'package:connectycube_sdk/connectycube_sdk.dart';
import '../utils/consts.dart';
import '../utils/platform_utils.dart';
import '../utils/pref_util.dart';
class PushNotificationsManager {
static const TAG = "PushNotificationsManager";
static final PushNotificationsManager _instance =
PushNotificationsManager._internal();
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
PushNotificationsManager._internal() {
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
}
BuildContext? applicationContext;
static PushNotificationsManager get instance => _instance;
Future<dynamic> Function(String? payload)? onNotificationClicked;
init() async {
log('[init]', TAG);
FirebaseMessaging firebaseMessaging = FirebaseMessaging.instance;
await firebaseMessaging.requestPermission(
alert: true, badge: true, sound: true);
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('ic_launcher_foreground');
final DarwinInitializationSettings initializationSettingsIOS =
DarwinInitializationSettings(
requestSoundPermission: true,
requestBadgePermission: true,
requestAlertPermission: true,
onDidReceiveLocalNotification: onDidReceiveLocalNotification,
);
final InitializationSettings initializationSettings =
InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
macOS: DarwinInitializationSettings());
await flutterLocalNotificationsPlugin.initialize(
initializationSettings,
onDidReceiveNotificationResponse:
(NotificationResponse notificationResponse) {
log('[onDidReceiveNotificationResponse] payload: ${notificationResponse.payload}',
TAG);
var data = notificationResponse.payload;
if (data != null) {
if (onNotificationClicked != null) {
onNotificationClicked?.call(data);
} else {
String? dialogId = jsonDecode(data)['dialog_id'];
SharedPrefs.instance.saveSelectedDialogId(dialogId ?? '');
}
}
},
onDidReceiveBackgroundNotificationResponse: notificationTapBackground,
);
String? token;
if (Platform.isAndroid || kIsWeb || Platform.isIOS || Platform.isMacOS) {
firebaseMessaging.getToken().then((token) {
log('[getToken] token: $token', TAG);
subscribe(token);
}).catchError((onError) {
log('[getToken] onError: $onError', TAG);
});
}
if (!isEmpty(token)) {
subscribe(token);
}
firebaseMessaging.onTokenRefresh.listen((newToken) {
subscribe(newToken);
});
FirebaseMessaging.onMessage.listen((remoteMessage) {
log('[onMessage] message: ${remoteMessage.data}', TAG);
showNotification(remoteMessage);
});
// TODO test after fix https://github.com/FirebaseExtended/flutterfire/issues/4898
FirebaseMessaging.onMessageOpenedApp.listen((remoteMessage) {
log('[onMessageOpenedApp] remoteMessage: $remoteMessage', TAG);
onNotificationClicked?.call(jsonEncode(remoteMessage.data));
});
}
subscribe(String? token) async {
log('[subscribe] token: $token', PushNotificationsManager.TAG);
SharedPrefs sharedPrefs = await SharedPrefs.instance.init();
if (sharedPrefs.getSubscriptionToken() == token) {
log('[subscribe] skip subscription for same token',
PushNotificationsManager.TAG);
return;
}
CreateSubscriptionParameters parameters = CreateSubscriptionParameters();
parameters.pushToken = token;
bool isProduction = kIsWeb ? true : bool.fromEnvironment('dart.vm.product');
parameters.environment =
isProduction ? CubeEnvironment.PRODUCTION : CubeEnvironment.DEVELOPMENT;
if (Platform.isAndroid || kIsWeb || Platform.isIOS || Platform.isMacOS) {
parameters.channel = NotificationsChannels.GCM;
parameters.platform = CubePlatform.ANDROID;
}
var deviceInfoPlugin = DeviceInfoPlugin();
var deviceId;
if (kIsWeb) {
var webBrowserInfo = await deviceInfoPlugin.webBrowserInfo;
deviceId = base64Encode(utf8.encode(webBrowserInfo.userAgent ?? ''));
} else if (Platform.isAndroid) {
var androidInfo = await deviceInfoPlugin.androidInfo;
deviceId = androidInfo.id;
} else if (Platform.isIOS) {
var iosInfo = await deviceInfoPlugin.iosInfo;
deviceId = iosInfo.identifierForVendor;
} else if (Platform.isMacOS) {
var macOsInfo = await deviceInfoPlugin.macOsInfo;
deviceId = macOsInfo.computerName;
}
parameters.udid = deviceId ?? Uuid().v4;
var packageInfo = await PackageInfo.fromPlatform();
parameters.bundleIdentifier = packageInfo.packageName;
createSubscription(parameters.getRequestParameters())
.then((cubeSubscription) {
log('[subscribe] subscription SUCCESS', PushNotificationsManager.TAG);
sharedPrefs.saveSubscriptionToken(token!);
cubeSubscription.forEach((subscription) {
if (subscription.clientIdentificationSequence == token) {
sharedPrefs.saveSubscriptionId(subscription.id!);
}
});
}).catchError((error) {
log('[subscribe] subscription ERROR: $error',
PushNotificationsManager.TAG);
});
}
Future<void> unsubscribe() {
return SharedPrefs.instance.init().then((sharedPrefs) {
int subscriptionId = sharedPrefs.getSubscriptionId();
if (subscriptionId != 0) {
return deleteSubscription(subscriptionId).then((voidResult) {
FirebaseMessaging.instance.deleteToken();
sharedPrefs.saveSubscriptionId(0);
});
}
return Future.value();
}).catchError((onError) {
log('[unsubscribe] ERROR: $onError', PushNotificationsManager.TAG);
});
}
Future<dynamic> onDidReceiveLocalNotification(
int id, String? title, String? body, String? payload) {
log('[onDidReceiveLocalNotification] id: $id , title: $title, body: $body, payload: $payload',
PushNotificationsManager.TAG);
return Future.value();
}
Future<dynamic> onSelectNotification(String? payload) {
log('[onSelectNotification] payload: $payload',
PushNotificationsManager.TAG);
onNotificationClicked?.call(payload);
return Future.value();
}
}
showNotification(RemoteMessage message) {
log('[showNotification] message: ${message.data}',
PushNotificationsManager.TAG);
Map<String, dynamic> data = message.data;
NotificationDetails buildNotificationDetails(
int? badge,
String threadIdentifier,
) {
final DarwinNotificationDetails darwinNotificationDetails =
DarwinNotificationDetails(
badgeNumber: badge,
threadIdentifier: threadIdentifier,
);
final AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
'messages_channel_id',
'Chat messages',
channelDescription: 'Chat messages will be received here',
importance: Importance.max,
priority: Priority.high,
showWhen: true,
color: Colors.green,
);
return NotificationDetails(
android: androidPlatformChannelSpecifics,
iOS: darwinNotificationDetails,
macOS: darwinNotificationDetails);
}
var badge = int.tryParse(data['badge'].toString());
var threadId = data['ios_thread_id'] ?? data['dialog_id'] ?? 'ios_thread_id';
FlutterLocalNotificationsPlugin().show(
6543,
"Chat sample",
data['message'].toString(),
buildNotificationDetails(badge, threadId),
payload: jsonEncode(data),
);
}
@pragma('vm:entry-point')
Future<void> onBackgroundMessage(RemoteMessage message) async {
log('[onBackgroundMessage] message: ${message.data}',
PushNotificationsManager.TAG);
showNotification(message);
if(!Platform.isIOS) {
updateBadgeCount(int.tryParse(message.data['badge'].toString()));
}
return Future.value();
}
Future<dynamic> onNotificationSelected(String? payload, BuildContext? context) {
log('[onSelectNotification] payload: $payload', PushNotificationsManager.TAG);
if (context == null) return Future.value();
log('[onSelectNotification] context != null', PushNotificationsManager.TAG);
if (payload != null) {
return SharedPrefs.instance.init().then((sharedPrefs) {
CubeUser? user = sharedPrefs.getUser();
Map<String, dynamic> payloadObject = jsonDecode(payload);
String? dialogId = payloadObject['dialog_id'];
log("[onSelectNotification] dialog_id: $dialogId",
PushNotificationsManager.TAG);
getDialogs({'id': dialogId}).then((dialogs) {
if (dialogs?.items != null && dialogs!.items.isNotEmpty) {
CubeDialog dialog = dialogs.items.first;
Navigator.pushNamed(context, 'chat_dialog',
arguments: {USER_ARG_NAME: user, DIALOG_ARG_NAME: dialog});
}
});
});
} else {
return Future.value();
}
}
@pragma('vm:entry-point')
void notificationTapBackground(NotificationResponse notificationResponse) {
log('[notificationTapBackground] payload: ${notificationResponse.payload}');
}
import 'package:flutter/material.dart';
import 'package:connectycube_sdk/connectycube_chat.dart';
import 'package:vmeeting/src/constants/colors_const.dart';
import 'utils/api_utils.dart';
import 'utils/consts.dart';
import 'widgets/common.dart';
class CreateChatScreen extends StatelessWidget {
final CubeUser _cubeUser;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.close, color: ColorConst.appBleckColor),
onPressed: () {
Navigator.of(context, rootNavigator: false).pop();
},
),
automaticallyImplyLeading: false,
title: const Text(
'Search users...',
),
),
body: BodyLayout(_cubeUser),
);
}
CreateChatScreen(this._cubeUser);
}
class BodyLayout extends StatefulWidget {
final CubeUser currentUser;
BodyLayout(this.currentUser);
@override
State<StatefulWidget> createState() {
return _BodyLayoutState(currentUser);
}
}
class _BodyLayoutState extends State<BodyLayout> {
static const String TAG = "_BodyLayoutState";
final CubeUser currentUser;
List<CubeUser> userList = [];
Set<int> _selectedUsers = {};
var _isUsersContinues = false;
var _isPrivateDialog = true;
String? userToSearch;
String userMsg = " ";
_BodyLayoutState(this.currentUser);
_searchUser(value) {
log("searchUser _user= $value");
if (value != null)
setState(() {
userToSearch = value;
_isUsersContinues = true;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.only(left: 16, right: 16, bottom: 16),
child: Column(
children: [
_buildTextFields(),
_buildDialogButton(),
Container(
margin: EdgeInsets.only(left: 8),
child: Visibility(
maintainSize: false,
maintainAnimation: false,
maintainState: false,
visible: _isUsersContinues,
child: CircularProgressIndicator(
strokeWidth: 2,
),
),
),
Expanded(
child: _getUsersList(context),
),
],
)),
floatingActionButton: new Visibility(
visible: !_isPrivateDialog,
child: FloatingActionButton(
heroTag: "New dialog",
child: Icon(
Icons.check,
color: Colors.white,
),
backgroundColor: Colors.blue,
onPressed: () => _createDialog(context, _selectedUsers, true),
),
),
);
}
Widget _buildTextFields() {
return new Container(
child: new Column(
children: <Widget>[
new Container(
child: new TextField(
autofocus: true,
textInputAction: TextInputAction.search,
decoration: new InputDecoration(labelText: 'Search users'),
onSubmitted: (value) {
_searchUser(value.trim());
}),
),
],
),
);
}
Widget _buildDialogButton() {
getIcon() {
if (_isPrivateDialog) {
return Icons.person;
} else {
return Icons.people;
}
}
getDescription() {
if (_isPrivateDialog) {
return "Create group chat";
} else {
return "Create private chat";
}
}
return new Container(
alignment: Alignment.centerLeft,
child: TextButton.icon(
icon: Icon(
getIcon(),
size: 25.0,
color: themeColor,
),
onPressed: () {
setState(() {
_isPrivateDialog = !_isPrivateDialog;
});
},
label: Text(getDescription()),
),
);
}
Widget _getUsersList(BuildContext context) {
clearValues() {
_isUsersContinues = false;
userToSearch = null;
userMsg = " ";
userList.clear();
}
if (_isUsersContinues) {
if (userToSearch != null && userToSearch!.isNotEmpty) {
getUsersByFullName(userToSearch!).then((users) {
log("getusers: $users", TAG);
setState(() {
clearValues();
userList.addAll(users!.items);
});
}).catchError((onError) {
log("getusers catchError: $onError", TAG);
setState(() {
clearValues();
userMsg = "Couldn't find user";
});
});
}
}
if (userList.isEmpty)
return FittedBox(
fit: BoxFit.contain,
child: Text(userMsg),
);
else
return ListView.builder(
itemCount: userList.length,
itemBuilder: _getListItemTile,
);
}
Widget _getListItemTile(BuildContext context, int index) {
getPrivateWidget() {
return Container(
child: TextButton(
child: Row(
children: <Widget>[
getUserAvatarWidget(userList[index], 30),
Flexible(
child: Container(
child: Column(
children: <Widget>[
Container(
child: Text(
'${userList[index].fullName}',
style: TextStyle(color: primaryColor),
),
alignment: Alignment.centerLeft,
margin: EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 5.0),
),
],
),
margin: EdgeInsets.only(left: 20.0),
),
),
Container(
child: Icon(
Icons.arrow_forward,
size: 25.0,
color: themeColor,
),
),
],
),
onPressed: () {
_createDialog(context, {userList[index].id!}, false);
},
),
margin: EdgeInsets.only(bottom: 10.0, left: 5.0, right: 5.0),
);
}
getGroupWidget() {
return Container(
child: TextButton(
child: Row(
children: <Widget>[
getUserAvatarWidget(userList[index], 30),
Flexible(
child: Container(
child: Column(
children: <Widget>[
Container(
child: Text(
'${userList[index].fullName}',
style: TextStyle(color: primaryColor),
),
alignment: Alignment.centerLeft,
margin: EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 5.0),
),
],
),
margin: EdgeInsets.only(left: 20.0),
),
),
Container(
child: Checkbox(
value: _selectedUsers.contains(userList[index].id),
onChanged: ((checked) {
setState(() {
if (checked!) {
_selectedUsers.add(userList[index].id!);
} else {
_selectedUsers.remove(userList[index].id);
}
});
}),
),
),
],
),
onPressed: () {
setState(() {
if (_selectedUsers.contains(userList[index].id)) {
_selectedUsers.remove(userList[index].id);
} else {
_selectedUsers.add(userList[index].id!);
}
});
},
),
margin: EdgeInsets.only(bottom: 10.0, left: 5.0, right: 5.0),
);
}
getItemWidget() {
if (_isPrivateDialog) {
return getPrivateWidget();
} else {
return getGroupWidget();
}
}
return getItemWidget();
}
void _createDialog(BuildContext context, Set<int> users, bool isGroup) async {
log("_createDialog with users= $users");
if (isGroup) {
CubeDialog newDialog = CubeDialog(CubeDialogType.GROUP, occupantsIds: users.toList());
List<CubeUser> usersToAdd = users.map((id) => userList.firstWhere((user) => user.id == id)).toList();
Navigator.of(context).pushNamed('configure_group_dialog', arguments: {
USER_ARG_NAME: currentUser,
DIALOG_ARG_NAME: newDialog,
SELECTED_USERS_ARG_NAME: usersToAdd,
});
} else {
CubeDialog newDialog = CubeDialog(CubeDialogType.PRIVATE, occupantsIds: users.toList());
createDialog(newDialog).then((createdDialog) {
Navigator.of(context, rootNavigator: true).pushNamedAndRemoveUntil('chat_dialog', (route) => false, arguments: {
USER_ARG_NAME: currentUser,
DIALOG_ARG_NAME: createdDialog
});
}).catchError((error) {
_processCreateDialogError(error);
});
}
}
void _processCreateDialogError(exception) {
log("Login error $exception", TAG);
showDialogError(exception, context);
}
@override
void initState() {
super.initState();
log("initState");
}
}
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:connectycube_sdk/connectycube_sdk.dart';
import 'utils/api_utils.dart';
import 'utils/consts.dart';
import 'widgets/common.dart';
class NewGroupDialogScreen extends StatelessWidget {
final CubeUser currentUser;
final CubeDialog _cubeDialog;
final List<CubeUser> users;
NewGroupDialogScreen(this.currentUser, this._cubeDialog, this.users);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'Group configuration...',
),
centerTitle: true,
),
body: NewChatScreen(currentUser, _cubeDialog, users),
resizeToAvoidBottomInset: false);
}
}
class NewChatScreen extends StatefulWidget {
static const String TAG = "_CreateChatScreenState";
final CubeUser currentUser;
final CubeDialog _cubeDialog;
final List<CubeUser?> users;
NewChatScreen(this.currentUser, this._cubeDialog, this.users);
@override
State createState() => NewChatScreenState(currentUser, _cubeDialog, users);
}
class NewChatScreenState extends State<NewChatScreen> {
static const String TAG = "NewChatScreenState";
final CubeUser currentUser;
final CubeDialog _cubeDialog;
final List<CubeUser?> users;
final TextEditingController _nameFilter = new TextEditingController();
NewChatScreenState(this.currentUser, this._cubeDialog, this.users);
@override
void initState() {
super.initState();
_nameFilter.addListener(_nameListener);
}
void _nameListener() {
if (_nameFilter.text.length > 4) {
log("_createDialogImage text= ${_nameFilter.text.trim()}");
_cubeDialog.name = _nameFilter.text.trim();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.all(16),
child: Column(
children: [
_buildGroupFields(),
_buildDialogOccupants(),
],
)),
floatingActionButton: FloatingActionButton(
heroTag: "New dialog",
child: Icon(
Icons.check,
color: Colors.white,
),
backgroundColor: Colors.blue,
onPressed: () => _createDialog(),
),
resizeToAvoidBottomInset: false);
}
_buildGroupFields() {
getIcon() {
return getDialogAvatarWidget(_cubeDialog, 45,
placeholder: Icon(
Icons.add_a_photo,
size: 45.0,
color: blueColor,
));
}
return Column(
children: <Widget>[
Row(
children: <Widget>[
RawMaterialButton(
onPressed: () => _createDialogImage(),
elevation: 2.0,
fillColor: Colors.white,
child: getIcon(),
padding: EdgeInsets.all(0),
shape: CircleBorder(),
),
SizedBox(
width: 16,
),
Flexible(
child: TextField(
autofocus: true,
controller: _nameFilter,
decoration: InputDecoration(labelText: 'Group Name...'),
),
)
],
),
Container(
child: Text(
'Please provide a group name and an optional group icon',
style: TextStyle(color: primaryColor),
),
alignment: Alignment.centerLeft,
margin: EdgeInsets.all(16.0),
),
],
);
}
_createDialogImage() async {
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.image,
);
if (result == null) return;
var uploadImageFuture = getUploadingImageFuture(result);
uploadImageFuture.then((cubeFile) {
var url = cubeFile.getPublicUrl();
log("_createDialogImage url= $url");
setState(() {
_cubeDialog.photo = url;
});
}).catchError((exception) {
_processDialogError(exception);
});
}
_buildDialogOccupants() {
_getListItemTile(BuildContext context, int index) {
return Container(
child: Column(
children: <Widget>[
getUserAvatarWidget(users[index]!, 25),
Container(
child: Column(
children: <Widget>[
Container(
child: Text(
users[index]!.fullName ??
users[index]!.login ??
users[index]!.email ??
'???',
style: TextStyle(color: primaryColor),
),
width: MediaQuery.of(context).size.width / 4,
alignment: Alignment.center,
margin: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 5.0),
),
],
),
margin: EdgeInsets.fromLTRB(0.0, 10.0, 0.0, 10.0),
),
],
),
);
}
_getOccupants() {
return ListView.builder(
shrinkWrap: true,
padding: EdgeInsets.symmetric(vertical: 20.0),
scrollDirection: Axis.horizontal,
itemCount: _cubeDialog.occupantsIds!.length,
itemBuilder: _getListItemTile,
);
}
return Container(
child: Expanded(
child: _getOccupants(),
),
);
}
void _processDialogError(exception) {
log("error $exception", TAG);
showDialogError(exception, context);
}
_createDialog() {
log("_createDialog _cubeDialog= $_cubeDialog");
if (_cubeDialog.name == null || _cubeDialog.name!.length < 5) {
showDialogMsg("Enter more than 4 character", context);
} else {
createDialog(_cubeDialog).then((createdDialog) {
Navigator.of(context, rootNavigator: true).pushNamedAndRemoveUntil(
'chat_dialog', (route) => false, arguments: {
USER_ARG_NAME: currentUser,
DIALOG_ARG_NAME: createdDialog
});
}).catchError((exception) {
_processDialogError(exception);
});
}
}
}
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';
import 'package:universal_io/io.dart';
import 'package:connectycube_sdk/connectycube_sdk.dart';
import 'utils/consts.dart';
import 'utils/pref_util.dart';
import 'utils/route_utils.dart';
const String PHONE_INPUT_ROUTE_NAME = 'PhoneInputScreen';
const String SMS_CODE_INPUT_ROUTE_NAME = 'SMSCodeInputScreen';
class VerifyPhoneNumber extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Navigator(
observers: [PhoneAuthRouteObserver(context)],
key: Navigation.verifyPhoneNavigation,
onGenerateRoute: (RouteSettings settings) {
return PageRouteBuilder(
reverseTransitionDuration:
Duration(milliseconds: Platform.isIOS ? 1000 : 300),
transitionsBuilder: (context, animation, secondaryAnimation, child) {
const begin = Offset(0.0, 1.0);
const end = Offset.zero;
const curve = Curves.ease;
var tween =
Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
return SlideTransition(
position: animation.drive(tween),
child: child,
);
},
settings: RouteSettings(name: PHONE_INPUT_ROUTE_NAME),
pageBuilder: (context, animation, secondaryAnimation) =>
PhoneInputScreen(
actions: [
SMSCodeRequestedAction((ctx1, action, flowKey, phoneNumber) {
Navigator.of(context).push(
MaterialPageRoute(
settings: RouteSettings(name: SMS_CODE_INPUT_ROUTE_NAME),
builder: (ctx2) => SMSCodeInputScreen(
flowKey: flowKey,
actions: [
AuthStateChangeAction<SignedIn>((ctx3, state) {
log('[AuthStateChangeAction] SignedIn');
state.user?.getIdToken().then((idToken) {
SharedPrefs.instance.saveLoginType(LoginType.phone);
Navigator.of(ctx3, rootNavigator: true)
.pushNamedAndRemoveUntil(
'login', (route) => false);
});
}),
AuthStateChangeAction<CredentialLinked>((ctx3, state) {
log('[AuthStateChangeAction] CredentialLinked');
state.user.getIdToken().then((idToken) {
SharedPrefs.instance.saveLoginType(LoginType.phone);
Navigator.of(ctx3, rootNavigator: true)
.pushNamedAndRemoveUntil(
'login', (route) => false);
});
}),
AuthStateChangeAction<Uninitialized>((ctx3, state) {
log('[AuthStateChangeAction] Uninitialized');
}),
AuthStateChangeAction<CredentialReceived>(
(ctx3, state) {
log('[AuthStateChangeAction] CredentialReceived');
}),
AuthStateChangeAction<AuthFailed>((ctx3, state) {
log('[AuthStateChangeAction] AuthFailed');
}),
AuthStateChangeAction<UserCreated>((ctx3, state) {
log('[AuthStateChangeAction] UserCreated');
state.credential.user?.getIdToken().then((idToken) {
SharedPrefs.instance.saveLoginType(LoginType.phone);
Navigator.of(ctx3, rootNavigator: true)
.pushNamedAndRemoveUntil(
'login', (route) => false);
});
}),
],
),
),
);
}),
],
),
);
},
);
}
}
class PhoneAuthRouteObserver extends RouteObserver {
final BuildContext context;
PhoneAuthRouteObserver(this.context);
@override
void didPop(Route route, Route? previousRoute) {
super.didPop(route, previousRoute);
if (route.settings.name == PHONE_INPUT_ROUTE_NAME) {
Navigator.of(context, rootNavigator: true).pop();
}
}
}
This diff is collapsed.
This diff is collapsed.
import 'package:flutter/material.dart';
import 'package:connectycube_sdk/connectycube_sdk.dart';
import 'add_occupant_screen.dart';
import 'chat_details_screen.dart';
import 'utils/consts.dart';
import 'utils/route_utils.dart';
class UpdateDialog extends StatelessWidget {
final CubeUser currentUser;
final CubeDialog currentDialog;
UpdateDialog(this.currentUser, this.currentDialog);
@override
Widget build(BuildContext context) {
return Navigator(
key: Navigation.updateDialogNavigation,
initialRoute: 'dialog_info',
onGenerateRoute: (RouteSettings settings) {
Map<String, dynamic>? args =
settings.arguments as Map<String, dynamic>?;
MaterialPageRoute pageRout;
switch (settings.name) {
case 'dialog_info':
pageRout = MaterialPageRoute(
builder: (context) =>
ChatDetailsScreen(currentUser, currentDialog));
break;
case 'search_users':
pageRout = MaterialPageRoute<List<int>?>(
builder: (context) => AddOccupantScreen(
args![USER_ARG_NAME],
));
break;
default:
pageRout = MaterialPageRoute(
builder: (context) => ChatDetailsScreen(
args![USER_ARG_NAME],
args[DIALOG_ARG_NAME],
));
break;
}
return pageRout;
},
);
}
}
import 'dart:async';
import 'dart:collection';
import 'package:connectycube_sdk/connectycube_chat.dart';
import 'package:connectycube_sdk/connectycube_sdk.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:universal_io/io.dart';
import '../managers/push_notifications_manager.dart';
import 'platform_utils.dart';
void showDialogError(exception, context) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Error"),
content: Text("Something went wrong $exception"),
actions: <Widget>[
TextButton(
child: Text("OK"),
onPressed: () => Navigator.of(context).pop(),
)
],
);
});
}
void showDialogMsg(msg, context) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text("Alert"),
content: Text(msg),
actions: <Widget>[
TextButton(
child: Text("OK"),
onPressed: () => Navigator.of(context).pop(),
)
],
);
});
}
class ListItem<T> {
bool isSelected = false; //Selection property to highlight or not
T data; //Data of the user
ListItem(this.data); //Constructor to assign the data
}
Future<Map<int, CubeUser>> getUsersByIds(Set<int> ids) async {
Completer<Map<int, CubeUser>> completer = Completer();
Map<int, CubeUser> users = HashMap();
try {
var result = await (getAllUsersByIds(ids)
as FutureOr<PagedResult<CubeUser>>);
users.addAll(Map.fromIterable(result.items,
key: (item) => item.id, value: (item) => item));
} catch (ex) {
log("exception= $ex");
}
completer.complete(users);
return completer.future;
}
Future<CubeFile> getUploadingImageFuture(FilePickerResult result) async {
// there possible to upload the file as an array of bytes, but here showed two ways just as an example
if(kIsWeb){
return uploadRawFile(result.files.single.bytes!, result.files.single.name, isPublic: true, onProgress: (progress) {
log("uploadImageFile progress= $progress");
});
} else {
return uploadFile(File(result.files.single.path!), isPublic: true, onProgress: (progress) {
log("uploadImageFile progress= $progress");
});
}
}
refreshBadgeCount(){
getUnreadMessagesCount().then((result) {
updateBadgeCount(result['total']);
});
}
\ No newline at end of file
import 'package:firebase_auth/firebase_auth.dart';
import 'package:connectycube_sdk/connectycube_sdk.dart';
import '../../firebase_options.dart';
Future<CubeSession> createPhoneAuthSession() async {
var phoneAuthIdToken = await FirebaseAuth.instance.currentUser?.getIdToken();
if (phoneAuthIdToken == null) {
return createSession();
}
return createSession().then((cubeSession) {
return signInUsingFirebase(
DefaultFirebaseOptions.currentPlatform.projectId,
phoneAuthIdToken,
).then((_) {
return CubeSessionManager.instance.activeSession!;
});
});
}
const String APP_ID = "476";
const String AUTH_KEY = "PDZjPBzAO8WPfCp";
const String AUTH_SECRET = "6247kjxXCLRaua6";
import 'dart:ui';
final themeColor = Color(0xfff5a623);
final primaryColor = Color(0xff203152);
final greyColor = Color(0xffaeaeae);
final greyColor2 = Color(0xffE8E8E8);
final greyColor3 = Color(0xffeaeaea);
final blueColor = Color(0xff0080ff);
final String SORT_ASC = "asc";
final String SORT_DESC = "desc";
final String USER_ARG_NAME = "user";
final String DIALOG_ARG_NAME = "dialog";
final String SELECTED_USERS_ARG_NAME = "selected_users";
enum LoginType { login, email, phone, facebook }
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_app_badger/flutter_app_badger.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
import 'package:universal_io/io.dart';
bool isDesktop() {
return Platform.isLinux || Platform.isMacOS || Platform.isWindows;
}
void showModal({
required BuildContext context,
required Widget child,
double maxWidth = 600,
}) {
if (isDesktop()) {
showDialog(
context: context,
builder: (BuildContext cxt) {
return Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(8.0))),
child: RawKeyboardListener(
focusNode: FocusNode(onKey: (FocusNode node, RawKeyEvent evt) {
if (evt.logicalKey == LogicalKeyboardKey.escape) {
if (evt is RawKeyDownEvent) {
Navigator.pop(context);
return KeyEventResult.handled;
}
}
return KeyEventResult.ignored;
}),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: maxWidth),
child: child),
),
),
);
});
} else if (Platform.isAndroid) {
showMaterialModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
enableDrag: true,
expand: true,
builder: (context) {
return child;
});
} else if (Platform.isIOS) {
showCupertinoModalBottomSheet(
context: context,
backgroundColor: Colors.transparent,
enableDrag: true,
expand: true,
builder: (context) {
return child;
});
}
}
void updateBadgeCount(int? count) {
FlutterAppBadger.isAppBadgeSupported().then((isBadgesSupported) {
if (isBadgesSupported) {
if (count == null || count == 0) {
FlutterAppBadger.removeBadge();
} else {
FlutterAppBadger.updateBadgeCount(count);
}
}
});
}
bool get isPhoneAuthSupported => kIsWeb || !isDesktop();
This diff is collapsed.
import 'package:flutter/material.dart';
class Navigation {
static GlobalKey<NavigatorState> mainNavigation = GlobalKey();
static GlobalKey<NavigatorState> createDialogNavigation = GlobalKey();
static GlobalKey<NavigatorState> updateDialogNavigation = GlobalKey();
static GlobalKey<NavigatorState> verifyPhoneNavigation = GlobalKey();
}
This diff is collapsed.
This diff is collapsed.
import '../utils/consts.dart';
import 'package:flutter/material.dart';
class Loading extends StatelessWidget {
const Loading();
@override
Widget build(BuildContext context) {
return Container(
child: Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(themeColor),
),
),
color: Colors.white.withOpacity(0.8),
);
}
}
This diff is collapsed.
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
desktop_webview_auth
emoji_picker_flutter
file_selector_linux file_selector_linux
flutter_webrtc flutter_webrtc
url_launcher_linux url_launcher_linux
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment