Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
vmeeting
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Inomjon
vmeeting
Commits
5bd5df70
Commit
5bd5df70
authored
Nov 28, 2023
by
Inomjon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Chat ishlatib ko'rildi
parent
cb19bef2
Changes
36
Show whitespace changes
Inline
Side-by-side
Showing
36 changed files
with
5670 additions
and
6 deletions
+5670
-6
app_routes.dart
lib/service/routes/app_routes.dart
+4
-0
routes_name.dart
lib/service/routes/routes_name.dart
+1
-0
chat_manager.dart
lib/src/managers/chat_manager.dart
+25
-0
app_utils.dart
lib/src/utils/app_utils.dart
+5
-0
add_occupant_screen.dart
lib/srcchat/add_occupant_screen.dart
+246
-0
chat_details_screen.dart
lib/srcchat/chat_details_screen.dart
+605
-0
chat_dialog_resizable_screen.dart
lib/srcchat/chat_dialog_resizable_screen.dart
+157
-0
chat_dialog_screen.dart
lib/srcchat/chat_dialog_screen.dart
+1211
-0
create_dialog_flow.dart
lib/srcchat/create_dialog_flow.dart
+45
-0
login_screen.dart
lib/srcchat/login_screen.dart
+544
-0
chat_manager.dart
lib/srcchat/managers/chat_manager.dart
+25
-0
push_notifications_manager.dart
lib/srcchat/managers/push_notifications_manager.dart
+287
-0
new_dialog_screen.dart
lib/srcchat/new_dialog_screen.dart
+337
-0
new_group_dialog_screen.dart
lib/srcchat/new_group_dialog_screen.dart
+222
-0
phone_auth_flow.dart
lib/srcchat/phone_auth_flow.dart
+111
-0
select_dialog_screen.dart
lib/srcchat/select_dialog_screen.dart
+461
-0
settings_screen.dart
lib/srcchat/settings_screen.dart
+384
-0
update_dialog_flow.dart
lib/srcchat/update_dialog_flow.dart
+52
-0
api_utils.dart
lib/srcchat/utils/api_utils.dart
+87
-0
auth_utils.dart
lib/srcchat/utils/auth_utils.dart
+21
-0
configs.dart
lib/srcchat/utils/configs.dart
+3
-0
consts.dart
lib/srcchat/utils/consts.dart
+17
-0
platform_utils.dart
lib/srcchat/utils/platform_utils.dart
+77
-0
pref_util.dart
lib/srcchat/utils/pref_util.dart
+128
-0
route_utils.dart
lib/srcchat/utils/route_utils.dart
+8
-0
common.dart
lib/srcchat/widgets/common.dart
+118
-0
full_photo.dart
lib/srcchat/widgets/full_photo.dart
+47
-0
loading.dart
lib/srcchat/widgets/loading.dart
+18
-0
main_page.dart
lib/views/main_view/main_page.dart
+4
-2
generated_plugin_registrant.cc
linux/flutter/generated_plugin_registrant.cc
+8
-0
generated_plugins.cmake
linux/flutter/generated_plugins.cmake
+2
-0
GeneratedPluginRegistrant.swift
macos/Flutter/GeneratedPluginRegistrant.swift
+16
-0
pubspec.lock
pubspec.lock
+361
-4
pubspec.yaml
pubspec.yaml
+17
-0
generated_plugin_registrant.cc
windows/flutter/generated_plugin_registrant.cc
+12
-0
generated_plugins.cmake
windows/flutter/generated_plugins.cmake
+4
-0
No files found.
lib/service/routes/app_routes.dart
View file @
5bd5df70
...
...
@@ -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/user_sign_up_bloc/user_sign_up_bloc.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
'../../blocs/user_login_bloc/user_log_in_bloc.dart'
;
import
'../../src/controllers/enter_number_cont.dart'
;
...
...
@@ -50,6 +51,9 @@ class MainNavigator extends StatelessWidget {
case
MainRoutes
.
reset_password_page
:
builder
=
(
BuildContext
_
)
=>
ResetPasswordPage
(
controller:
controller
);
break
;
// case MainRoutes.chat_dialog:
// builder = (BuildContext _) => ChatDialogScreen(args![USER_ARG_NAME], args[DIALOG_ARG_NAME]);
// break;
case
MainRoutes
.
user_signup_page
:
builder
=
(
BuildContext
_
)
=>
MultiBlocProvider
(
providers:
[
...
...
lib/service/routes/routes_name.dart
View file @
5bd5df70
...
...
@@ -8,4 +8,5 @@ class MainRoutes {
static
const
String
old_sign_in
=
"old_sign_in"
;
static
const
String
edit_profile_page
=
"edit_profile_page"
;
static
const
String
reset_password_page
=
"reset_password_page"
;
static
const
String
chat_dialog
=
"chat_dialog"
;
}
\ No newline at end of file
lib/src/managers/chat_manager.dart
0 → 100644
View file @
5bd5df70
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
;
}
}
lib/src/utils/app_utils.dart
View file @
5bd5df70
...
...
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import
'package:google_fonts/google_fonts.dart'
;
import
'package:vmeeting/src/constants/colors_const.dart'
;
import
'../../app_models/app_users_model/app_users_model.dart'
;
import
'../widgets/custom_loading/customloading.dart'
;
class
AppUtils
{
static
Widget
buttonLoader
=
SizedBox
(
...
...
@@ -36,5 +37,9 @@ class AppUtils {
}
static
UserModel
userModel
=
UserModel
();
static
CubeUser
cubeUser
=
CubeUser
();
static
Widget
buildLoading
()
{
return
Center
(
child:
CustomLoading
());
}
}
lib/srcchat/add_occupant_screen.dart
0 → 100644
View file @
5bd5df70
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
);
}
}
lib/srcchat/chat_details_screen.dart
0 → 100644
View file @
5bd5df70
import
'dart:async'
;
import
'package:file_picker/file_picker.dart'
;
import
'package:flutter/material.dart'
;
import
'package:fluttertoast/fluttertoast.dart'
;
import
'package:connectycube_sdk/connectycube_sdk.dart'
;
import
'utils/api_utils.dart'
;
import
'utils/consts.dart'
;
import
'widgets/common.dart'
;
class
ChatDetailsScreen
extends
StatelessWidget
{
final
CubeUser
_cubeUser
;
final
CubeDialog
_cubeDialog
;
ChatDetailsScreen
(
this
.
_cubeUser
,
this
.
_cubeDialog
);
@override
Widget
build
(
BuildContext
context
)
{
return
WillPopScope
(
onWillPop:
()
=>
_onBackPressed
(
context
),
child:
Scaffold
(
appBar:
AppBar
(
leading:
IconButton
(
icon:
Icon
(
Icons
.
close
,
color:
Colors
.
white
),
onPressed:
()
{
Navigator
.
of
(
context
,
rootNavigator:
true
).
pop
();
},
),
automaticallyImplyLeading:
false
,
title:
Text
(
_cubeDialog
.
type
==
CubeDialogType
.
PRIVATE
?
"Contact details"
:
"Group details"
,
),
centerTitle:
false
,
actions:
<
Widget
>[
if
(
_cubeDialog
.
type
!=
CubeDialogType
.
PRIVATE
)
IconButton
(
onPressed:
()
{
_exitDialog
(
context
);
},
icon:
Icon
(
Icons
.
exit_to_app
,
),
)
],
),
body:
DetailScreen
(
_cubeUser
,
_cubeDialog
),
),
);
}
Future
<
bool
>
_onBackPressed
(
BuildContext
context
)
{
Navigator
.
pop
(
context
);
return
Future
.
value
(
false
);
}
_exitDialog
(
BuildContext
context
)
{
print
(
'_exitDialog'
);
showDialog
(
context:
context
,
builder:
(
BuildContext
context
)
{
return
AlertDialog
(
title:
Text
(
'Leave Dialog'
),
content:
Text
(
"Are you sure you want to leave this dialog?"
),
actions:
<
Widget
>[
TextButton
(
child:
Text
(
'Cancel'
),
onPressed:
()
{
Navigator
.
pop
(
context
);
},
),
TextButton
(
child:
Text
(
'Ok'
),
onPressed:
()
{
deleteDialog
(
_cubeDialog
.
dialogId
!).
then
((
onValue
)
{
Fluttertoast
.
showToast
(
msg:
'Success'
);
Navigator
.
of
(
context
,
rootNavigator:
true
)
.
pushNamedAndRemoveUntil
(
'select_dialog'
,
(
route
)
=>
false
,
arguments:
{
USER_ARG_NAME:
_cubeUser
},
);
}).
catchError
((
error
)
{
showDialogError
(
error
,
context
);
});
},
),
],
);
},
);
}
}
class
DetailScreen
extends
StatefulWidget
{
static
const
String
TAG
=
"DetailScreen"
;
final
CubeUser
_cubeUser
;
final
CubeDialog
_cubeDialog
;
DetailScreen
(
this
.
_cubeUser
,
this
.
_cubeDialog
);
@override
State
createState
()
=>
_cubeDialog
.
type
==
CubeDialogType
.
PRIVATE
?
ContactScreenState
(
_cubeUser
,
_cubeDialog
)
:
GroupScreenState
(
_cubeUser
,
_cubeDialog
);
}
abstract
class
ScreenState
extends
State
<
DetailScreen
>
{
final
CubeUser
_cubeUser
;
CubeDialog
_cubeDialog
;
final
Map
<
int
,
CubeUser
>
_occupants
=
Map
();
var
_isProgressContinues
=
false
;
ScreenState
(
this
.
_cubeUser
,
this
.
_cubeDialog
);
@override
void
initState
()
{
super
.
initState
();
if
(
_occupants
.
isEmpty
)
{
initUsers
();
}
}
initUsers
()
async
{
_isProgressContinues
=
true
;
if
(
_cubeDialog
.
occupantsIds
==
null
||
_cubeDialog
.
occupantsIds
!.
isEmpty
)
{
setState
(()
{
_isProgressContinues
=
false
;
});
return
;
}
var
result
=
await
getUsersByIds
(
_cubeDialog
.
occupantsIds
!.
toSet
());
_occupants
.
clear
();
_occupants
.
addAll
(
result
);
_occupants
.
remove
(
_cubeUser
.
id
);
setState
(()
{
_isProgressContinues
=
false
;
});
}
}
class
ContactScreenState
extends
ScreenState
{
CubeUser
?
contactUser
;
initUser
()
{
contactUser
=
_occupants
.
values
.
isNotEmpty
?
_occupants
.
values
.
first
:
CubeUser
(
fullName:
"Absent"
);
}
ContactScreenState
(
_cubeUser
,
_cubeDialog
)
:
super
(
_cubeUser
,
_cubeDialog
);
@override
Widget
build
(
BuildContext
context
)
{
initUser
();
return
Scaffold
(
body:
Container
(
alignment:
Alignment
.
center
,
padding:
EdgeInsets
.
all
(
60
),
child:
Column
(
children:
[
_buildAvatarFields
(),
_buildTextFields
(),
_buildButtons
(),
Container
(
margin:
EdgeInsets
.
only
(
left:
8
),
child:
Visibility
(
maintainSize:
false
,
maintainAnimation:
false
,
maintainState:
false
,
visible:
_isProgressContinues
,
child:
CircularProgressIndicator
(
strokeWidth:
2
,
),
),
),
],
)),
);
}
Widget
_buildAvatarFields
()
{
if
(
_isProgressContinues
)
{
return
SizedBox
.
shrink
();
}
return
Stack
(
children:
<
Widget
>[
getUserAvatarWidget
(
contactUser
!,
50
)],
);
}
Widget
_buildTextFields
()
{
if
(
_isProgressContinues
)
{
return
SizedBox
.
shrink
();
}
return
Container
(
margin:
EdgeInsets
.
all
(
50
),
child:
Column
(
children:
<
Widget
>[
Container
(
padding:
EdgeInsets
.
only
(
right:
10
,
left:
10
,
bottom:
3
,
// space between underline and text
),
decoration:
BoxDecoration
(
border:
Border
(
bottom:
BorderSide
(
color:
primaryColor
,
// Text colour here
width:
1.0
,
// Underline width
))),
child:
Text
(
contactUser
!.
fullName
??
contactUser
!.
login
??
contactUser
!.
email
??
''
,
style:
TextStyle
(
color:
primaryColor
,
fontSize:
20
,
// Text colour here
),
),
)
],
),
);
}
Widget
_buildButtons
()
{
if
(
_isProgressContinues
)
{
return
SizedBox
.
shrink
();
}
return
new
Container
(
child:
new
Column
(
children:
<
Widget
>[
new
ElevatedButton
(
child:
Text
(
'Start dialog'
,
style:
TextStyle
(
color:
Colors
.
white
,
),
),
onPressed:
()
=>
Navigator
.
of
(
context
,
rootNavigator:
true
).
pop
(),
),
],
),
);
}
}
class
GroupScreenState
extends
ScreenState
{
final
TextEditingController
_nameFilter
=
new
TextEditingController
();
String
?
_photoUrl
=
""
;
String
_name
=
""
;
Set
<
int
?>
_usersToRemove
=
{};
List
<
int
>?
_usersToAdd
;
GroupScreenState
(
_cubeUser
,
_cubeDialog
)
:
super
(
_cubeUser
,
_cubeDialog
)
{
_nameFilter
.
addListener
(
_nameListen
);
_nameFilter
.
text
=
_cubeDialog
.
name
;
clearFields
();
}
void
_nameListen
()
{
if
(
_nameFilter
.
text
.
isEmpty
)
{
_name
=
""
;
}
else
{
_name
=
_nameFilter
.
text
.
trim
();
}
}
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
body:
SingleChildScrollView
(
child:
Center
(
child:
ConstrainedBox
(
constraints:
BoxConstraints
(
maxWidth:
400
),
child:
Container
(
alignment:
Alignment
.
center
,
padding:
EdgeInsets
.
all
(
40
),
child:
Column
(
children:
[
_buildPhotoFields
(),
_buildTextFields
(),
_buildGroupFields
(),
Container
(
margin:
EdgeInsets
.
only
(
left:
8
),
child:
Visibility
(
maintainSize:
false
,
maintainAnimation:
false
,
maintainState:
false
,
visible:
_isProgressContinues
,
child:
CircularProgressIndicator
(
strokeWidth:
2
,
),
),
),
],
)),
),
),
),
floatingActionButton:
FloatingActionButton
(
heroTag:
"Update dialog"
,
child:
Icon
(
Icons
.
check
,
color:
Colors
.
white
,
),
backgroundColor:
Colors
.
blue
,
onPressed:
()
=>
_updateDialog
(),
),
);
}
Widget
_buildPhotoFields
()
{
if
(
_isProgressContinues
)
{
return
SizedBox
.
shrink
();
}
Widget
avatarCircle
=
getDialogAvatarWidget
(
_cubeDialog
,
50
);
return
new
Stack
(
children:
<
Widget
>[
InkWell
(
splashColor:
greyColor2
,
borderRadius:
BorderRadius
.
circular
(
45
),
onTap:
()
=>
_chooseUserImage
(),
child:
avatarCircle
,
),
new
Positioned
(
child:
RawMaterialButton
(
onPressed:
()
{
_chooseUserImage
();
},
elevation:
2.0
,
fillColor:
Colors
.
white
,
child:
Icon
(
Icons
.
mode_edit
,
size:
20.0
,
),
padding:
EdgeInsets
.
all
(
5.0
),
shape:
CircleBorder
(),
),
top:
55.0
,
right:
35.0
,
),
],
);
}
_chooseUserImage
()
async
{
FilePickerResult
?
result
=
await
FilePicker
.
platform
.
pickFiles
(
type:
FileType
.
image
,
);
if
(
result
==
null
)
return
;
var
uploadImageFuture
=
getUploadingImageFuture
(
result
);
uploadImageFuture
.
then
((
cubeFile
)
{
_photoUrl
=
cubeFile
.
getPublicUrl
();
setState
(()
{
_cubeDialog
.
photo
=
_photoUrl
;
});
}).
catchError
((
error
)
{
_processUpdateError
(
error
);
});
}
Widget
_buildTextFields
()
{
if
(
_isProgressContinues
)
{
return
SizedBox
.
shrink
();
}
return
Container
(
padding:
EdgeInsets
.
only
(
bottom:
20
),
child:
Column
(
children:
<
Widget
>[
Container
(
child:
TextField
(
autofocus:
true
,
style:
TextStyle
(
color:
primaryColor
,
fontSize:
20.0
),
controller:
_nameFilter
,
decoration:
InputDecoration
(
labelText:
'Change group name'
),
),
),
],
),
);
}
_buildGroupFields
()
{
if
(
_isProgressContinues
)
{
return
SizedBox
.
shrink
();
}
return
Column
(
children:
<
Widget
>[
_addMemberBtn
(),
_getUsersList
(),
],
);
}
Widget
_addMemberBtn
()
{
return
Container
(
padding:
EdgeInsets
.
only
(
bottom:
3
,
// space between underline and text
),
decoration:
BoxDecoration
(
border:
Border
(
bottom:
BorderSide
(
color:
Colors
.
green
,
// Text colour here
width:
2.0
,
// Underline width
))),
child:
Row
(
mainAxisSize:
MainAxisSize
.
max
,
children:
<
Widget
>[
Text
(
'Members:'
,
style:
TextStyle
(
color:
primaryColor
,
fontSize:
18
,
// Text colour here
),
),
Expanded
(
flex:
1
,
child:
Container
()),
IconButton
(
onPressed:
()
{
_addOpponent
();
},
icon:
Icon
(
Icons
.
person_add
,
size:
26.0
,
color:
Colors
.
green
,
),
),
Visibility
(
visible:
_usersToRemove
.
isNotEmpty
,
child:
IconButton
(
onPressed:
()
{
_removeOpponent
();
},
icon:
Icon
(
Icons
.
person_remove
,
size:
26.0
,
color:
Colors
.
red
,
),
),
)
],
),
);
}
Widget
_getUsersList
()
{
if
(
_isProgressContinues
)
{
return
SizedBox
.
shrink
();
}
return
ListView
.
separated
(
padding:
EdgeInsets
.
only
(
top:
8
),
scrollDirection:
Axis
.
vertical
,
shrinkWrap:
true
,
primary:
false
,
itemCount:
_occupants
.
length
,
itemBuilder:
_getListItemTile
,
separatorBuilder:
(
context
,
index
)
{
return
Divider
(
thickness:
2
,
indent:
20
,
endIndent:
20
);
},
);
}
Widget
_getListItemTile
(
BuildContext
context
,
int
index
)
{
final
user
=
_occupants
.
values
.
elementAt
(
index
);
Widget
getUserAvatar
()
{
if
(
user
.
avatar
!=
null
&&
user
.
avatar
!.
isNotEmpty
)
{
return
getUserAvatarWidget
(
user
,
25
);
}
else
{
return
Material
(
child:
Icon
(
Icons
.
account_circle
,
size:
50.0
,
color:
greyColor
,
),
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
25.0
)),
clipBehavior:
Clip
.
hardEdge
,
);
}
}
return
Container
(
child:
TextButton
(
child:
Row
(
children:
<
Widget
>[
getUserAvatar
(),
Flexible
(
child:
Container
(
child:
Column
(
children:
<
Widget
>[
Container
(
child:
Text
(
'
${user.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:
_usersToRemove
.
contains
(
_occupants
.
values
.
elementAt
(
index
).
id
),
onChanged:
((
checked
)
{
setState
(()
{
if
(
checked
!)
{
_usersToRemove
.
add
(
_occupants
.
values
.
elementAt
(
index
).
id
);
}
else
{
_usersToRemove
.
remove
(
_occupants
.
values
.
elementAt
(
index
).
id
);
}
});
}),
),
),
],
),
onPressed:
()
{
log
(
"user onPressed"
);
},
),
margin:
EdgeInsets
.
only
(
bottom:
10.0
),
);
}
void
_processUpdateError
(
exception
)
{
log
(
"_processUpdateUserError error
$exception
"
);
setState
(()
{
clearFields
();
_isProgressContinues
=
false
;
});
showDialogError
(
exception
,
context
);
}
_addOpponent
()
async
{
print
(
'_addOpponent'
);
_usersToAdd
=
await
Navigator
.
pushNamed
(
context
,
'search_users'
,
arguments:
{
USER_ARG_NAME:
_cubeUser
,
},
);
if
(
_usersToAdd
!=
null
&&
_usersToAdd
!.
isNotEmpty
)
_updateDialog
();
}
_removeOpponent
()
async
{
print
(
'_removeOpponent'
);
if
(
_usersToRemove
.
isNotEmpty
)
_updateDialog
();
}
void
_updateDialog
()
{
print
(
'_updateDialog
$_name
'
);
if
(
_name
.
isEmpty
&&
_photoUrl
!.
isEmpty
&&
(
_usersToAdd
?.
isEmpty
??
true
)
&&
(
_usersToRemove
.
isEmpty
))
{
Fluttertoast
.
showToast
(
msg:
'Nothing to save'
);
return
;
}
Map
<
String
,
dynamic
>
params
=
{};
if
(
_name
.
isNotEmpty
)
params
[
'name'
]
=
_name
;
if
(
_photoUrl
!.
isNotEmpty
)
params
[
'photo'
]
=
_photoUrl
;
if
(
_usersToAdd
?.
isNotEmpty
??
false
)
params
[
'push_all'
]
=
{
'occupants_ids'
:
List
.
of
(
_usersToAdd
!)};
if
(
_usersToRemove
.
isNotEmpty
)
params
[
'pull_all'
]
=
{
'occupants_ids'
:
List
.
of
(
_usersToRemove
)};
setState
(()
{
_isProgressContinues
=
true
;
});
updateDialog
(
_cubeDialog
.
dialogId
!,
params
).
then
((
dialog
)
{
_cubeDialog
=
dialog
;
Fluttertoast
.
showToast
(
msg:
'Success'
);
setState
(()
{
if
((
_usersToAdd
?.
isNotEmpty
??
false
)
||
(
_usersToRemove
.
isNotEmpty
))
initUsers
();
_isProgressContinues
=
false
;
clearFields
();
});
}).
catchError
((
error
)
{
_processUpdateError
(
error
);
});
}
clearFields
()
{
_name
=
''
;
_photoUrl
=
''
;
_usersToAdd
=
null
;
_usersToRemove
.
clear
();
}
}
lib/srcchat/chat_dialog_resizable_screen.dart
0 → 100644
View file @
5bd5df70
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
;
}
lib/srcchat/chat_dialog_screen.dart
0 → 100644
View file @
5bd5df70
import
'dart:async'
;
import
'package:cached_network_image/cached_network_image.dart'
;
import
'package:collection/collection.dart'
show
IterableExtension
;
import
'package:connectivity_plus/connectivity_plus.dart'
;
import
'package:emoji_picker_flutter/emoji_picker_flutter.dart'
;
import
'package:file_picker/file_picker.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
;
import
'package:fluttertoast/fluttertoast.dart'
;
import
'package:intl/intl.dart'
;
import
'package:universal_io/io.dart'
;
import
'package:connectycube_sdk/connectycube_sdk.dart'
;
import
'../src/constants/colors_const.dart'
;
import
'../src/utils/app_utils.dart'
;
import
'managers/chat_manager.dart'
;
import
'update_dialog_flow.dart'
;
import
'utils/api_utils.dart'
;
import
'utils/consts.dart'
;
import
'utils/platform_utils.dart'
as
platformUtils
;
import
'widgets/common.dart'
;
import
'widgets/full_photo.dart'
;
import
'widgets/loading.dart'
;
class
ChatDialogScreen
extends
StatelessWidget
{
final
CubeUser
_cubeUser
;
final
CubeDialog
_cubeDialog
;
const
ChatDialogScreen
(
this
.
_cubeUser
,
this
.
_cubeDialog
,
{
super
.
key
});
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
appBar:
AppBar
(
backgroundColor:
ColorConst
.
appGreenColor
,
title:
Text
(
_cubeDialog
.
name
!=
null
?
_cubeDialog
.
name
!
:
''
,
),
centerTitle:
false
,
actions:
<
Widget
>[
IconButton
(
onPressed:
()
=>
showChatDetails
(
context
,
_cubeUser
,
_cubeDialog
),
icon:
Icon
(
Icons
.
info_outline
,
color:
ColorConst
.
appBleckColor
,
),
),
],
),
body:
ChatScreen
(
_cubeUser
,
_cubeDialog
),
);
}
}
class
ChatScreen
extends
StatefulWidget
{
static
const
String
TAG
=
"_CreateChatScreenState"
;
final
CubeUser
_cubeUser
;
final
CubeDialog
_cubeDialog
;
ChatScreen
(
this
.
_cubeUser
,
this
.
_cubeDialog
,
{
super
.
key
});
@override
State
createState
()
=>
ChatScreenState
(
_cubeUser
,
_cubeDialog
);
}
class
ChatScreenState
extends
State
<
ChatScreen
>
{
final
CubeUser
_cubeUser
;
final
CubeDialog
_cubeDialog
;
final
Map
<
int
?,
CubeUser
?>
_occupants
=
{};
late
bool
isLoading
;
late
StreamSubscription
<
ConnectivityResult
>
connectivityStateSubscription
;
String
?
imageUrl
;
List
<
CubeMessage
>
listMessage
=
[];
Timer
?
typingTimer
;
bool
isTyping
=
false
;
String
userStatus
=
''
;
static
const
int
TYPING_TIMEOUT
=
700
;
static
const
int
STOP_TYPING_TIMEOUT
=
2000
;
int
_sendIsTypingTime
=
DateTime
.
now
().
millisecondsSinceEpoch
;
Timer
?
_sendStopTypingTimer
;
final
TextEditingController
textEditingController
=
TextEditingController
();
final
ScrollController
listScrollController
=
ScrollController
();
StreamSubscription
<
CubeMessage
>?
msgSubscription
;
StreamSubscription
<
MessageStatus
>?
deliveredSubscription
;
StreamSubscription
<
MessageStatus
>?
readSubscription
;
StreamSubscription
<
TypingStatus
>?
typingSubscription
;
StreamSubscription
<
MessageReaction
>?
reactionsSubscription
;
final
List
<
CubeMessage
>
_unreadMessages
=
[];
final
List
<
CubeMessage
>
_unsentMessages
=
[];
static
const
int
messagesPerPage
=
50
;
int
lastPartSize
=
0
;
List
<
CubeMessage
>
oldMessages
=
[];
late
FocusNode
_editMessageFocusNode
;
ChatScreenState
(
this
.
_cubeUser
,
this
.
_cubeDialog
);
@override
void
initState
()
{
super
.
initState
();
_initCubeChat
();
isLoading
=
false
;
imageUrl
=
''
;
listScrollController
.
addListener
(
onScrollChanged
);
connectivityStateSubscription
=
Connectivity
().
onConnectivityChanged
.
listen
(
onConnectivityChanged
);
_editMessageFocusNode
=
createEditMessageFocusNode
();
}
@override
void
dispose
()
{
msgSubscription
?.
cancel
();
deliveredSubscription
?.
cancel
();
readSubscription
?.
cancel
();
typingSubscription
?.
cancel
();
reactionsSubscription
?.
cancel
();
textEditingController
.
dispose
();
connectivityStateSubscription
.
cancel
();
super
.
dispose
();
}
void
openGallery
()
async
{
FilePickerResult
?
result
=
await
FilePicker
.
platform
.
pickFiles
(
type:
FileType
.
image
,
);
if
(
result
==
null
)
return
;
setState
(()
{
isLoading
=
true
;
});
var
uploadImageFuture
=
getUploadingImageFuture
(
result
);
Uint8List
imageData
;
if
(
kIsWeb
)
{
imageData
=
result
.
files
.
single
.
bytes
!;
}
else
{
imageData
=
File
(
result
.
files
.
single
.
path
!).
readAsBytesSync
();
}
var
decodedImage
=
await
decodeImageFromList
(
imageData
);
uploadImageFile
(
uploadImageFuture
,
decodedImage
);
}
Future
uploadImageFile
(
Future
<
CubeFile
>
uploadAction
,
imageData
)
async
{
uploadAction
.
then
((
cubeFile
)
{
onSendChatAttachment
(
cubeFile
,
imageData
);
}).
catchError
((
ex
)
{
setState
(()
{
isLoading
=
false
;
});
Fluttertoast
.
showToast
(
msg:
'This file is not an image'
);
});
}
void
onReceiveMessage
(
CubeMessage
message
)
{
log
(
"onReceiveMessage message=
$message
"
);
if
(
message
.
dialogId
!=
_cubeDialog
.
dialogId
)
return
;
addMessageToListView
(
message
);
}
void
onDeliveredMessage
(
MessageStatus
status
)
{
log
(
"onDeliveredMessage message=
$status
"
);
updateReadDeliveredStatusMessage
(
status
,
false
);
}
void
onReadMessage
(
MessageStatus
status
)
{
log
(
"onReadMessage message=
${status.messageId}
"
);
updateReadDeliveredStatusMessage
(
status
,
true
);
}
void
onReactionReceived
(
MessageReaction
reaction
)
{
log
(
"onReactionReceived message=
${reaction.messageId}
"
);
_updateMessageReactions
(
reaction
);
}
void
onTypingMessage
(
TypingStatus
status
)
{
log
(
"TypingStatus message=
${status.userId}
"
);
if
(
status
.
userId
==
_cubeUser
.
id
||
(
status
.
dialogId
!=
null
&&
status
.
dialogId
!=
_cubeDialog
.
dialogId
))
return
;
userStatus
=
_occupants
[
status
.
userId
]?.
fullName
??
_occupants
[
status
.
userId
]?.
login
??
_occupants
[
status
.
userId
]?.
email
??
''
;
if
(
userStatus
.
isEmpty
)
return
;
userStatus
=
"
$userStatus
is typing ..."
;
if
(
isTyping
!=
true
)
{
setState
(()
{
isTyping
=
true
;
});
}
startTypingTimer
();
}
startTypingTimer
()
{
typingTimer
?.
cancel
();
typingTimer
=
Timer
(
const
Duration
(
milliseconds:
900
),
()
{
setState
(()
{
isTyping
=
false
;
});
});
}
void
onSendChatMessage
(
String
content
)
{
if
(
content
.
trim
()
!=
''
)
{
final
message
=
createCubeMsg
();
message
.
body
=
content
.
trim
();
onSendMessage
(
message
);
}
else
{
Fluttertoast
.
showToast
(
msg:
'Nothing to send'
);
}
}
void
onSendChatAttachment
(
CubeFile
cubeFile
,
imageData
)
async
{
final
attachment
=
CubeAttachment
();
attachment
.
id
=
cubeFile
.
uid
;
attachment
.
type
=
CubeAttachmentType
.
IMAGE_TYPE
;
attachment
.
url
=
cubeFile
.
getPublicUrl
();
attachment
.
height
=
imageData
.
height
;
attachment
.
width
=
imageData
.
width
;
final
message
=
createCubeMsg
();
message
.
body
=
"Attachment"
;
message
.
attachments
=
[
attachment
];
onSendMessage
(
message
);
}
CubeMessage
createCubeMsg
()
{
var
message
=
CubeMessage
();
message
.
dateSent
=
DateTime
.
now
().
millisecondsSinceEpoch
~/
1000
;
message
.
markable
=
true
;
message
.
saveToHistory
=
true
;
return
message
;
}
void
onSendMessage
(
CubeMessage
message
)
async
{
log
(
"onSendMessage message=
$message
"
);
textEditingController
.
clear
();
await
_cubeDialog
.
sendMessage
(
message
);
message
.
senderId
=
_cubeUser
.
id
;
addMessageToListView
(
message
);
listScrollController
.
animateTo
(
0.0
,
duration:
const
Duration
(
milliseconds:
300
),
curve:
Curves
.
easeOut
);
if
(
_cubeDialog
.
type
==
CubeDialogType
.
PRIVATE
)
{
ChatManager
.
instance
.
sentMessagesController
.
add
(
message
..
dialogId
=
_cubeDialog
.
dialogId
);
}
}
updateReadDeliveredStatusMessage
(
MessageStatus
status
,
bool
isRead
)
{
log
(
'[updateReadDeliveredStatusMessage]'
);
setState
(()
{
CubeMessage
?
msg
=
listMessage
.
firstWhereOrNull
((
msg
)
=>
msg
.
messageId
==
status
.
messageId
);
if
(
msg
==
null
)
return
;
if
(
isRead
)
{
msg
.
readIds
==
null
?
msg
.
readIds
=
[
status
.
userId
]
:
msg
.
readIds
?.
add
(
status
.
userId
);
}
else
{
msg
.
deliveredIds
==
null
?
msg
.
deliveredIds
=
[
status
.
userId
]
:
msg
.
deliveredIds
?.
add
(
status
.
userId
);
}
log
(
'[updateReadDeliveredStatusMessage] status updated for
$msg
'
);
});
}
addMessageToListView
(
CubeMessage
message
)
{
setState
(()
{
isLoading
=
false
;
int
existMessageIndex
=
listMessage
.
indexWhere
((
cubeMessage
)
{
return
cubeMessage
.
messageId
==
message
.
messageId
;
});
if
(
existMessageIndex
!=
-
1
)
{
listMessage
[
existMessageIndex
]
=
message
;
}
else
{
listMessage
.
insert
(
0
,
message
);
}
});
}
@override
Widget
build
(
BuildContext
context
)
{
return
SafeArea
(
child:
Stack
(
children:
<
Widget
>[
Column
(
children:
<
Widget
>[
// List of messages
buildListMessage
(),
//Typing content
buildTyping
(),
// Input content
buildInput
(),
],
),
// Loading
// buildLoading()
],
),
);
}
Widget
buildItem
(
int
index
,
CubeMessage
message
)
{
markAsReadIfNeed
()
{
var
isOpponentMsgRead
=
message
.
readIds
!=
null
&&
message
.
readIds
!.
contains
(
_cubeUser
.
id
);
if
(
message
.
senderId
!=
_cubeUser
.
id
&&
!
isOpponentMsgRead
)
{
if
(
message
.
readIds
==
null
)
{
message
.
readIds
=
[
_cubeUser
.
id
!];
}
else
{
message
.
readIds
!.
add
(
_cubeUser
.
id
!);
}
if
(
CubeChatConnection
.
instance
.
chatConnectionState
==
CubeChatConnectionState
.
Ready
)
{
_cubeDialog
.
readMessage
(
message
);
}
else
{
_unreadMessages
.
add
(
message
);
}
ChatManager
.
instance
.
readMessagesController
.
add
(
MessageStatus
(
_cubeUser
.
id
!,
message
.
messageId
!,
_cubeDialog
.
dialogId
!));
}
}
Widget
getReadDeliveredWidget
()
{
log
(
"[getReadDeliveredWidget]"
);
bool
messageIsRead
()
{
log
(
"[getReadDeliveredWidget] messageIsRead"
);
if
(
_cubeDialog
.
type
==
CubeDialogType
.
PRIVATE
)
{
return
message
.
readIds
!=
null
&&
(
message
.
recipientId
==
null
||
message
.
readIds
!.
contains
(
message
.
recipientId
));
}
return
message
.
readIds
!=
null
&&
message
.
readIds
!.
any
(
(
int
id
)
=>
id
!=
_cubeUser
.
id
&&
_occupants
.
keys
.
contains
(
id
));
}
bool
messageIsDelivered
()
{
log
(
"[getReadDeliveredWidget] messageIsDelivered"
);
if
(
_cubeDialog
.
type
==
CubeDialogType
.
PRIVATE
)
{
return
message
.
deliveredIds
!=
null
&&
(
message
.
recipientId
==
null
||
message
.
deliveredIds
!.
contains
(
message
.
recipientId
));
}
return
message
.
deliveredIds
!=
null
&&
message
.
deliveredIds
!.
any
(
(
int
id
)
=>
id
!=
_cubeUser
.
id
&&
_occupants
.
keys
.
contains
(
id
));
}
if
(
messageIsRead
())
{
log
(
"[getReadDeliveredWidget] if messageIsRead"
);
return
getMessageStateWidget
(
MessageState
.
read
);
}
else
if
(
messageIsDelivered
())
{
log
(
"[getReadDeliveredWidget] if messageIsDelivered"
);
return
getMessageStateWidget
(
MessageState
.
delivered
);
}
else
{
log
(
"[getReadDeliveredWidget] sent"
);
return
getMessageStateWidget
(
MessageState
.
sent
);
}
}
Widget
getDateWidget
()
{
return
Text
(
DateFormat
(
'HH:mm'
).
format
(
DateTime
.
fromMillisecondsSinceEpoch
(
message
.
dateSent
!
*
1000
)),
style:
TextStyle
(
color:
greyColor
,
fontSize:
12.0
,
fontStyle:
FontStyle
.
italic
),
);
}
Widget
getHeaderDateWidget
()
{
return
Container
(
alignment:
Alignment
.
center
,
margin:
const
EdgeInsets
.
all
(
10.0
),
child:
Text
(
DateFormat
(
'dd MMMM'
).
format
(
DateTime
.
fromMillisecondsSinceEpoch
(
message
.
dateSent
!
*
1000
)),
style:
TextStyle
(
color:
primaryColor
,
fontSize:
20.0
,
fontStyle:
FontStyle
.
italic
),
),
);
}
bool
isHeaderView
()
{
int
headerId
=
int
.
parse
(
DateFormat
(
'ddMMyyyy'
).
format
(
DateTime
.
fromMillisecondsSinceEpoch
(
message
.
dateSent
!
*
1000
)));
if
(
index
>=
listMessage
.
length
-
1
)
{
return
false
;
}
var
msgPrev
=
listMessage
[
index
+
1
];
int
nextItemHeaderId
=
int
.
parse
(
DateFormat
(
'ddMMyyyy'
).
format
(
DateTime
.
fromMillisecondsSinceEpoch
(
msgPrev
.
dateSent
!
*
1000
)));
var
result
=
headerId
!=
nextItemHeaderId
;
return
result
;
}
if
(
message
.
senderId
==
_cubeUser
.
id
)
{
// Right (own message)
return
Column
(
children:
<
Widget
>[
isHeaderView
()
?
getHeaderDateWidget
()
:
SizedBox
.
shrink
(),
GestureDetector
(
onLongPress:
()
=>
_reactOnMessage
(
message
),
child:
Row
(
mainAxisAlignment:
MainAxisAlignment
.
end
,
children:
<
Widget
>[
Padding
(
padding:
EdgeInsets
.
only
(
bottom:
isLastMessageRight
(
index
)
?
20.0
:
10.0
,
right:
4.0
),
child:
GestureDetector
(
onTap:
()
=>
_reactOnMessage
(
message
),
child:
const
Icon
(
Icons
.
add_reaction_outlined
,
size:
16
,
color:
Colors
.
grey
),
),
),
message
.
attachments
?.
isNotEmpty
??
false
// Image
?
Container
(
margin:
EdgeInsets
.
only
(
bottom:
isLastMessageRight
(
index
)
?
20.0
:
10.0
,
right:
10.0
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
end
,
children:
[
GestureDetector
(
onTap:
()
{
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
FullPhoto
(
url:
message
.
attachments
!.
first
.
url
!)));
},
child:
ClipRRect
(
borderRadius:
const
BorderRadius
.
only
(
topLeft:
Radius
.
circular
(
8.0
),
topRight:
Radius
.
circular
(
8.0
)),
child:
CachedNetworkImage
(
placeholder:
(
context
,
url
)
=>
Container
(
width:
200.0
,
height:
200.0
,
padding:
const
EdgeInsets
.
all
(
70.0
),
decoration:
BoxDecoration
(
color:
greyColor2
,
borderRadius:
const
BorderRadius
.
all
(
Radius
.
circular
(
8.0
),
),
),
child:
AppUtils
.
buildLoading
(),
),
errorWidget:
(
context
,
url
,
error
)
=>
Material
(
borderRadius:
const
BorderRadius
.
all
(
Radius
.
circular
(
8.0
),
),
clipBehavior:
Clip
.
hardEdge
,
child:
Image
.
asset
(
'images/img_not_available.jpeg'
,
width:
200.0
,
height:
200.0
,
fit:
BoxFit
.
cover
,
),
),
imageUrl:
message
.
attachments
!.
first
.
url
!,
width:
200.0
,
height:
200.0
,
fit:
BoxFit
.
cover
,
),
),
),
if
(
message
.
reactions
!=
null
&&
message
.
reactions
!.
total
.
isNotEmpty
)
getReactionsWidget
(
message
),
Row
(
mainAxisSize:
MainAxisSize
.
min
,
children:
[
getDateWidget
(),
getReadDeliveredWidget
(),
],
),
]),
)
:
message
.
body
!=
null
&&
message
.
body
!.
isNotEmpty
// Text
?
Flexible
(
child:
Container
(
constraints:
const
BoxConstraints
(
maxWidth:
480
),
padding:
const
EdgeInsets
.
fromLTRB
(
15.0
,
10.0
,
15.0
,
10.0
),
decoration:
BoxDecoration
(
color:
greyColor2
,
borderRadius:
BorderRadius
.
circular
(
8.0
)),
margin:
EdgeInsets
.
only
(
bottom:
isLastMessageRight
(
index
)
?
20.0
:
10.0
,
right:
10.0
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
end
,
children:
[
Text
(
message
.
body
!,
style:
TextStyle
(
color:
primaryColor
),
),
if
(
message
.
reactions
!=
null
&&
message
.
reactions
!.
total
.
isNotEmpty
)
getReactionsWidget
(
message
),
Row
(
mainAxisSize:
MainAxisSize
.
min
,
children:
[
getDateWidget
(),
getReadDeliveredWidget
(),
],
),
]),
),
)
:
Container
(
padding:
const
EdgeInsets
.
fromLTRB
(
15.0
,
10.0
,
15.0
,
10.0
),
width:
200.0
,
decoration:
BoxDecoration
(
color:
greyColor2
,
borderRadius:
BorderRadius
.
circular
(
8.0
)),
margin:
EdgeInsets
.
only
(
bottom:
isLastMessageRight
(
index
)
?
20.0
:
10.0
,
right:
10.0
),
child:
Text
(
"Empty"
,
style:
TextStyle
(
color:
primaryColor
),
),
),
],
),
),
],
);
}
else
{
// Left (opponent message)
markAsReadIfNeed
();
return
Container
(
margin:
const
EdgeInsets
.
only
(
bottom:
10.0
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
<
Widget
>[
isHeaderView
()
?
getHeaderDateWidget
()
:
SizedBox
.
shrink
(),
GestureDetector
(
onLongPress:
()
=>
_reactOnMessage
(
message
),
child:
Row
(
children:
<
Widget
>[
getUserAvatarWidget
(
_occupants
[
message
.
senderId
],
30
),
message
.
attachments
?.
isNotEmpty
??
false
?
Container
(
margin:
EdgeInsets
.
only
(
left:
10.0
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
GestureDetector
(
onTap:
()
{
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
FullPhoto
(
url:
message
.
attachments
!.
first
.
url
!)));
},
child:
ClipRRect
(
borderRadius:
const
BorderRadius
.
only
(
topLeft:
Radius
.
circular
(
8.0
),
topRight:
Radius
.
circular
(
8.0
)),
child:
CachedNetworkImage
(
placeholder:
(
context
,
url
)
=>
Container
(
width:
200.0
,
height:
200.0
,
padding:
const
EdgeInsets
.
all
(
70.0
),
decoration:
BoxDecoration
(
color:
greyColor2
,
borderRadius:
const
BorderRadius
.
all
(
Radius
.
circular
(
8.0
),
),
),
child:
AppUtils
.
buildLoading
()
),
errorWidget:
(
context
,
url
,
error
)
=>
Material
(
borderRadius:
const
BorderRadius
.
all
(
Radius
.
circular
(
8.0
),
),
clipBehavior:
Clip
.
hardEdge
,
child:
Image
.
asset
(
'images/img_not_available.jpeg'
,
width:
200.0
,
height:
200.0
,
fit:
BoxFit
.
cover
,
),
),
imageUrl:
message
.
attachments
!.
first
.
url
!,
width:
200.0
,
height:
200.0
,
fit:
BoxFit
.
cover
,
),
),
),
if
(
message
.
reactions
!=
null
&&
message
.
reactions
!.
total
.
isNotEmpty
)
getReactionsWidget
(
message
),
getDateWidget
(),
]),
)
:
message
.
body
!=
null
&&
message
.
body
!.
isNotEmpty
?
Flexible
(
child:
Container
(
constraints:
const
BoxConstraints
(
minWidth:
0.0
,
maxWidth:
480
),
padding:
const
EdgeInsets
.
fromLTRB
(
15.0
,
10.0
,
15.0
,
10.0
),
decoration:
BoxDecoration
(
color:
primaryColor
,
borderRadius:
BorderRadius
.
circular
(
8.0
)),
margin:
const
EdgeInsets
.
only
(
left:
10.0
),
child:
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
[
Text
(
message
.
body
!,
style:
const
TextStyle
(
color:
Colors
.
white
),
),
if
(
message
.
reactions
!=
null
&&
message
.
reactions
!.
total
.
isNotEmpty
)
getReactionsWidget
(
message
),
getDateWidget
(),
]),
),
)
:
Container
(
padding:
const
EdgeInsets
.
fromLTRB
(
15.0
,
10.0
,
15.0
,
10.0
),
width:
200.0
,
decoration:
BoxDecoration
(
color:
greyColor2
,
borderRadius:
BorderRadius
.
circular
(
8.0
)),
margin:
EdgeInsets
.
only
(
bottom:
isLastMessageRight
(
index
)
?
20.0
:
10.0
,
right:
10.0
),
child:
Text
(
"Empty"
,
style:
TextStyle
(
color:
primaryColor
),
),
),
Padding
(
padding:
const
EdgeInsets
.
only
(
// bottom: isLastMessageRight(index) ? 20.0 : 10.0,
left:
4.0
),
child:
GestureDetector
(
onTap:
()
=>
_reactOnMessage
(
message
),
child:
Icon
(
Icons
.
add_reaction_outlined
,
size:
16
,
color:
primaryColor
),
),
),
],
),
)
],
),
);
}
}
bool
isLastMessageLeft
(
int
index
)
{
if
((
index
>
0
&&
listMessage
[
index
-
1
].
id
==
_cubeUser
.
id
)
||
index
==
0
)
{
return
true
;
}
else
{
return
false
;
}
}
bool
isLastMessageRight
(
int
index
)
{
if
((
index
>
0
&&
listMessage
[
index
-
1
].
id
!=
_cubeUser
.
id
)
||
index
==
0
)
{
return
true
;
}
else
{
return
false
;
}
}
Widget
buildLoading
()
{
return
Positioned
(
child:
isLoading
?
AppUtils
.
buildLoading
()
:
Container
(),
);
}
Widget
buildTyping
()
{
return
Visibility
(
visible:
isTyping
,
child:
Container
(
alignment:
Alignment
.
centerLeft
,
margin:
const
EdgeInsets
.
all
(
16.0
),
child:
Text
(
userStatus
,
style:
TextStyle
(
color:
primaryColor
),
),
),
);
}
Widget
buildInput
()
{
return
Container
(
width:
double
.
infinity
,
height:
50.0
,
decoration:
BoxDecoration
(
border:
Border
(
top:
BorderSide
(
color:
greyColor2
,
width:
0.5
)),
color:
Colors
.
white
),
child:
Row
(
children:
<
Widget
>[
// Button send image
Material
(
color:
Colors
.
white
,
child:
Container
(
margin:
const
EdgeInsets
.
symmetric
(
horizontal:
1.0
),
child:
IconButton
(
icon:
const
Icon
(
Icons
.
image
),
onPressed:
()
{
openGallery
();
},
color:
primaryColor
,
),
),
),
// Edit text
Flexible
(
child:
TextField
(
autofocus:
platformUtils
.
isDesktop
(),
focusNode:
_editMessageFocusNode
,
keyboardType:
TextInputType
.
multiline
,
maxLines:
null
,
style:
TextStyle
(
color:
primaryColor
,
fontSize:
15.0
),
controller:
textEditingController
,
decoration:
InputDecoration
.
collapsed
(
hintText:
'Type your message...'
,
hintStyle:
TextStyle
(
color:
greyColor
),
),
onChanged:
(
text
)
{
sendIsTypingStatus
();
},
),
),
// Button send message
Material
(
color:
Colors
.
white
,
child:
Container
(
margin:
const
EdgeInsets
.
symmetric
(
horizontal:
8.0
),
child:
IconButton
(
icon:
const
Icon
(
Icons
.
send
),
onPressed:
()
=>
onSendChatMessage
(
textEditingController
.
text
),
color:
primaryColor
,
),
),
),
],
),
);
}
Widget
buildListMessage
()
{
getWidgetMessages
(
listMessage
)
{
return
ListView
.
builder
(
padding:
const
EdgeInsets
.
all
(
10.0
),
itemBuilder:
(
context
,
index
)
=>
buildItem
(
index
,
listMessage
[
index
]),
itemCount:
listMessage
.
length
,
reverse:
true
,
controller:
listScrollController
,
);
}
return
Flexible
(
child:
StreamBuilder
<
List
<
CubeMessage
>>(
stream:
getMessagesList
().
asStream
(),
builder:
(
context
,
snapshot
)
{
if
(!
snapshot
.
hasData
)
{
return
Center
(
child:
AppUtils
.
buildLoading
(),
);
}
else
{
listMessage
=
snapshot
.
data
??
[];
return
getWidgetMessages
(
listMessage
);
}
},
),
);
}
Future
<
List
<
CubeMessage
>>
getMessagesList
()
async
{
if
(
listMessage
.
isNotEmpty
)
return
Future
.
value
(
listMessage
);
Completer
<
List
<
CubeMessage
>>
completer
=
Completer
();
List
<
CubeMessage
>?
messages
;
try
{
await
Future
.
wait
<
void
>([
getMessagesByDate
(
0
,
false
).
then
((
loadedMessages
)
{
isLoading
=
false
;
messages
=
loadedMessages
;
}),
getAllUsersByIds
(
_cubeDialog
.
occupantsIds
!.
toSet
()).
then
((
result
)
=>
_occupants
.
addAll
({
for
(
var
item
in
result
!.
items
)
item
.
id
:
item
}))
]);
completer
.
complete
(
messages
);
}
catch
(
error
)
{
completer
.
completeError
(
error
);
}
return
completer
.
future
;
}
void
onScrollChanged
()
{
if
((
listScrollController
.
position
.
pixels
==
listScrollController
.
position
.
maxScrollExtent
)
&&
messagesPerPage
>=
lastPartSize
)
{
setState
(()
{
isLoading
=
true
;
if
(
oldMessages
.
isNotEmpty
)
{
getMessagesBetweenDates
(
oldMessages
.
first
.
dateSent
??
0
,
listMessage
.
last
.
dateSent
??
DateTime
.
now
().
millisecondsSinceEpoch
~/
1000
)
.
then
((
newMessages
)
{
setState
(()
{
isLoading
=
false
;
listMessage
.
addAll
(
newMessages
);
if
(
newMessages
.
length
<
messagesPerPage
)
{
oldMessages
.
insertAll
(
0
,
listMessage
);
listMessage
=
List
.
from
(
oldMessages
);
oldMessages
.
clear
();
}
});
});
}
else
{
getMessagesByDate
(
listMessage
.
last
.
dateSent
??
0
,
false
)
.
then
((
messages
)
{
setState
(()
{
isLoading
=
false
;
listMessage
.
addAll
(
messages
);
});
});
}
});
}
}
Future
<
bool
>
onBackPress
()
{
return
Navigator
.
pushNamedAndRemoveUntil
<
bool
>(
context
,
'select_dialog'
,
(
r
)
=>
false
,
arguments:
{
USER_ARG_NAME:
_cubeUser
}).
then
((
value
)
{
return
true
;
});
}
_initChatListeners
()
{
log
(
"[_initChatListeners]"
);
msgSubscription
=
CubeChatConnection
.
instance
.
chatMessagesManager
!.
chatMessagesStream
.
listen
(
onReceiveMessage
);
deliveredSubscription
=
CubeChatConnection
.
instance
.
messagesStatusesManager
!.
deliveredStream
.
listen
(
onDeliveredMessage
);
readSubscription
=
CubeChatConnection
.
instance
.
messagesStatusesManager
!.
readStream
.
listen
(
onReadMessage
);
typingSubscription
=
CubeChatConnection
.
instance
.
typingStatusesManager
!.
isTypingStream
.
listen
(
onTypingMessage
);
reactionsSubscription
=
CubeChatConnection
.
instance
.
messagesReactionsManager
?.
reactionsStream
.
listen
(
onReactionReceived
);
}
void
_initCubeChat
()
{
log
(
"_initCubeChat"
);
if
(
CubeChatConnection
.
instance
.
isAuthenticated
())
{
log
(
"[_initCubeChat] isAuthenticated"
);
_initChatListeners
();
}
else
{
log
(
"[_initCubeChat] not authenticated"
);
CubeChatConnection
.
instance
.
connectionStateStream
.
listen
((
state
)
{
log
(
"[_initCubeChat] state
$state
"
);
if
(
CubeChatConnectionState
.
Ready
==
state
)
{
_initChatListeners
();
if
(
_unreadMessages
.
isNotEmpty
)
{
_unreadMessages
.
forEach
((
cubeMessage
)
{
_cubeDialog
.
readMessage
(
cubeMessage
);
});
_unreadMessages
.
clear
();
}
if
(
_unsentMessages
.
isNotEmpty
)
{
_unsentMessages
.
forEach
((
cubeMessage
)
{
_cubeDialog
.
sendMessage
(
cubeMessage
);
});
_unsentMessages
.
clear
();
}
}
});
}
}
void
sendIsTypingStatus
()
{
var
currentTime
=
DateTime
.
now
().
millisecondsSinceEpoch
;
var
isTypingTimeout
=
currentTime
-
_sendIsTypingTime
;
if
(
isTypingTimeout
>=
TYPING_TIMEOUT
)
{
_sendIsTypingTime
=
currentTime
;
_cubeDialog
.
sendIsTypingStatus
();
_startStopTypingStatus
();
}
}
void
_startStopTypingStatus
()
{
_sendStopTypingTimer
?.
cancel
();
_sendStopTypingTimer
=
Timer
(
Duration
(
milliseconds:
STOP_TYPING_TIMEOUT
),
()
{
_cubeDialog
.
sendStopTypingStatus
();
});
}
Future
<
List
<
CubeMessage
>>
getMessagesByDate
(
int
date
,
bool
isLoadNew
)
async
{
var
params
=
GetMessagesParameters
();
params
.
sorter
=
RequestSorter
(
SORT_DESC
,
''
,
'date_sent'
);
params
.
limit
=
messagesPerPage
;
params
.
filters
=
[
RequestFilter
(
''
,
'date_sent'
,
isLoadNew
||
date
==
0
?
'gt'
:
'lt'
,
date
)
];
return
getMessages
(
_cubeDialog
.
dialogId
!,
params
.
getRequestParameters
())
.
then
((
result
)
{
lastPartSize
=
result
!.
items
.
length
;
return
result
.
items
;
})
.
whenComplete
(()
{})
.
catchError
((
onError
)
{
return
List
<
CubeMessage
>.
empty
(
growable:
true
);
});
}
Future
<
List
<
CubeMessage
>>
getMessagesBetweenDates
(
int
startDate
,
int
endDate
)
async
{
var
params
=
GetMessagesParameters
();
params
.
sorter
=
RequestSorter
(
SORT_DESC
,
''
,
'date_sent'
);
params
.
limit
=
messagesPerPage
;
params
.
filters
=
[
RequestFilter
(
''
,
'date_sent'
,
'gt'
,
startDate
),
RequestFilter
(
''
,
'date_sent'
,
'lt'
,
endDate
)
];
return
getMessages
(
_cubeDialog
.
dialogId
!,
params
.
getRequestParameters
())
.
then
((
result
)
{
return
result
!.
items
;
});
}
void
onConnectivityChanged
(
ConnectivityResult
connectivityType
)
{
log
(
"[ChatScreenState] connectivityType changed to '
$connectivityType
'"
);
if
(
connectivityType
==
ConnectivityResult
.
wifi
||
connectivityType
==
ConnectivityResult
.
mobile
)
{
setState
(()
{
isLoading
=
true
;
});
getMessagesBetweenDates
(
listMessage
.
first
.
dateSent
??
0
,
DateTime
.
now
().
millisecondsSinceEpoch
~/
1000
)
.
then
((
newMessages
)
{
setState
(()
{
if
(
newMessages
.
length
==
messagesPerPage
)
{
oldMessages
=
List
.
from
(
listMessage
);
listMessage
=
newMessages
;
}
else
{
listMessage
.
insertAll
(
0
,
newMessages
);
}
});
}).
whenComplete
(()
{
setState
(()
{
isLoading
=
false
;
});
});
}
}
getReactionsWidget
(
CubeMessage
message
)
{
if
(
message
.
reactions
==
null
)
return
Container
();
var
isOwnMessage
=
message
.
senderId
==
_cubeUser
.
id
;
return
LayoutBuilder
(
builder:
(
context
,
constraints
)
{
var
widgetWidth
=
constraints
.
maxWidth
==
double
.
infinity
?
240
:
constraints
.
maxWidth
;
var
maxColumns
=
(
widgetWidth
/
60
).
round
();
if
(
message
.
reactions
!.
total
.
length
<
maxColumns
)
{
maxColumns
=
message
.
reactions
!.
total
.
length
;
}
return
SizedBox
(
width:
maxColumns
*
56
,
child:
GridView
.
count
(
primary:
false
,
crossAxisCount:
maxColumns
,
mainAxisSpacing:
4
,
childAspectRatio:
2
,
physics:
NeverScrollableScrollPhysics
(),
shrinkWrap:
true
,
padding:
const
EdgeInsets
.
symmetric
(
vertical:
4
),
children:
<
Widget
>[
...
message
.
reactions
!.
total
.
keys
.
map
((
reaction
)
{
return
GestureDetector
(
onTap:
()
=>
_performReaction
(
Emoji
(
reaction
,
''
),
message
),
child:
Padding
(
padding:
EdgeInsets
.
only
(
left:
isOwnMessage
?
4
:
0
,
right:
isOwnMessage
?
0
:
4
,
),
child:
ClipRRect
(
borderRadius:
const
BorderRadius
.
all
(
Radius
.
circular
(
16
),
),
child:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
vertical:
4
,
horizontal:
6
),
color:
message
.
reactions
!.
own
.
contains
(
reaction
)
?
Colors
.
green
:
Colors
.
grey
,
child:
Row
(
crossAxisAlignment:
CrossAxisAlignment
.
center
,
mainAxisAlignment:
MainAxisAlignment
.
spaceAround
,
children:
[
Text
(
reaction
,
style:
kIsWeb
?
const
TextStyle
(
color:
Colors
.
green
,
fontFamily:
'NotoColorEmoji'
)
:
null
),
Text
(
'
${message.reactions!.total[reaction].toString()}
'
,
style:
const
TextStyle
(
color:
Colors
.
white
,
)),
],
)),
)));
}).
toList
()
],
));
});
}
_reactOnMessage
(
CubeMessage
message
)
{
showDialog
<
Emoji
>(
context:
context
,
builder:
(
BuildContext
context
)
{
return
Dialog
(
child:
Container
(
margin:
const
EdgeInsets
.
all
(
8
),
decoration:
BoxDecoration
(
color:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
8.0
)),
width:
400
,
height:
400
,
child:
EmojiPicker
(
config:
const
Config
(
emojiTextStyle:
kIsWeb
?
TextStyle
(
color:
Colors
.
green
,
fontFamily:
'NotoColorEmoji'
)
:
null
,
iconColorSelected:
Colors
.
green
,
indicatorColor:
Colors
.
green
,
bgColor:
Colors
.
white
,
),
onEmojiSelected:
(
category
,
emoji
)
{
Navigator
.
pop
(
context
,
emoji
);
},
)));
}).
then
((
emoji
)
{
log
(
"onEmojiSelected emoji:
${emoji?.emoji}
"
);
if
(
emoji
!=
null
)
{
_performReaction
(
emoji
,
message
);
}
});
}
void
_performReaction
(
Emoji
emoji
,
CubeMessage
message
)
{
if
((
message
.
reactions
?.
own
.
isNotEmpty
??
false
)
&&
(
message
.
reactions
?.
own
.
contains
(
emoji
.
emoji
)
??
false
))
{
removeMessageReaction
(
message
.
messageId
!,
emoji
.
emoji
);
_updateMessageReactions
(
MessageReaction
(
_cubeUser
.
id
!,
_cubeDialog
.
dialogId
!,
message
.
messageId
!,
removeReaction:
emoji
.
emoji
));
}
else
{
addMessageReaction
(
message
.
messageId
!,
emoji
.
emoji
);
_updateMessageReactions
(
MessageReaction
(
_cubeUser
.
id
!,
_cubeDialog
.
dialogId
!,
message
.
messageId
!,
addReaction:
emoji
.
emoji
));
}
}
void
_updateMessageReactions
(
MessageReaction
reaction
)
{
log
(
'[_updateMessageReactions]'
);
setState
(()
{
CubeMessage
?
msg
=
listMessage
.
firstWhereOrNull
((
msg
)
=>
msg
.
messageId
==
reaction
.
messageId
);
if
(
msg
==
null
)
return
;
if
(
msg
.
reactions
==
null
)
{
msg
.
reactions
=
CubeMessageReactions
.
fromJson
({
'own'
:
{
if
(
reaction
.
userId
==
_cubeUser
.
id
)
reaction
.
addReaction
},
'total'
:
{
reaction
.
addReaction
:
1
}
});
}
else
{
if
(
reaction
.
addReaction
!=
null
)
{
if
(
reaction
.
userId
!=
_cubeUser
.
id
||
!(
msg
.
reactions
?.
own
.
contains
(
reaction
.
addReaction
)
??
false
))
{
if
(
reaction
.
userId
==
_cubeUser
.
id
)
{
msg
.
reactions
!.
own
.
add
(
reaction
.
addReaction
!);
}
msg
.
reactions
!.
total
[
reaction
.
addReaction
!]
=
msg
.
reactions
!.
total
[
reaction
.
addReaction
]
==
null
?
1
:
msg
.
reactions
!.
total
[
reaction
.
addReaction
]!
+
1
;
}
}
if
(
reaction
.
removeReaction
!=
null
)
{
if
(
reaction
.
userId
!=
_cubeUser
.
id
||
(
msg
.
reactions
?.
own
.
contains
(
reaction
.
removeReaction
)
??
false
))
{
if
(
reaction
.
userId
==
_cubeUser
.
id
)
{
msg
.
reactions
!.
own
.
remove
(
reaction
.
removeReaction
!);
}
msg
.
reactions
!.
total
[
reaction
.
removeReaction
!]
=
msg
.
reactions
!.
total
[
reaction
.
removeReaction
]
!=
null
&&
msg
.
reactions
!.
total
[
reaction
.
removeReaction
]!
>
0
?
msg
.
reactions
!.
total
[
reaction
.
removeReaction
]!
-
1
:
0
;
}
msg
.
reactions
!.
total
.
removeWhere
((
key
,
value
)
=>
value
==
0
);
}
}
});
}
FocusNode
createEditMessageFocusNode
()
{
return
FocusNode
(
onKey:
(
FocusNode
node
,
RawKeyEvent
evt
)
{
if
(!
evt
.
isShiftPressed
&&
evt
.
logicalKey
==
LogicalKeyboardKey
.
enter
)
{
if
(
evt
is
RawKeyDownEvent
)
{
onSendChatMessage
(
textEditingController
.
text
);
}
_editMessageFocusNode
.
requestFocus
();
return
KeyEventResult
.
handled
;
}
else
if
(
evt
.
logicalKey
==
LogicalKeyboardKey
.
enter
)
{
if
(
evt
is
RawKeyDownEvent
)
{
setState
(()
{
textEditingController
.
text
=
textEditingController
.
text
+
'
\n
'
;
textEditingController
.
selection
=
TextSelection
.
collapsed
(
offset:
textEditingController
.
text
.
length
);
});
}
_editMessageFocusNode
.
requestFocus
();
return
KeyEventResult
.
handled
;
}
else
{
return
KeyEventResult
.
ignored
;
}
},
);
}
}
void
showChatDetails
(
BuildContext
context
,
CubeUser
cubeUser
,
CubeDialog
cubeDialog
)
async
{
log
(
"_chatDetails=
$cubeDialog
"
);
platformUtils
.
showModal
(
context:
context
,
child:
UpdateDialog
(
cubeUser
,
cubeDialog
));
}
lib/srcchat/create_dialog_flow.dart
0 → 100644
View file @
5bd5df70
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
,
);
});
}
}
lib/srcchat/login_screen.dart
0 → 100644
View file @
5bd5df70
import
'dart:convert'
;
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:flutter/material.dart'
;
import
'package:flutter_local_notifications/flutter_local_notifications.dart'
;
import
'package:fluttertoast/fluttertoast.dart'
;
import
'package:connectycube_sdk/connectycube_sdk.dart'
;
import
'../firebase_options.dart'
;
import
'managers/push_notifications_manager.dart'
;
import
'phone_auth_flow.dart'
;
import
'utils/api_utils.dart'
;
import
'utils/consts.dart'
;
import
'utils/platform_utils.dart'
as
platformUtils
;
import
'utils/pref_util.dart'
;
class
LoginScreen
extends
StatelessWidget
{
static
const
String
TAG
=
"LoginScreen"
;
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
// appBar: AppBar(automaticallyImplyLeading: false, title: Text('Chat')),
body:
LoginPage
(),
);
}
}
class
LoginPage
extends
StatefulWidget
{
@override
State
<
StatefulWidget
>
createState
()
=>
new
LoginPageState
();
}
// Used for controlling whether the user is loggin or creating an account
enum
FormType
{
login
,
register
}
class
LoginPageState
extends
State
<
LoginPage
>
{
static
const
String
TAG
=
"_LoginPageState"
;
final
TextEditingController
_loginFilter
=
new
TextEditingController
();
final
TextEditingController
_passwordFilter
=
new
TextEditingController
();
String
_login
=
""
;
String
_password
=
""
;
FormType
_form
=
FormType
.
login
;
// our default setting is to login, and we should switch to creating an account when the user chooses to
bool
_isLoginContinues
=
false
;
List
<
bool
>
loginEmailSelection
=
[
true
,
false
];
bool
isEmailSelected
=
false
;
LoginPageState
()
{
_loginFilter
.
addListener
(
_loginListen
);
_passwordFilter
.
addListener
(
_passwordListen
);
}
void
_loginListen
()
{
if
(
_loginFilter
.
text
.
isEmpty
)
{
_login
=
""
;
}
else
{
_login
=
_loginFilter
.
text
;
}
}
void
_passwordListen
()
{
if
(
_passwordFilter
.
text
.
isEmpty
)
{
_password
=
""
;
}
else
{
_password
=
_passwordFilter
.
text
;
}
}
// Swap in between our two forms, registering and logging in
void
_formChange
()
async
{
setState
(()
{
if
(
_form
==
FormType
.
register
)
{
_form
=
FormType
.
login
;
}
else
{
_form
=
FormType
.
register
;
}
});
}
@override
void
initState
()
{
super
.
initState
();
loginEmailSelection
=
[
true
,
false
];
isEmailSelected
=
loginEmailSelection
[
1
];
}
@override
Widget
build
(
BuildContext
context
)
{
return
new
Scaffold
(
body:
Center
(
child:
SingleChildScrollView
(
child:
new
Container
(
padding:
EdgeInsets
.
symmetric
(
horizontal:
16.0
),
child:
new
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
center
,
mainAxisSize:
MainAxisSize
.
max
,
mainAxisAlignment:
MainAxisAlignment
.
end
,
children:
<
Widget
>[
_buildLogoField
(),
_initLoginWidgets
()],
),
),
),
),
);
}
Widget
_buildLogoField
()
{
// return Image.asset('assets/images/splash.png');
return
Container
(
child:
Align
(
alignment:
FractionalOffset
.
center
,
child:
Column
(
children:
[
ConstrainedBox
(
constraints:
BoxConstraints
(
maxWidth:
350
),
child:
Image
.
asset
(
'assets/images/splash.png'
),
),
Container
(
margin:
EdgeInsets
.
only
(
left:
8
),
height:
18
,
width:
18
,
child:
Visibility
(
visible:
_isLoginContinues
,
child:
CircularProgressIndicator
(
strokeWidth:
2
,
),
),
),
],
),
),
);
}
_initLoginWidgets
()
{
return
FutureBuilder
<
Widget
>(
future:
getFilterChipsWidgets
(),
builder:
(
BuildContext
context
,
AsyncSnapshot
<
Widget
>
snapshot
)
{
if
(
snapshot
.
hasData
)
{
return
snapshot
.
data
!;
}
return
SizedBox
.
shrink
();
});
}
Future
<
Widget
>
getFilterChipsWidgets
()
async
{
if
(
_isLoginContinues
)
return
SizedBox
.
shrink
();
SharedPrefs
sharedPrefs
=
await
SharedPrefs
.
instance
.
init
();
var
loginType
=
sharedPrefs
.
getLoginType
();
var
user
=
sharedPrefs
.
getUser
();
if
((
user
!=
null
&&
loginType
==
null
)
||
loginType
!=
null
)
{
_loginToCCWithSavedUser
(
context
,
loginType
??
LoginType
.
login
);
return
SizedBox
.
shrink
();
}
else
return
new
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
center
,
mainAxisSize:
MainAxisSize
.
max
,
mainAxisAlignment:
MainAxisAlignment
.
end
,
children:
<
Widget
>[
_buildTextFields
(),
_buildButtons
()],
);
}
Widget
_buildTextFields
()
{
return
ConstrainedBox
(
constraints:
BoxConstraints
(
maxWidth:
400
),
child:
Container
(
child:
Column
(
children:
<
Widget
>[
Align
(
alignment:
Alignment
.
centerRight
,
child:
ToggleButtons
(
constraints:
BoxConstraints
(
maxHeight:
38
),
borderColor:
Colors
.
green
,
fillColor:
Colors
.
green
.
shade400
,
borderWidth:
1
,
selectedBorderColor:
Colors
.
green
,
selectedColor:
Colors
.
white
,
borderRadius:
BorderRadius
.
circular
(
28
),
children:
<
Widget
>[
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
vertical:
4
,
horizontal:
6.0
),
child:
Text
(
'By Login'
,
style:
TextStyle
(
color:
isEmailSelected
?
Colors
.
green
:
Colors
.
white
),
),
),
Padding
(
padding:
const
EdgeInsets
.
symmetric
(
vertical:
4
,
horizontal:
6.0
),
child:
Text
(
'By E-mail'
,
style:
TextStyle
(
color:
isEmailSelected
?
Colors
.
white
:
Colors
.
green
),
),
),
],
onPressed:
(
int
index
)
{
setState
(()
{
for
(
int
i
=
0
;
i
<
loginEmailSelection
.
length
;
i
++)
{
loginEmailSelection
[
i
]
=
i
==
index
;
}
isEmailSelected
=
loginEmailSelection
[
1
];
});
},
isSelected:
loginEmailSelection
,
),
),
Container
(
child:
TextField
(
keyboardType:
isEmailSelected
?
TextInputType
.
emailAddress
:
TextInputType
.
text
,
controller:
_loginFilter
,
decoration:
InputDecoration
(
labelText:
isEmailSelected
?
'E-mail'
:
'Login'
),
),
),
Container
(
child:
TextField
(
keyboardType:
TextInputType
.
visiblePassword
,
controller:
_passwordFilter
,
decoration:
InputDecoration
(
labelText:
'Password'
),
obscureText:
true
,
enableSuggestions:
false
,
autocorrect:
false
,
onSubmitted:
(
_
)
{
_form
==
FormType
.
login
?
_loginPressed
()
:
_createAccountPressed
();
},
),
)
],
),
),
);
}
Widget
_buildButtons
()
{
return
ConstrainedBox
(
constraints:
BoxConstraints
(
maxWidth:
400
),
child:
_form
==
FormType
.
login
?
Container
(
margin:
EdgeInsets
.
only
(
top:
8
),
child:
Column
(
children:
<
Widget
>[
ElevatedButton
(
child:
new
Text
(
'Login'
),
onPressed:
_loginPressed
,
),
TextButton
(
child:
new
Text
(
'Don
\'
t have an account? Tap here to register.'
),
onPressed:
_formChange
,
),
...
createCIPButtons
(),
],
),
)
:
Container
(
margin:
EdgeInsets
.
only
(
top:
8
),
child:
Column
(
children:
<
Widget
>[
ElevatedButton
(
child:
new
Text
(
'Create an Account'
),
onPressed:
_createAccountPressed
,
),
TextButton
(
child:
new
Text
(
'Have an account? Click here to login.'
),
onPressed:
_formChange
,
),
...
createCIPButtons
(),
],
),
),
);
}
List
<
Widget
>
createCIPButtons
()
{
return
[
Visibility
(
visible:
platformUtils
.
isPhoneAuthSupported
,
child:
OutlinedButton
.
icon
(
style:
OutlinedButton
.
styleFrom
(
minimumSize:
Size
(
190
,
36
),
),
icon:
Icon
(
Icons
.
dialpad
,
),
label:
Text
(
'By Phone number'
),
onPressed:
()
{
platformUtils
.
showModal
(
context:
context
,
child:
VerifyPhoneNumber
());
},
),
),
SizedBox
(
height:
6
,
),
OutlinedButton
.
icon
(
style:
OutlinedButton
.
styleFrom
(
foregroundColor:
Colors
.
blue
,
minimumSize:
Size
(
190
,
36
),
),
icon:
Icon
(
Icons
.
facebook
,
color:
Colors
.
blue
.
shade700
,
),
label:
Text
(
'By Facebook'
,
style:
TextStyle
(
color:
Colors
.
blue
.
shade700
),
),
onPressed:
()
{
Fluttertoast
.
showToast
(
msg:
'Coming soon'
,
gravity:
ToastGravity
.
BOTTOM
);
},
),
];
}
void
_loginPressed
()
{
print
(
'login with
$_login
and
$_password
'
);
var
userToLogin
=
CubeUser
();
if
(
isEmailSelected
)
{
userToLogin
.
email
=
_login
;
}
else
{
userToLogin
.
login
=
_login
;
}
userToLogin
.
password
=
_password
;
_loginToCC
(
context
,
userToLogin
,
saveUser:
true
);
}
void
_createAccountPressed
()
{
print
(
'create an user with
$_login
and
$_password
'
);
var
userToSignUp
=
CubeUser
();
if
(
isEmailSelected
)
{
userToSignUp
.
email
=
_login
;
}
else
{
userToSignUp
.
login
=
_login
;
}
userToSignUp
.
password
=
_password
;
userToSignUp
.
fullName
=
_login
;
_signInCC
(
context
,
userToSignUp
);
}
_signInCC
(
BuildContext
context
,
CubeUser
user
)
async
{
if
(
_isLoginContinues
)
return
;
setState
(()
{
_isLoginContinues
=
true
;
});
if
(!
CubeSessionManager
.
instance
.
isActiveSessionValid
())
{
try
{
await
createSession
();
}
catch
(
error
)
{
_processLoginError
(
error
);
}
}
signUp
(
user
).
then
((
newUser
)
{
print
(
"signUp newUser
$newUser
"
);
user
.
id
=
newUser
.
id
;
SharedPrefs
.
instance
.
saveNewUser
(
user
,
isEmailSelected
?
LoginType
.
email
:
LoginType
.
login
);
signIn
(
user
).
then
((
result
)
{
PushNotificationsManager
.
instance
.
init
();
_loginToCubeChat
(
context
,
user
);
});
}).
catchError
((
exception
)
{
_processLoginError
(
exception
);
});
}
_loginToCC
(
BuildContext
context
,
CubeUser
user
,
{
bool
saveUser
=
false
})
{
print
(
"_loginToCC user:
$user
"
);
if
(
_isLoginContinues
)
return
;
setState
(()
{
_isLoginContinues
=
true
;
});
createSession
(
user
).
then
((
cubeSession
)
async
{
print
(
"createSession cubeSession:
$cubeSession
"
);
var
tempUser
=
user
;
user
=
cubeSession
.
user
!..
password
=
tempUser
.
password
;
if
(
saveUser
)
SharedPrefs
.
instance
.
init
().
then
((
sharedPrefs
)
{
sharedPrefs
.
saveNewUser
(
user
,
isEmailSelected
?
LoginType
.
email
:
LoginType
.
login
);
});
PushNotificationsManager
.
instance
.
init
();
_loginToCubeChat
(
context
,
user
);
}).
catchError
((
error
)
{
_processLoginError
(
error
);
});
}
_loginToCCWithSavedUser
(
BuildContext
context
,
LoginType
loginType
)
async
{
log
(
"[_loginToCCWithSavedUser] user:
$loginType
"
);
if
(
_isLoginContinues
)
return
;
setState
(()
{
_isLoginContinues
=
true
;
});
Future
<
CubeUser
>?
signInFuture
;
if
(
loginType
==
LoginType
.
phone
)
{
var
phoneAuthToken
=
await
FirebaseAuth
.
instance
.
currentUser
?.
getIdToken
();
if
(
phoneAuthToken
==
null
)
{
setState
(()
{
_isLoginContinues
=
false
;
});
showDialog
(
context:
context
,
builder:
(
BuildContext
context
)
{
return
AlertDialog
(
title:
Text
(
'Error'
),
content:
Text
(
'Your Phone authentication session was expired, please refresh it by second login using your phone number'
),
actions:
<
Widget
>[
TextButton
(
child:
Text
(
"OK"
),
onPressed:
()
=>
Navigator
.
of
(
context
).
pop
(),
)
],
);
});
return
;
}
signInFuture
=
createSession
().
then
((
cubeSession
)
{
return
signInUsingFirebase
(
DefaultFirebaseOptions
.
currentPlatform
.
projectId
,
phoneAuthToken
)
.
then
((
cubeUser
)
{
return
SharedPrefs
.
instance
.
init
().
then
((
sharedPrefs
)
{
sharedPrefs
.
saveNewUser
(
cubeUser
,
LoginType
.
phone
);
return
cubeUser
..
password
=
CubeSessionManager
.
instance
.
activeSession
?.
token
;
});
});
});
}
else
if
(
loginType
==
LoginType
.
login
||
loginType
==
LoginType
.
email
)
{
signInFuture
=
SharedPrefs
.
instance
.
init
().
then
((
sharedPrefs
)
{
var
savedUser
=
sharedPrefs
.
getUser
();
return
createSession
(
savedUser
).
then
((
value
)
{
return
savedUser
!;
});
});
}
signInFuture
?.
then
((
cubeUser
)
{
PushNotificationsManager
.
instance
.
init
();
_loginToCubeChat
(
context
,
cubeUser
);
}).
catchError
((
error
)
{
_processLoginError
(
error
);
});
}
_loginToCubeChat
(
BuildContext
context
,
CubeUser
user
)
{
log
(
"_loginToCubeChat user
$user
"
);
CubeChatConnectionSettings
.
instance
.
totalReconnections
=
0
;
CubeChatConnection
.
instance
.
login
(
user
).
then
((
cubeUser
)
{
_isLoginContinues
=
false
;
_goDialogScreen
(
context
,
cubeUser
);
}).
catchError
((
error
)
{
_processLoginError
(
error
);
});
}
void
_processLoginError
(
exception
)
{
log
(
"Login error
$exception
"
,
TAG
);
setState
(()
{
_isLoginContinues
=
false
;
});
showDialogError
(
exception
,
context
);
}
void
_goDialogScreen
(
BuildContext
context
,
CubeUser
cubeUser
)
async
{
log
(
"_goDialogScreen"
);
FlutterLocalNotificationsPlugin
()
.
getNotificationAppLaunchDetails
()
.
then
((
details
)
{
log
(
"getNotificationAppLaunchDetails"
);
String
?
payload
=
details
!.
notificationResponse
?.
payload
;
log
(
"getNotificationAppLaunchDetails, payload:
$payload
"
);
var
dialogId
;
if
(
payload
==
null
)
{
dialogId
=
SharedPrefs
.
instance
.
getSelectedDialogId
();
log
(
"getNotificationAppLaunchDetails, selectedDialogId:
$dialogId
"
);
}
else
{
Map
<
String
,
dynamic
>
payloadObject
=
jsonDecode
(
payload
);
dialogId
=
payloadObject
[
'dialog_id'
];
}
if
(
dialogId
!=
null
&&
dialogId
.
isNotEmpty
)
{
getDialogs
({
'id'
:
dialogId
}).
then
((
dialogs
)
{
if
(
dialogs
?.
items
!=
null
&&
dialogs
!.
items
.
isNotEmpty
)
{
CubeDialog
dialog
=
dialogs
.
items
.
first
;
navigateToNextScreen
(
cubeUser
,
dialog
);
}
else
{
navigateToNextScreen
(
cubeUser
,
null
);
}
}).
catchError
((
onError
)
{
navigateToNextScreen
(
cubeUser
,
null
);
});
}
else
{
navigateToNextScreen
(
cubeUser
,
null
);
}
}).
catchError
((
onError
)
{
log
(
"getNotificationAppLaunchDetails ERROR"
);
navigateToNextScreen
(
cubeUser
,
null
);
});
}
void
navigateToNextScreen
(
CubeUser
cubeUser
,
CubeDialog
?
dialog
)
{
SharedPrefs
.
instance
.
saveSelectedDialogId
(
''
);
Navigator
.
pushReplacementNamed
(
context
,
'select_dialog'
,
arguments:
{
USER_ARG_NAME:
cubeUser
,
DIALOG_ARG_NAME:
dialog
},
);
if
(
dialog
!=
null
&&
!
platformUtils
.
isDesktop
())
{
Navigator
.
pushNamed
(
context
,
'chat_dialog'
,
arguments:
{
USER_ARG_NAME:
cubeUser
,
DIALOG_ARG_NAME:
dialog
});
}
}
}
lib/srcchat/managers/chat_manager.dart
0 → 100644
View file @
5bd5df70
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
;
}
}
lib/srcchat/managers/push_notifications_manager.dart
0 → 100644
View file @
5bd5df70
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}
'
);
}
lib/srcchat/new_dialog_screen.dart
0 → 100644
View file @
5bd5df70
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"
);
}
}
lib/srcchat/new_group_dialog_screen.dart
0 → 100644
View file @
5bd5df70
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
);
});
}
}
}
lib/srcchat/phone_auth_flow.dart
0 → 100644
View file @
5bd5df70
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
();
}
}
}
lib/srcchat/select_dialog_screen.dart
0 → 100644
View file @
5bd5df70
import
'dart:async'
;
import
'package:collection/collection.dart'
show
IterableExtension
;
import
'package:flutter/material.dart'
;
import
'package:fluttertoast/fluttertoast.dart'
;
import
'package:intl/intl.dart'
;
import
'package:connectycube_sdk/connectycube_sdk.dart'
;
import
'package:vmeeting/service/routes/routes_name.dart'
;
import
'../src/constants/colors_const.dart'
;
import
'../src/utils/app_utils.dart'
;
import
'chat_dialog_screen.dart'
;
import
'create_dialog_flow.dart'
;
import
'managers/chat_manager.dart'
;
import
'settings_screen.dart'
;
import
'utils/api_utils.dart'
;
import
'utils/consts.dart'
;
import
'utils/platform_utils.dart'
;
import
'widgets/common.dart'
;
class
SelectDialogScreen
extends
StatelessWidget
{
final
Function
(
CubeDialog
)?
onDialogSelectedCallback
;
final
CubeDialog
?
selectedDialog
;
SelectDialogScreen
(
this
.
selectedDialog
,
this
.
onDialogSelectedCallback
);
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
appBar:
AppBar
(
automaticallyImplyLeading:
false
,
title:
Text
(
'Logged in as
${AppUtils.cubeUser.fullName ?? AppUtils.cubeUser.login ?? AppUtils.cubeUser.email}
'
,
),
actions:
<
Widget
>[
IconButton
(
onPressed:
()
=>
_openSettings
(
context
),
icon:
Icon
(
Icons
.
settings
,
color:
ColorConst
.
appBleckColor
,
),
),
],
),
body:
BodyLayout
(
AppUtils
.
cubeUser
,
selectedDialog
,
onDialogSelectedCallback
),
);
}
_openSettings
(
BuildContext
context
)
{
showModal
(
context:
context
,
child:
SettingsScreen
(
AppUtils
.
cubeUser
));
}
}
class
BodyLayout
extends
StatefulWidget
{
final
CubeUser
currentUser
;
final
Function
(
CubeDialog
)?
onDialogSelectedCallback
;
final
CubeDialog
?
selectedDialog
;
BodyLayout
(
this
.
currentUser
,
this
.
selectedDialog
,
this
.
onDialogSelectedCallback
,
{
super
.
key
});
@override
State
<
StatefulWidget
>
createState
()
{
return
_BodyLayoutState
(
currentUser
,
selectedDialog
,
onDialogSelectedCallback
);
}
}
class
_BodyLayoutState
extends
State
<
BodyLayout
>
{
static
const
String
TAG
=
"_BodyLayoutState"
;
final
CubeUser
currentUser
;
List
<
ListItem
<
CubeDialog
>>
dialogList
=
[];
var
_isDialogContinues
=
true
;
StreamSubscription
<
CubeMessage
>?
msgSubscription
;
StreamSubscription
<
MessageStatus
>?
msgDeliveringSubscription
;
StreamSubscription
<
MessageStatus
>?
msgReadingSubscription
;
StreamSubscription
<
MessageStatus
>?
msgLocalReadingSubscription
;
StreamSubscription
<
CubeMessage
>?
msgSendingSubscription
;
final
ChatMessagesManager
?
chatMessagesManager
=
CubeChatConnection
.
instance
.
chatMessagesManager
;
Function
(
CubeDialog
)?
onDialogSelectedCallback
;
CubeDialog
?
selectedDialog
;
Map
<
String
,
Set
<
String
>>
unreadMessages
=
{};
_BodyLayoutState
(
this
.
currentUser
,
this
.
selectedDialog
,
this
.
onDialogSelectedCallback
);
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
body:
Container
(
padding:
const
EdgeInsets
.
only
(
top:
2
),
child:
Column
(
children:
[
Visibility
(
visible:
_isDialogContinues
&&
dialogList
.
isEmpty
,
child:
Container
(
margin:
const
EdgeInsets
.
all
(
40
),
alignment:
FractionalOffset
.
center
,
child:
AppUtils
.
buildLoading
(),
),
),
Expanded
(
child:
_getDialogsList
(
context
),
),
],
),
),
floatingActionButton:
FloatingActionButton
(
heroTag:
"New dialog"
,
backgroundColor:
Colors
.
blue
,
onPressed:
()
=>
_createNewDialog
(
context
),
child:
const
Icon
(
Icons
.
add_comment
,
color:
Colors
.
white
,
),
),
);
}
void
_createNewDialog
(
BuildContext
context
)
async
{
showModal
(
context:
context
,
child:
CreateDialog
(
currentUser
));
}
void
_processGetDialogError
(
exception
)
{
log
(
"GetDialog error
$exception
"
,
TAG
);
setState
(()
{
_isDialogContinues
=
false
;
});
showDialogError
(
exception
,
context
);
}
Widget
_getDialogsList
(
BuildContext
context
)
{
if
(
_isDialogContinues
)
{
getDialogs
().
then
((
dialogs
)
{
_isDialogContinues
=
false
;
log
(
"getDialogs:
$dialogs
"
,
TAG
);
setState
(()
{
dialogList
.
clear
();
dialogList
.
addAll
(
dialogs
?.
items
.
map
((
dialog
)
=>
ListItem
(
dialog
)).
toList
()
??
[]);
});
}).
catchError
((
exception
)
{
_processGetDialogError
(
exception
);
});
}
if
(
_isDialogContinues
&&
dialogList
.
isEmpty
)
{
return
const
SizedBox
.
shrink
();
}
else
if
(
dialogList
.
isEmpty
)
return
const
Center
(
child:
Text
(
'No dialogs yet'
,
style:
TextStyle
(
fontSize:
20
),
),
);
else
return
ListView
.
separated
(
itemCount:
dialogList
.
length
,
itemBuilder:
_getListItemTile
,
separatorBuilder:
(
context
,
index
)
{
return
Divider
(
thickness:
1
,
indent:
68
,
height:
1
,
);
},
);
}
Widget
_getListItemTile
(
BuildContext
context
,
int
index
)
{
Widget
getDialogIcon
()
{
var
dialog
=
dialogList
[
index
].
data
;
if
(
dialog
.
type
==
CubeDialogType
.
PRIVATE
)
{
return
Icon
(
Icons
.
person
,
size:
40.0
,
color:
greyColor
,
);
}
else
{
return
Icon
(
Icons
.
group
,
size:
40.0
,
color:
greyColor
,
);
}
}
getDialogAvatar
()
{
var
dialog
=
dialogList
[
index
].
data
;
return
getDialogAvatarWidget
(
dialog
,
25
,
placeholder:
getDialogIcon
(),
errorWidget:
getDialogIcon
());
}
return
Container
(
color:
selectedDialog
!=
null
&&
selectedDialog
!.
dialogId
==
dialogList
[
index
].
data
.
dialogId
?
const
Color
.
fromARGB
(
100
,
168
,
228
,
160
)
:
null
,
padding:
const
EdgeInsets
.
symmetric
(
vertical:
5
,
horizontal:
10
),
child:
GestureDetector
(
behavior:
HitTestBehavior
.
translucent
,
child:
Row
(
children:
<
Widget
>[
getDialogAvatar
(),
Flexible
(
child:
Container
(
margin:
EdgeInsets
.
only
(
left:
8.0
),
child:
Column
(
children:
<
Widget
>[
Container
(
alignment:
Alignment
.
centerLeft
,
child:
Text
(
'
${dialogList[index].data.name ?? 'Unknown dialog'}
'
,
style:
TextStyle
(
color:
primaryColor
,
fontWeight:
FontWeight
.
bold
,
fontSize:
16.0
,
overflow:
TextOverflow
.
ellipsis
),
maxLines:
1
,
),
),
Container
(
alignment:
Alignment
.
centerLeft
,
child:
Text
(
dialogList
[
index
].
data
.
lastMessage
??
''
,
style:
TextStyle
(
color:
primaryColor
,
overflow:
TextOverflow
.
ellipsis
),
maxLines:
2
,
),
),
],
),
),
),
Visibility
(
maintainAnimation:
true
,
maintainState:
true
,
visible:
dialogList
[
index
].
isSelected
,
child:
IconButton
(
iconSize:
25.0
,
icon:
Icon
(
Icons
.
delete
,
color:
themeColor
,
),
onPressed:
()
{
_deleteDialog
(
context
,
dialogList
[
index
].
data
);
},
),
),
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
end
,
children:
[
Row
(
children:
[
getMessageStateWidget
(
dialogList
[
index
].
data
.
lastMessageState
),
Text
(
DateFormat
(
'MMM dd'
).
format
(
dialogList
[
index
].
data
.
lastMessageDateSent
!=
null
?
DateTime
.
fromMillisecondsSinceEpoch
(
dialogList
[
index
].
data
.
lastMessageDateSent
!
*
1000
)
:
dialogList
[
index
].
data
.
updatedAt
!),
style:
TextStyle
(
color:
primaryColor
),
),
],
),
if
(
dialogList
[
index
].
data
.
unreadMessageCount
!=
null
&&
dialogList
[
index
].
data
.
unreadMessageCount
!=
0
)
Padding
(
padding:
const
EdgeInsets
.
only
(
top:
4
),
child:
Container
(
padding:
const
EdgeInsets
.
symmetric
(
vertical:
2
,
horizontal:
6
),
decoration:
BoxDecoration
(
color:
Colors
.
green
,
borderRadius:
BorderRadius
.
circular
(
10.0
)),
child:
Text
(
dialogList
[
index
].
data
.
unreadMessageCount
.
toString
(),
style:
const
TextStyle
(
color:
Colors
.
white
),
),
),
),
],
),
],
),
onLongPress:
()
{
setState
(()
{
dialogList
[
index
].
isSelected
=
!
dialogList
[
index
].
isSelected
;
});
},
onTap:
()
{
_selectDialog
(
context
,
dialogList
[
index
].
data
);
},
),
);
}
void
_deleteDialog
(
BuildContext
context
,
CubeDialog
dialog
)
async
{
log
(
"_deleteDialog=
$dialog
"
);
Fluttertoast
.
showToast
(
msg:
'Coming soon'
);
}
void
_selectDialog
(
BuildContext
context
,
CubeDialog
dialog
)
async
{
if
(
onDialogSelectedCallback
!=
null
)
{
onDialogSelectedCallback
?.
call
(
dialog
);
setState
(()
{
selectedDialog
=
dialog
;
});
}
else
{
Navigator
.
push
(
context
,
MaterialPageRoute
(
builder:
(
context
)
=>
ChatDialogScreen
(
currentUser
,
dialog
)),
);
}
}
void
refresh
()
{
setState
(()
{
_isDialogContinues
=
true
;
});
}
@override
void
initState
()
{
super
.
initState
();
refreshBadgeCount
();
msgSubscription
=
chatMessagesManager
!.
chatMessagesStream
.
listen
(
onReceiveMessage
);
msgDeliveringSubscription
=
CubeChatConnection
.
instance
.
messagesStatusesManager
?.
deliveredStream
.
listen
(
onMessageDelivered
);
msgReadingSubscription
=
CubeChatConnection
.
instance
.
messagesStatusesManager
?.
readStream
.
listen
(
onMessageRead
);
msgLocalReadingSubscription
=
ChatManager
.
instance
.
readMessagesStream
.
listen
(
onMessageRead
);
msgSendingSubscription
=
ChatManager
.
instance
.
sentMessagesStream
.
listen
(
onReceiveMessage
);
}
@override
void
dispose
()
{
super
.
dispose
();
log
(
"dispose"
,
TAG
);
msgSubscription
?.
cancel
();
msgDeliveringSubscription
?.
cancel
();
msgReadingSubscription
?.
cancel
();
msgLocalReadingSubscription
?.
cancel
();
msgSendingSubscription
?.
cancel
();
}
void
onReceiveMessage
(
CubeMessage
message
)
{
log
(
"onReceiveMessage global message=
$message
"
);
updateDialog
(
message
);
}
updateDialog
(
CubeMessage
msg
)
{
refreshBadgeCount
();
ListItem
<
CubeDialog
>?
dialogItem
=
dialogList
.
firstWhereOrNull
((
dlg
)
=>
dlg
.
data
.
dialogId
==
msg
.
dialogId
);
if
(
dialogItem
==
null
)
return
;
setState
(()
{
dialogItem
.
data
.
lastMessage
=
msg
.
body
;
dialogItem
.
data
.
lastMessageId
=
msg
.
messageId
;
if
(
msg
.
senderId
!=
currentUser
.
id
)
{
dialogItem
.
data
.
unreadMessageCount
=
dialogItem
.
data
.
unreadMessageCount
==
null
?
1
:
dialogItem
.
data
.
unreadMessageCount
!
+
1
;
unreadMessages
[
msg
.
dialogId
!]
=
<
String
>[
...
unreadMessages
[
msg
.
dialogId
]
??
[],
msg
.
messageId
!
].
toSet
();
dialogItem
.
data
.
lastMessageState
=
null
;
}
else
{
dialogItem
.
data
.
lastMessageState
=
MessageState
.
sent
;
}
dialogItem
.
data
.
lastMessageDateSent
=
msg
.
dateSent
;
dialogList
.
sort
((
a
,
b
)
{
DateTime
dateA
;
if
(
a
.
data
.
lastMessageDateSent
!=
null
)
{
dateA
=
DateTime
.
fromMillisecondsSinceEpoch
(
a
.
data
.
lastMessageDateSent
!
*
1000
);
}
else
{
dateA
=
a
.
data
.
updatedAt
!;
}
DateTime
dateB
;
if
(
b
.
data
.
lastMessageDateSent
!=
null
)
{
dateB
=
DateTime
.
fromMillisecondsSinceEpoch
(
b
.
data
.
lastMessageDateSent
!
*
1000
);
}
else
{
dateB
=
b
.
data
.
updatedAt
!;
}
if
(
dateA
.
isAfter
(
dateB
))
{
return
-
1
;
}
else
if
(
dateA
.
isBefore
(
dateB
))
{
return
1
;
}
else
{
return
0
;
}
});
});
}
void
onMessageDelivered
(
MessageStatus
messageStatus
)
{
_updateLastMessageState
(
messageStatus
,
MessageState
.
delivered
);
}
void
onMessageRead
(
MessageStatus
messageStatus
)
{
_updateLastMessageState
(
messageStatus
,
MessageState
.
read
);
if
(
messageStatus
.
userId
==
currentUser
.
id
&&
unreadMessages
.
containsKey
(
messageStatus
.
dialogId
))
{
if
(
unreadMessages
[
messageStatus
.
dialogId
]
?.
remove
(
messageStatus
.
messageId
)
??
false
)
{
setState
(()
{
var
dialog
=
dialogList
.
firstWhereOrNull
(
(
dlg
)
=>
dlg
.
data
.
dialogId
==
messageStatus
.
dialogId
)
?.
data
;
if
(
dialog
==
null
)
return
;
dialog
.
unreadMessageCount
=
dialog
.
unreadMessageCount
==
null
||
dialog
.
unreadMessageCount
==
0
?
0
:
dialog
.
unreadMessageCount
!
-
1
;
});
}
}
}
void
_updateLastMessageState
(
MessageStatus
messageStatus
,
MessageState
state
)
{
var
dialog
=
dialogList
.
firstWhereOrNull
((
dlg
)
=>
dlg
.
data
.
dialogId
==
messageStatus
.
dialogId
)
?.
data
;
if
(
dialog
==
null
)
return
;
if
(
messageStatus
.
messageId
==
dialog
.
lastMessageId
&&
messageStatus
.
userId
!=
currentUser
.
id
)
{
if
(
dialog
.
lastMessageState
!=
state
)
{
setState
(()
{
dialog
.
lastMessageState
=
state
;
});
}
}
}
}
lib/srcchat/settings_screen.dart
0 → 100644
View file @
5bd5df70
import
'package:file_picker/file_picker.dart'
;
import
'package:firebase_auth/firebase_auth.dart'
;
import
'package:flutter/material.dart'
;
import
'package:fluttertoast/fluttertoast.dart'
;
import
'package:connectycube_sdk/connectycube_sdk.dart'
;
import
'../src/constants/colors_const.dart'
;
import
'../src/utils/app_utils.dart'
;
import
'managers/push_notifications_manager.dart'
;
import
'utils/api_utils.dart'
;
import
'utils/consts.dart'
;
import
'utils/pref_util.dart'
;
import
'widgets/common.dart'
;
class
SettingsScreen
extends
StatelessWidget
{
final
CubeUser
currentUser
;
SettingsScreen
(
this
.
currentUser
);
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
appBar:
AppBar
(
leading:
IconButton
(
icon:
Icon
(
Icons
.
close
,
color:
ColorConst
.
appBleckColor
),
onPressed:
()
{
Navigator
.
of
(
context
).
pop
();
},
),
automaticallyImplyLeading:
false
,
title:
const
Text
(
'Settings'
,
style:
TextStyle
(
fontWeight:
FontWeight
.
bold
),
),
centerTitle:
false
,
),
body:
BodyLayout
(
currentUser
),
resizeToAvoidBottomInset:
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
;
var
_isUsersContinues
=
false
;
String
?
_avatarUrl
=
""
;
final
TextEditingController
_loginFilter
=
new
TextEditingController
();
final
TextEditingController
_nameFilter
=
new
TextEditingController
();
final
TextEditingController
_emailFilter
=
new
TextEditingController
();
String
_login
=
""
;
String
_name
=
""
;
String
_email
=
""
;
_BodyLayoutState
(
this
.
currentUser
)
{
_loginFilter
.
addListener
(
_loginListen
);
_nameFilter
.
addListener
(
_nameListen
);
_emailFilter
.
addListener
(
_emailListen
);
_nameFilter
.
text
=
currentUser
.
fullName
??
''
;
_loginFilter
.
text
=
currentUser
.
login
??
''
;
_emailFilter
.
text
=
currentUser
.
email
??
''
;
}
void
_loginListen
()
{
if
(
_loginFilter
.
text
.
isEmpty
)
{
_login
=
""
;
}
else
{
_login
=
_loginFilter
.
text
.
trim
();
}
}
void
_nameListen
()
{
if
(
_nameFilter
.
text
.
isEmpty
)
{
_name
=
""
;
}
else
{
_name
=
_nameFilter
.
text
.
trim
();
}
}
void
_emailListen
()
{
if
(
_emailFilter
.
text
.
isEmpty
)
{
_email
=
""
;
}
else
{
_email
=
_emailFilter
.
text
.
trim
();
}
}
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
body:
SingleChildScrollView
(
child:
Center
(
child:
ConstrainedBox
(
constraints:
BoxConstraints
(
maxWidth:
400
),
child:
Container
(
alignment:
Alignment
.
center
,
padding:
EdgeInsets
.
all
(
60
),
child:
Column
(
children:
[
_buildAvatarFields
(),
_buildTextFields
(),
_buildButtons
(),
Container
(
margin:
const
EdgeInsets
.
only
(
left:
8
),
child:
Visibility
(
maintainSize:
false
,
maintainAnimation:
false
,
maintainState:
false
,
visible:
_isUsersContinues
,
child:
AppUtils
.
buttonLoader
),
),
],
),
),
),
),
),
);
}
Widget
_buildAvatarFields
()
{
Widget
avatarCircle
=
getUserAvatarWidget
(
currentUser
,
50
);
return
Stack
(
children:
<
Widget
>[
InkWell
(
splashColor:
greyColor2
,
borderRadius:
BorderRadius
.
circular
(
45
),
onTap:
()
=>
_chooseUserImage
(),
child:
avatarCircle
,
),
Positioned
(
top:
55.0
,
right:
35.0
,
child:
RawMaterialButton
(
onPressed:
()
{
_chooseUserImage
();
},
elevation:
2.0
,
fillColor:
Colors
.
white
,
padding:
const
EdgeInsets
.
all
(
5.0
),
shape:
const
CircleBorder
(),
child:
const
Icon
(
Icons
.
mode_edit
,
size:
20.0
,
),
),
),
],
);
}
_chooseUserImage
()
async
{
FilePickerResult
?
result
=
await
FilePicker
.
platform
.
pickFiles
(
type:
FileType
.
image
,
);
if
(
result
==
null
)
return
;
var
uploadImageFuture
=
getUploadingImageFuture
(
result
);
uploadImageFuture
.
then
((
cubeFile
)
{
_avatarUrl
=
cubeFile
.
getPublicUrl
();
setState
(()
{
currentUser
.
avatar
=
_avatarUrl
;
});
}).
catchError
((
exception
)
{
_processUpdateUserError
(
exception
);
});
}
Widget
_buildTextFields
()
{
return
Column
(
children:
<
Widget
>[
TextField
(
controller:
_nameFilter
,
decoration:
const
InputDecoration
(
labelText:
'Change name'
),
),
TextField
(
controller:
_loginFilter
,
decoration:
const
InputDecoration
(
labelText:
'Change login'
),
),
TextField
(
controller:
_emailFilter
,
decoration:
const
InputDecoration
(
labelText:
'Change e-mail'
),
),
],
);
}
Widget
_buildButtons
()
{
return
Column
(
children:
<
Widget
>[
const
SizedBox
(
height:
6
,
),
ElevatedButton
(
style:
OutlinedButton
.
styleFrom
(
backgroundColor:
ColorConst
.
appGreenColor
,
minimumSize:
const
Size
(
120
,
36
),
),
onPressed:
_updateUser
,
child:
const
Text
(
'Save'
),
),
const
SizedBox
(
height:
6
,
),
OutlinedButton
.
icon
(
style:
OutlinedButton
.
styleFrom
(
backgroundColor:
ColorConst
.
appMainColor
,
minimumSize:
const
Size
(
160
,
36
),
),
icon:
const
Icon
(
Icons
.
logout
,
),
label:
const
Text
(
'Logout'
),
onPressed:
_logout
,
),
const
SizedBox
(
height:
6
,
),
OutlinedButton
.
icon
(
style:
OutlinedButton
.
styleFrom
(
foregroundColor:
Colors
.
red
.
shade300
,
minimumSize:
const
Size
(
160
,
36
),
),
icon:
const
Icon
(
Icons
.
delete
,
color:
Colors
.
red
,
),
label:
const
Text
(
'Delete user'
,
style:
TextStyle
(
color:
Colors
.
red
),
),
onPressed:
_deleteUserPressed
,
),
],
);
}
void
_updateUser
()
{
print
(
'_updateUser user with login:
$_login
, name:
$_name
, e-mail:
$_email
'
);
if
(
_login
.
isEmpty
&&
_name
.
isEmpty
&&
_avatarUrl
!.
isEmpty
&&
_email
.
isEmpty
)
{
Fluttertoast
.
showToast
(
msg:
'Nothing to save'
);
return
;
}
var
userToUpdate
=
CubeUser
()..
id
=
currentUser
.
id
;
if
(
_name
.
isNotEmpty
)
userToUpdate
.
fullName
=
_name
;
if
(
_login
.
isNotEmpty
)
userToUpdate
.
login
=
_login
;
if
(
_email
.
isNotEmpty
)
userToUpdate
.
email
=
_email
;
if
(
_avatarUrl
!.
isNotEmpty
)
userToUpdate
.
avatar
=
_avatarUrl
;
setState
(()
{
_isUsersContinues
=
true
;
});
updateUser
(
userToUpdate
).
then
((
user
)
{
SharedPrefs
.
instance
.
updateUser
(
user
);
Fluttertoast
.
showToast
(
msg:
'Success'
);
setState
(()
{
_isUsersContinues
=
false
;
});
}).
catchError
((
exception
)
{
_processUpdateUserError
(
exception
);
});
}
void
_logout
()
{
print
(
'_logout
$_login
and
$_name
'
);
showDialog
(
context:
context
,
builder:
(
BuildContext
context
)
{
return
AlertDialog
(
title:
const
Text
(
"Logout"
),
content:
const
Text
(
"Are you sure you want logout current user"
),
actions:
<
Widget
>[
TextButton
(
child:
const
Text
(
"CANCEL"
),
onPressed:
()
{
Navigator
.
pop
(
context
);
},
),
TextButton
(
child:
const
Text
(
"OK"
),
onPressed:
()
{
signOut
().
then
(
(
voidValue
)
{
Navigator
.
pop
(
context
);
// cancel current Dialog
},
).
catchError
(
(
onError
)
{
Navigator
.
pop
(
context
);
// cancel current Dialog
},
).
whenComplete
(()
{
CubeChatConnection
.
instance
.
destroy
();
PushNotificationsManager
.
instance
.
unsubscribe
();
FirebaseAuth
.
instance
.
currentUser
?.
unlink
(
PhoneAuthProvider
.
PROVIDER_ID
);
SharedPrefs
.
instance
.
deleteUser
();
Navigator
.
pop
(
context
);
// cancel current screen
_navigateToLoginScreen
(
context
);
});
},
),
],
);
},
);
}
void
_deleteUserPressed
()
{
print
(
'_deleteUserPressed
${_login.isNotEmpty ? _login : _email}
'
);
_userDelete
();
}
void
_userDelete
()
{
showDialog
(
context:
context
,
builder:
(
BuildContext
context
)
{
return
AlertDialog
(
title:
Text
(
"Delete user"
),
content:
Text
(
"Are you sure you want to delete current user?"
),
actions:
<
Widget
>[
TextButton
(
child:
Text
(
"CANCEL"
),
onPressed:
()
{
Navigator
.
pop
(
context
);
},
),
TextButton
(
child:
Text
(
"OK"
),
onPressed:
()
async
{
CubeChatConnection
.
instance
.
destroy
();
await
SharedPrefs
.
instance
.
deleteUser
();
deleteUser
(
currentUser
.
id
!).
then
(
(
voidValue
)
{
Navigator
.
pop
(
context
);
// cancel current Dialog
},
).
catchError
(
(
onError
)
{
Navigator
.
pop
(
context
);
// cancel current Dialog
},
).
whenComplete
(()
async
{
await
PushNotificationsManager
.
instance
.
unsubscribe
();
Navigator
.
pop
(
context
);
// cancel current screen
_navigateToLoginScreen
(
context
);
});
},
),
],
);
},
);
}
_navigateToLoginScreen
(
BuildContext
context
)
{
Navigator
.
pushNamedAndRemoveUntil
(
context
,
'login'
,
(
route
)
=>
false
);
}
void
_processUpdateUserError
(
exception
)
{
log
(
"_processUpdateUserError error
$exception
"
,
TAG
);
setState
(()
{
_isUsersContinues
=
false
;
});
showDialogError
(
exception
,
context
);
}
}
lib/srcchat/update_dialog_flow.dart
0 → 100644
View file @
5bd5df70
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
;
},
);
}
}
lib/srcchat/utils/api_utils.dart
0 → 100644
View file @
5bd5df70
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
lib/srcchat/utils/auth_utils.dart
0 → 100644
View file @
5bd5df70
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
!;
});
});
}
lib/srcchat/utils/configs.dart
0 → 100644
View file @
5bd5df70
const
String
APP_ID
=
"476"
;
const
String
AUTH_KEY
=
"PDZjPBzAO8WPfCp"
;
const
String
AUTH_SECRET
=
"6247kjxXCLRaua6"
;
lib/srcchat/utils/consts.dart
0 → 100644
View file @
5bd5df70
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
}
lib/srcchat/utils/platform_utils.dart
0 → 100644
View file @
5bd5df70
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
();
lib/srcchat/utils/pref_util.dart
0 → 100644
View file @
5bd5df70
import
'dart:async'
;
import
'package:collection/collection.dart'
;
import
'package:flutter/foundation.dart'
;
import
'package:shared_preferences/shared_preferences.dart'
;
import
'package:connectycube_sdk/connectycube_chat.dart'
;
import
'consts.dart'
;
const
String
prefLoginType
=
"pref_login_type"
;
const
String
prefUserLogin
=
"pref_user_login"
;
const
String
prefUserEmail
=
"pref_user_email"
;
const
String
prefUserPhone
=
"pref_user_phone"
;
const
String
prefUserPsw
=
"pref_user_psw"
;
const
String
prefUserName
=
"pref_user_name"
;
const
String
prefUserId
=
"pref_user_id"
;
const
String
prefUserAvatar
=
"pref_user_avatar"
;
const
String
prefSubscriptionToken
=
"pref_subscription_token"
;
const
String
prefSubscriptionId
=
"pref_subscription_id"
;
const
String
prefSelectedDialogId
=
"pref_selected_dialog_id"
;
class
SharedPrefs
{
static
final
SharedPrefs
_instance
=
SharedPrefs
.
_internal
();
late
SharedPreferences
prefs
;
SharedPrefs
.
_internal
();
bool
inited
=
false
;
static
SharedPrefs
get
instance
=>
_instance
;
Future
<
SharedPrefs
>
init
()
async
{
Completer
<
SharedPrefs
>
completer
=
Completer
();
if
(
inited
)
{
completer
.
complete
(
_instance
);
}
else
{
prefs
=
await
SharedPreferences
.
getInstance
();
inited
=
true
;
completer
.
complete
(
_instance
);
}
return
completer
.
future
;
}
saveNewUser
(
CubeUser
cubeUser
,
LoginType
loginType
)
{
prefs
.
clear
();
prefs
.
setString
(
prefLoginType
,
describeEnum
(
loginType
));
if
(
cubeUser
.
login
!=
null
)
prefs
.
setString
(
prefUserLogin
,
cubeUser
.
login
!);
if
(
cubeUser
.
email
!=
null
)
prefs
.
setString
(
prefUserEmail
,
cubeUser
.
email
!);
if
(
cubeUser
.
phone
!=
null
)
prefs
.
setString
(
prefUserPhone
,
cubeUser
.
phone
!);
if
(
cubeUser
.
password
!=
null
)
prefs
.
setString
(
prefUserPsw
,
cubeUser
.
password
!);
if
(
cubeUser
.
fullName
!=
null
)
prefs
.
setString
(
prefUserName
,
cubeUser
.
fullName
!);
prefs
.
setInt
(
prefUserId
,
cubeUser
.
id
!);
if
(
cubeUser
.
avatar
!=
null
)
prefs
.
setString
(
prefUserAvatar
,
cubeUser
.
avatar
!);
}
updateUser
(
CubeUser
cubeUser
)
{
if
(
cubeUser
.
password
!=
null
)
prefs
.
setString
(
prefUserPsw
,
cubeUser
.
password
!);
if
(
cubeUser
.
login
!=
null
)
prefs
.
setString
(
prefUserLogin
,
cubeUser
.
login
!);
if
(
cubeUser
.
email
!=
null
)
prefs
.
setString
(
prefUserEmail
,
cubeUser
.
email
!);
if
(
cubeUser
.
phone
!=
null
)
prefs
.
setString
(
prefUserPhone
,
cubeUser
.
phone
!);
if
(
cubeUser
.
fullName
!=
null
)
prefs
.
setString
(
prefUserName
,
cubeUser
.
fullName
!);
if
(
cubeUser
.
avatar
!=
null
)
prefs
.
setString
(
prefUserAvatar
,
cubeUser
.
avatar
!);
}
CubeUser
?
getUser
()
{
if
(
prefs
.
getString
(
prefUserLogin
)
==
null
&&
prefs
.
getString
(
prefUserEmail
)
==
null
)
return
null
;
var
user
=
CubeUser
();
user
.
login
=
prefs
.
getString
(
prefUserLogin
);
user
.
email
=
prefs
.
getString
(
prefUserEmail
);
user
.
phone
=
prefs
.
getString
(
prefUserPhone
);
user
.
password
=
prefs
.
getString
(
prefUserPsw
);
user
.
fullName
=
prefs
.
getString
(
prefUserName
);
user
.
id
=
prefs
.
getInt
(
prefUserId
);
user
.
avatar
=
prefs
.
getString
(
prefUserAvatar
);
return
user
;
}
LoginType
?
getLoginType
()
{
var
savedLoginType
=
prefs
.
getString
(
prefLoginType
);
if
(
savedLoginType
==
null
)
return
null
;
var
loginType
=
LoginType
.
values
.
firstWhereOrNull
((
e
)
=>
describeEnum
(
e
)
==
savedLoginType
);
return
loginType
;
}
saveLoginType
(
LoginType
loginType
)
{
prefs
.
setString
(
prefLoginType
,
describeEnum
(
loginType
));
}
Future
<
bool
>
deleteUser
()
{
return
prefs
.
clear
();
}
saveSubscriptionToken
(
String
token
)
{
prefs
.
setString
(
prefSubscriptionToken
,
token
);
}
String
getSubscriptionToken
()
{
return
prefs
.
getString
(
prefSubscriptionToken
)
??
""
;
}
saveSubscriptionId
(
int
id
)
{
prefs
.
setInt
(
prefSubscriptionId
,
id
);
}
int
getSubscriptionId
()
{
return
prefs
.
getInt
(
prefSubscriptionId
)
??
0
;
}
saveSelectedDialogId
(
String
dialogId
)
{
prefs
.
setString
(
prefSelectedDialogId
,
dialogId
);
}
String
?
getSelectedDialogId
()
{
return
prefs
.
getString
(
prefSelectedDialogId
);
}
}
lib/srcchat/utils/route_utils.dart
0 → 100644
View file @
5bd5df70
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
();
}
lib/srcchat/widgets/common.dart
0 → 100644
View file @
5bd5df70
import
'package:cached_network_image/cached_network_image.dart'
;
import
'package:connectycube_sdk/connectycube_chat.dart'
;
import
'package:flutter/material.dart'
;
import
'../utils/consts.dart'
;
Widget
getAvatarTextWidget
(
bool
condition
,
String
?
text
,
{
double
?
fontSize
})
{
if
(
condition
)
return
SizedBox
.
shrink
();
else
return
Text
(
isEmpty
(
text
)
?
'?'
:
text
!,
style:
TextStyle
(
fontSize:
fontSize
??
30
,
color:
Colors
.
green
),
);
}
Widget
getUserAvatarWidget
(
CubeUser
?
cubeUser
,
double
radius
,
{
Widget
?
placeholder
,
Widget
?
errorWidget
})
{
return
getAvatarWidget
(
cubeUser
?.
avatar
,
cubeUser
?.
fullName
,
radius
,
placeholder:
placeholder
,
errorWidget:
errorWidget
);
}
Widget
getDialogAvatarWidget
(
CubeDialog
?
cubeDialog
,
double
radius
,
{
Widget
?
placeholder
,
Widget
?
errorWidget
})
{
return
getAvatarWidget
(
cubeDialog
?.
photo
,
cubeDialog
?.
name
,
radius
,
placeholder:
placeholder
,
errorWidget:
errorWidget
);
}
Widget
getAvatarWidget
(
String
?
imageUrl
,
String
?
name
,
double
radius
,
{
Widget
?
placeholder
,
Widget
?
errorWidget
})
{
return
CircleAvatar
(
backgroundColor:
Color
.
fromARGB
(
20
,
100
,
100
,
100
),
radius:
radius
,
child:
ClipRRect
(
borderRadius:
BorderRadius
.
circular
(
radius
),
child:
CachedNetworkImage
(
imageUrl:
imageUrl
??
''
,
errorWidget:
(
context
,
url
,
error
)
{
return
errorWidget
??
placeholder
??
Center
(
child:
Container
(
child:
getAvatarTextWidget
(
false
,
name
?.
substring
(
0
,
2
).
toUpperCase
()
??
'?'
,
fontSize:
radius
)));
},
placeholder:
(
context
,
url
)
{
return
placeholder
??
Center
(
child:
Container
(
child:
getAvatarTextWidget
(
false
,
name
?.
substring
(
0
,
2
).
toUpperCase
()
??
'?'
,
fontSize:
radius
)));
},
fit:
BoxFit
.
cover
,
width:
radius
*
2
,
height:
radius
*
2
,
),
),
);
}
Widget
getMessageStateWidget
(
MessageState
?
state
)
{
var
result
;
switch
(
state
)
{
case
MessageState
.
read
:
result
=
Stack
(
children:
<
Widget
>[
Icon
(
Icons
.
check_rounded
,
size:
15.0
,
color:
Colors
.
green
,
),
Padding
(
padding:
EdgeInsets
.
only
(
left:
4
,),
child:
Icon
(
Icons
.
check_rounded
,
size:
15.0
,
color:
Colors
.
green
,
),
)
]);
break
;
case
MessageState
.
delivered
:
result
=
Stack
(
children:
<
Widget
>[
Icon
(
Icons
.
check_rounded
,
size:
15.0
,
color:
greyColor
,
),
Padding
(
padding:
EdgeInsets
.
only
(
left:
4
),
child:
Icon
(
Icons
.
check_rounded
,
size:
15.0
,
color:
greyColor
,
),
)
]);
break
;
case
MessageState
.
sent
:
result
=
Icon
(
Icons
.
check_rounded
,
size:
15.0
,
color:
greyColor
,
);
break
;
default
:
result
=
SizedBox
.
shrink
();
break
;
}
return
result
;
}
lib/srcchat/widgets/full_photo.dart
0 → 100644
View file @
5bd5df70
import
'package:cached_network_image/cached_network_image.dart'
;
import
'package:flutter/material.dart'
;
import
'package:photo_view/photo_view.dart'
;
class
FullPhoto
extends
StatelessWidget
{
final
String
url
;
FullPhoto
({
Key
?
key
,
required
this
.
url
})
:
super
(
key:
key
);
@override
Widget
build
(
BuildContext
context
)
{
return
Scaffold
(
appBar:
AppBar
(
title:
Text
(
'Full photo'
,
),
centerTitle:
true
,
),
body:
FullPhotoScreen
(
url:
url
),
);
}
}
class
FullPhotoScreen
extends
StatefulWidget
{
final
String
url
;
FullPhotoScreen
({
Key
?
key
,
required
this
.
url
})
:
super
(
key:
key
);
@override
State
createState
()
=>
FullPhotoScreenState
(
url:
url
);
}
class
FullPhotoScreenState
extends
State
<
FullPhotoScreen
>
{
final
String
url
;
FullPhotoScreenState
({
Key
?
key
,
required
this
.
url
});
@override
void
initState
()
{
super
.
initState
();
}
@override
Widget
build
(
BuildContext
context
)
{
return
Container
(
child:
PhotoView
(
imageProvider:
NetworkImage
(
url
)));
}
}
lib/srcchat/widgets/loading.dart
0 → 100644
View file @
5bd5df70
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
),
);
}
}
lib/views/main_view/main_page.dart
View file @
5bd5df70
...
...
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import
'package:flutter_bloc/flutter_bloc.dart'
;
import
'package:vmeeting/blocs/home_page_bloc/home_page_bloc.dart'
;
import
'package:vmeeting/src/controllers/enter_number_cont.dart'
;
import
'package:vmeeting/src/utils/app_utils.dart'
;
import
'package:vmeeting/views/home_view/home_page.dart'
;
import
'package:vmeeting/views/profile_view/profile_page.dart'
;
import
'package:vmeeting/views/users_view/users_page.dart'
;
...
...
@@ -11,6 +12,7 @@ import '../../src/constants/colors_const.dart';
import
'../../src/utils/pref_util.dart'
;
import
'../../src/widgets/appbar_animation_widgets/icon_painters/category_icon_painter.dart'
;
import
'../../src/widgets/appbar_animation_widgets/sliver_scaffold.dart'
;
import
'../../srcchat/select_dialog_screen.dart'
;
import
'../drower_view/drower_menue.dart'
;
class
MainPage
extends
StatefulWidget
{
...
...
@@ -32,7 +34,7 @@ class _MainPageState extends State<MainPage> {
],
child:
HomePage
(
controller:
widget
.
controller
),
),
AllUsersViewPage
(
controller:
widget
.
controller
),
SelectDialogScreen
(
null
,
null
),
ProfilePage
(
controller:
widget
.
controller
)
];
...
...
@@ -73,7 +75,7 @@ class _MainPageState extends State<MainPage> {
onTap:
OnTapped
,
items:
const
<
Widget
>[
Icon
(
Icons
.
home
),
Icon
(
Icons
.
search
),
Icon
(
Icons
.
chat
),
Icon
(
Icons
.
person
)
],
),
...
...
linux/flutter/generated_plugin_registrant.cc
View file @
5bd5df70
...
...
@@ -6,11 +6,19 @@
#include "generated_plugin_registrant.h"
#include <desktop_webview_auth/desktop_webview_auth_plugin.h>
#include <emoji_picker_flutter/emoji_picker_flutter_plugin.h>
#include <file_selector_linux/file_selector_plugin.h>
#include <flutter_webrtc/flutter_web_r_t_c_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void
fl_register_plugins
(
FlPluginRegistry
*
registry
)
{
g_autoptr
(
FlPluginRegistrar
)
desktop_webview_auth_registrar
=
fl_plugin_registry_get_registrar_for_plugin
(
registry
,
"DesktopWebviewAuthPlugin"
);
desktop_webview_auth_plugin_register_with_registrar
(
desktop_webview_auth_registrar
);
g_autoptr
(
FlPluginRegistrar
)
emoji_picker_flutter_registrar
=
fl_plugin_registry_get_registrar_for_plugin
(
registry
,
"EmojiPickerFlutterPlugin"
);
emoji_picker_flutter_plugin_register_with_registrar
(
emoji_picker_flutter_registrar
);
g_autoptr
(
FlPluginRegistrar
)
file_selector_linux_registrar
=
fl_plugin_registry_get_registrar_for_plugin
(
registry
,
"FileSelectorPlugin"
);
file_selector_plugin_register_with_registrar
(
file_selector_linux_registrar
);
...
...
linux/flutter/generated_plugins.cmake
View file @
5bd5df70
...
...
@@ -3,6 +3,8 @@
#
list
(
APPEND FLUTTER_PLUGIN_LIST
desktop_webview_auth
emoji_picker_flutter
file_selector_linux
flutter_webrtc
url_launcher_linux
...
...
macos/Flutter/GeneratedPluginRegistrant.swift
View file @
5bd5df70
...
...
@@ -5,22 +5,38 @@
import
FlutterMacOS
import
Foundation
import
connectivity_plus
import
desktop_webview_auth
import
device_info_plus
import
emoji_picker_flutter
import
file_selector_macos
import
firebase_auth
import
firebase_core
import
firebase_messaging
import
flutter_app_badger
import
flutter_local_notifications
import
flutter_webrtc
import
package_info_plus
import
path_provider_foundation
import
shared_preferences_foundation
import
sqflite
import
url_launcher_macos
func
RegisterGeneratedPlugins
(
registry
:
FlutterPluginRegistry
)
{
ConnectivityPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"ConnectivityPlugin"
))
DesktopWebviewAuthPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"DesktopWebviewAuthPlugin"
))
DeviceInfoPlusMacosPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"DeviceInfoPlusMacosPlugin"
))
EmojiPickerFlutterPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"EmojiPickerFlutterPlugin"
))
FileSelectorPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"FileSelectorPlugin"
))
FLTFirebaseAuthPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"FLTFirebaseAuthPlugin"
))
FLTFirebaseCorePlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"FLTFirebaseCorePlugin"
))
FLTFirebaseMessagingPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"FLTFirebaseMessagingPlugin"
))
FlutterAppBadgerPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"FlutterAppBadgerPlugin"
))
FlutterLocalNotificationsPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"FlutterLocalNotificationsPlugin"
))
FlutterWebRTCPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"FlutterWebRTCPlugin"
))
FPPPackageInfoPlusPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"FPPPackageInfoPlusPlugin"
))
PathProviderPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"PathProviderPlugin"
))
SharedPreferencesPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"SharedPreferencesPlugin"
))
SqflitePlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"SqflitePlugin"
))
UrlLauncherPlugin
.
register
(
with
:
registry
.
registrar
(
forPlugin
:
"UrlLauncherPlugin"
))
}
pubspec.lock
View file @
5bd5df70
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_flutterfire_internals:
dependency: transitive
description:
name: _flutterfire_internals
sha256: "5dadadeecceac19d6a63c9d2e037bb8df58ddd4aedb94e8a056af2f39ee50f9d"
url: "https://pub.dev"
source: hosted
version: "1.3.11"
archive:
dependency: transitive
description:
...
...
@@ -9,6 +17,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.4.6"
args:
dependency: transitive
description:
name: args
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596
url: "https://pub.dev"
source: hosted
version: "2.4.2"
async:
dependency: transitive
description:
...
...
@@ -33,6 +49,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
cached_network_image:
dependency: "direct main"
description:
name: cached_network_image
sha256: f98972704692ba679db144261172a8e20feb145636c617af0eb4022132a6797f
url: "https://pub.dev"
source: hosted
version: "3.3.0"
cached_network_image_platform_interface:
dependency: transitive
description:
name: cached_network_image_platform_interface
sha256: "56aa42a7a01e3c9db8456d9f3f999931f1e05535b5a424271e9a38cabf066613"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
cached_network_image_web:
dependency: transitive
description:
name: cached_network_image_web
sha256: "759b9a9f8f6ccbb66c185df805fac107f05730b1dab9c64626d1008cca532257"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
characters:
dependency: transitive
description:
...
...
@@ -58,13 +98,29 @@ packages:
source: hosted
version: "1.1.1"
collection:
dependency:
transitive
dependency:
"direct main"
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
source: hosted
version: "1.18.0"
connectivity_plus:
dependency: "direct main"
description:
name: connectivity_plus
sha256: "77a180d6938f78ca7d2382d2240eb626c0f6a735d0bfdce227d8ffb80f95c48b"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a
url: "https://pub.dev"
source: hosted
version: "1.2.4"
connectycube_flutter_call_kit:
dependency: "direct main"
description:
...
...
@@ -153,6 +209,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.3"
dbus:
dependency: transitive
description:
name: dbus
sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
url: "https://pub.dev"
source: hosted
version: "0.7.10"
desktop_webview_auth:
dependency: transitive
description:
name: desktop_webview_auth
sha256: "328533e6b8f5ad72b890a49d1d1df0fc827b319b0da30c7612bdb284ebe448d2"
url: "https://pub.dev"
source: hosted
version: "0.0.14"
device_info_plus:
dependency: "direct main"
description:
...
...
@@ -169,6 +241,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.0"
email_validator:
dependency: transitive
description:
name: email_validator
sha256: e9a90f27ab2b915a27d7f9c2a7ddda5dd752d6942616ee83529b686fc086221b
url: "https://pub.dev"
source: hosted
version: "2.1.17"
emoji_picker_flutter:
dependency: "direct main"
description:
name: emoji_picker_flutter
sha256: "009c51efc763d5a6ba05a5628b8b2184c327cd117d66ea9c3e7edf2ff269c423"
url: "https://pub.dev"
source: hosted
version: "1.6.3"
equatable:
dependency: "direct main"
description:
...
...
@@ -201,6 +289,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.0"
file_picker:
dependency: "direct main"
description:
name: file_picker
sha256: be325344c1f3070354a1d84a231a1ba75ea85d413774ec4bdf444c023342e030
url: "https://pub.dev"
source: hosted
version: "5.5.0"
file_selector_linux:
dependency: transitive
description:
...
...
@@ -233,6 +329,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.9.3+1"
firebase_auth:
dependency: "direct main"
description:
name: firebase_auth
sha256: "738c4225bf8e766750423abcaeab1dc45f1bdb8975e2b32b69d561350b124685"
url: "https://pub.dev"
source: hosted
version: "4.12.1"
firebase_auth_platform_interface:
dependency: transitive
description:
name: firebase_auth_platform_interface
sha256: "40f759021591e3ce5741e981ae7488903dd31cab59aa19a663a8255511d58f95"
url: "https://pub.dev"
source: hosted
version: "7.0.3"
firebase_auth_web:
dependency: transitive
description:
name: firebase_auth_web
sha256: e068327d62503c32e2b3fbcc27991f6d8a687e2fdb60e9ad865835208b2a29ed
url: "https://pub.dev"
source: hosted
version: "5.8.6"
firebase_core:
dependency: "direct main"
description:
...
...
@@ -257,11 +377,91 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.8.1"
firebase_dynamic_links:
dependency: transitive
description:
name: firebase_dynamic_links
sha256: "3d7d8f922f91949b4ea4648fe278b7397c50f81557db7101c24bb52896972565"
url: "https://pub.dev"
source: hosted
version: "5.4.3"
firebase_dynamic_links_platform_interface:
dependency: transitive
description:
name: firebase_dynamic_links_platform_interface
sha256: "7a924375c503830007d182d9b70fe9bc8e9b2ed72cda3ddf74a964ba55179919"
url: "https://pub.dev"
source: hosted
version: "0.2.6+11"
firebase_messaging:
dependency: "direct main"
description:
name: firebase_messaging
sha256: "53952a6f7860c44429bec80719c411e0ff77ce6cf31fade1515c7bdd87abe4a1"
url: "https://pub.dev"
source: hosted
version: "14.7.3"
firebase_messaging_platform_interface:
dependency: transitive
description:
name: firebase_messaging_platform_interface
sha256: "543390d1c76aaf3fa563de223d1732a5def5a5efe31428e43a44e9a47efc5ed3"
url: "https://pub.dev"
source: hosted
version: "4.5.12"
firebase_messaging_web:
dependency: transitive
description:
name: firebase_messaging_web
sha256: ecfe4e851652dc5f40f1e42efcac0c2bc6bf5ef08faca936c94e1472c247d222
url: "https://pub.dev"
source: hosted
version: "3.5.12"
firebase_ui_auth:
dependency: "direct main"
description:
name: firebase_ui_auth
sha256: c3ec2926f4e0e56974e628e0ace10273e5615a4ae62d281e6f3fd68012406224
url: "https://pub.dev"
source: hosted
version: "1.10.0"
firebase_ui_localizations:
dependency: transitive
description:
name: firebase_ui_localizations
sha256: "3f2548115b5f33fabc5cc47bee2f1d1d11a049381fbd64a8ecdd04fda6564319"
url: "https://pub.dev"
source: hosted
version: "1.8.0"
firebase_ui_oauth:
dependency: transitive
description:
name: firebase_ui_oauth
sha256: "3fe9ea5f870ec26fda06f72e79ca0693310b746f18815df88dfb510c653f8606"
url: "https://pub.dev"
source: hosted
version: "1.4.14"
firebase_ui_shared:
dependency: transitive
description:
name: firebase_ui_shared
sha256: f1d07c130a39104d32fba1dab274b7bcb13be2bf4e652624a4ccabb58f9781f1
url: "https://pub.dev"
source: hosted
version: "1.4.1"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_app_badger:
dependency: "direct main"
description:
name: flutter_app_badger
sha256: "64d4a279bab862ed28850431b9b446b9820aaae0bf363322d51077419f930fa8"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
flutter_background:
dependency: "direct main"
description:
...
...
@@ -278,6 +478,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "8.1.3"
flutter_cache_manager:
dependency: transitive
description:
name: flutter_cache_manager
sha256: "8207f27539deb83732fdda03e259349046a39a4c767269285f449ade355d54ba"
url: "https://pub.dev"
source: hosted
version: "3.3.1"
flutter_lints:
dependency: "direct dev"
description:
...
...
@@ -286,6 +494,35 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.3"
flutter_local_notifications:
dependency: "direct main"
description:
name: flutter_local_notifications
sha256: "53c332ecee8e4d723269c1c2d0cdf7cbbff0a66cc0554d230a6f38cae81760d1"
url: "https://pub.dev"
source: hosted
version: "14.1.4"
flutter_local_notifications_linux:
dependency: transitive
description:
name: flutter_local_notifications_linux
sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03"
url: "https://pub.dev"
source: hosted
version: "4.0.0+1"
flutter_local_notifications_platform_interface:
dependency: transitive
description:
name: flutter_local_notifications_platform_interface
sha256: "7cf643d6d5022f3baed0be777b0662cce5919c0a7b86e700299f22dc4ae660ef"
url: "https://pub.dev"
source: hosted
version: "7.0.0+1"
flutter_localizations:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
...
...
@@ -302,6 +539,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.2.0"
flutter_svg:
dependency: transitive
description:
name: flutter_svg
sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c
url: "https://pub.dev"
source: hosted
version: "2.0.9"
flutter_test:
dependency: "direct dev"
description: flutter
...
...
@@ -320,6 +565,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.9.45"
fluttertoast:
dependency: "direct main"
description:
name: fluttertoast
sha256: dfdde255317af381bfc1c486ed968d5a43a2ded9c931e87cbecd88767d6a71c1
url: "https://pub.dev"
source: hosted
version: "8.2.4"
google_fonts:
dependency: "direct main"
description:
...
...
@@ -448,6 +701,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
logging:
dependency: "direct main"
description:
name: logging
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
lottie:
dependency: "direct main"
description:
...
...
@@ -488,6 +749,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.4"
modal_bottom_sheet:
dependency: "direct main"
description:
name: modal_bottom_sheet
sha256: "3bba63c62d35c931bce7f8ae23a47f9a05836d8cb3c11122ada64e0b2f3d718f"
url: "https://pub.dev"
source: hosted
version: "3.0.0-pre"
nested:
dependency: transitive
description:
...
...
@@ -496,6 +765,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
nm:
dependency: transitive
description:
name: nm
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
url: "https://pub.dev"
source: hosted
version: "0.5.0"
objectid:
dependency: transitive
description:
...
...
@@ -504,6 +781,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.0"
octo_image:
dependency: transitive
description:
name: octo_image
sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
os_detect:
dependency: transitive
description:
...
...
@@ -536,8 +821,16 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.8.3"
path_p
rovider
:
path_p
arsing
:
dependency: transitive
description:
name: path_parsing
sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf
url: "https://pub.dev"
source: hosted
version: "1.0.1"
path_provider:
dependency: "direct main"
description:
name: path_provider
sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa
...
...
@@ -632,6 +925,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.4.0"
photo_view:
dependency: "direct main"
description:
name: photo_view
sha256: "8036802a00bae2a78fc197af8a158e3e2f7b500561ed23b4c458107685e645bb"
url: "https://pub.dev"
source: hosted
version: "0.14.0"
platform:
dependency: transitive
description:
...
...
@@ -688,8 +989,16 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.2.1"
shared_preferences
:
rxdart
:
dependency: transitive
description:
name: rxdart
sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb"
url: "https://pub.dev"
source: hosted
version: "0.27.7"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
...
...
@@ -757,6 +1066,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.0"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: "591f1602816e9c31377d5f008c2d9ef7b8aca8941c3f89cc5fd9d84da0c38a9a"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: bb4738f15b23352822f4c42a531677e5c6f522e079461fd240ead29d8d8a54a6
url: "https://pub.dev"
source: hosted
version: "2.5.0+2"
stack_trace:
dependency: transitive
description:
...
...
@@ -805,6 +1130,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.6.1"
timezone:
dependency: transitive
description:
name: timezone
sha256: "1cfd8ddc2d1cfd836bc93e67b9be88c3adaeca6f40a00ca999104c30693cdca0"
url: "https://pub.dev"
source: hosted
version: "0.9.2"
tuple:
dependency: transitive
description:
...
...
@@ -918,13 +1251,37 @@ packages:
source: hosted
version: "3.1.0"
uuid:
dependency:
transitive
dependency:
"direct main"
description:
name: uuid
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
url: "https://pub.dev"
source: hosted
version: "3.0.7"
vector_graphics:
dependency: transitive
description:
name: vector_graphics
sha256: "0f0c746dd2d6254a0057218ff980fc7f5670fd0fcf5e4db38a490d31eed4ad43"
url: "https://pub.dev"
source: hosted
version: "1.1.9+1"
vector_graphics_codec:
dependency: transitive
description:
name: vector_graphics_codec
sha256: "0edf6d630d1bfd5589114138ed8fada3234deacc37966bec033d3047c29248b7"
url: "https://pub.dev"
source: hosted
version: "1.1.9+1"
vector_graphics_compiler:
dependency: transitive
description:
name: vector_graphics_compiler
sha256: d24333727332d9bd20990f1483af4e09abdb9b1fc7c3db940b56ab5c42790c26
url: "https://pub.dev"
source: hosted
version: "1.1.9+1"
vector_math:
dependency: transitive
description:
...
...
pubspec.yaml
View file @
5bd5df70
...
...
@@ -52,6 +52,23 @@ dependencies:
lottie
:
^2.4.0
curved_navigation_bar
:
^1.0.3
image_picker
:
^1.0.4
shared_preferences
:
^2.0.12
file_picker
:
^5.2.11
fluttertoast
:
^8.0.8
cached_network_image
:
^3.2.0
photo_view
:
^0.14.0
emoji_picker_flutter
:
^1.5.4
firebase_messaging
:
^14.6.3
flutter_local_notifications
:
^14.1.1
connectivity_plus
:
^4.0.0
logging
:
^1.0.2
collection
:
^1.17.1
path_provider
:
^2.0.8
uuid
:
^3.0.6
modal_bottom_sheet
:
^3.0.0-pre
firebase_ui_auth
:
^1.4.3
firebase_auth
:
^4.6.3
flutter_app_badger
:
^1.5.0
dev_dependencies
:
flutter_test
:
...
...
windows/flutter/generated_plugin_registrant.cc
View file @
5bd5df70
...
...
@@ -6,15 +6,27 @@
#include "generated_plugin_registrant.h"
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <desktop_webview_auth/desktop_webview_auth_plugin.h>
#include <emoji_picker_flutter/emoji_picker_flutter_plugin_c_api.h>
#include <file_selector_windows/file_selector_windows.h>
#include <firebase_auth/firebase_auth_plugin_c_api.h>
#include <firebase_core/firebase_core_plugin_c_api.h>
#include <flutter_webrtc/flutter_web_r_t_c_plugin.h>
#include <permission_handler_windows/permission_handler_windows_plugin.h>
#include <url_launcher_windows/url_launcher_windows.h>
void
RegisterPlugins
(
flutter
::
PluginRegistry
*
registry
)
{
ConnectivityPlusWindowsPluginRegisterWithRegistrar
(
registry
->
GetRegistrarForPlugin
(
"ConnectivityPlusWindowsPlugin"
));
DesktopWebviewAuthPluginRegisterWithRegistrar
(
registry
->
GetRegistrarForPlugin
(
"DesktopWebviewAuthPlugin"
));
EmojiPickerFlutterPluginCApiRegisterWithRegistrar
(
registry
->
GetRegistrarForPlugin
(
"EmojiPickerFlutterPluginCApi"
));
FileSelectorWindowsRegisterWithRegistrar
(
registry
->
GetRegistrarForPlugin
(
"FileSelectorWindows"
));
FirebaseAuthPluginCApiRegisterWithRegistrar
(
registry
->
GetRegistrarForPlugin
(
"FirebaseAuthPluginCApi"
));
FirebaseCorePluginCApiRegisterWithRegistrar
(
registry
->
GetRegistrarForPlugin
(
"FirebaseCorePluginCApi"
));
FlutterWebRTCPluginRegisterWithRegistrar
(
...
...
windows/flutter/generated_plugins.cmake
View file @
5bd5df70
...
...
@@ -3,7 +3,11 @@
#
list
(
APPEND FLUTTER_PLUGIN_LIST
connectivity_plus
desktop_webview_auth
emoji_picker_flutter
file_selector_windows
firebase_auth
firebase_core
flutter_webrtc
permission_handler_windows
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment