4
2
Fork 0

merge: merging pinned conversation updates to sse

feat/SSE-support-revamped-caching-implementation
Sudharshan S. 2019-08-31 15:04:56 +08:00
commit 12350d23c9
Signed by: sudharshan
GPG Key ID: C861C97AAF3D9559
13 changed files with 628 additions and 31 deletions

View File

@ -21,7 +21,7 @@ class ConversationBloc {
Observable<List<Conversation>> get pinnedConversations =>
_conversationsFetcher.stream.map((conversationList) => conversationList
.where((conversation) => conversation.pinned)
.where((conversation) => conversation.pinned ?? false)
.toList());
Observable<Conversation> getConversation(String id) =>

View File

@ -45,6 +45,19 @@ class ConversationApiProvider {
}
}
Future<void> pinConversation(String id) async {
final jwt = await loginManager.getToken();
try {
await globalHttpClient
.post("$baseUrlCore/user/conversation/$id/pin", headers: {
HttpHeaders.contentTypeHeader: " application/json",
HttpHeaders.authorizationHeader: "Bearer $jwt"
});
} catch (e) {
throw e;
}
}
Future<void> createConversationMember(
String conversationId, String userId) async {
final jwt = await loginManager.getToken();

View File

@ -1,33 +1,52 @@
import "package:flutter/material.dart";
import "../../widgets/image_avatar.dart";
import "../../../models/user_model.dart";
import "../../../models/conversation_model.dart";
import "../../../blocs/conversation_bloc.dart";
class ConversationInactiveView extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _ConversationInactiveViewState();
}
}
class _ConversationInactiveViewState extends State<ConversationInactiveView> {
@override
initState() {
super.initState();
conversationsBloc.fetchConversations();
}
class ConversationInactiveView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
child: Column(mainAxisSize: MainAxisSize.min, children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
ImageAvatar(
radius: 18,
padding: EdgeInsets.only(right: 5.0),
info: ImageAvatarInfo.fromUser(
User("1", "Isaac", "Tay", "+65 91043593", "", "", ""))),
ImageAvatar(
radius: 18,
padding: EdgeInsets.only(right: 5.0),
info: ImageAvatarInfo.fromUser(
User("1", "Isaac", "Tay", "+65 91043593", "", "", ""))),
ImageAvatar(
radius: 18,
padding: EdgeInsets.only(right: 5.0),
info: ImageAvatarInfo.fromUser(
User("1", "Isaac", "Tay", "+65 91043593", "", "", "")))
])
StreamBuilder(
stream: conversationsBloc.pinnedConversations,
builder: (context, AsyncSnapshot<List<Conversation>> snapshot) {
if (snapshot.hasData) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: (snapshot.data.length < 1)
? <Widget>[
Spacer(),
Text("No pinned conversations :("),
Spacer()
]
: snapshot.data
.map((conversation) => ImageAvatar(
radius: 18,
padding: EdgeInsets.only(right: 5.0),
info: ImageAvatarInfo.fromConversation(
conversation)))
.toList());
} else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Center(child: CircularProgressIndicator());
})
]));
}
}

View File

@ -9,6 +9,7 @@ import "../blocs/message_bloc.dart";
import "./conversation_tab/conversation_tab.dart";
import "./contact_tab/contact_tab.dart";
import './settings_tab/settings_tab.dart';
import "./bottom_bar/bottom_bar.dart";
class Home extends StatefulWidget {
@ -89,7 +90,7 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
children: <Widget>[
ContactTab(),
ConversationTab(),
Container(),
SettingsTab(),
]),
bottomNavigationBar:
Column(mainAxisSize: MainAxisSize.min, children: <Widget>[

View File

@ -0,0 +1,51 @@
import "package:flutter/material.dart";
import 'package:frontend_flutter/src/ui/settings_tab/widgets/data_usage_view.dart';
import 'package:frontend_flutter/src/ui/settings_tab/widgets/interface_view.dart';
import 'package:frontend_flutter/src/ui/settings_tab/widgets/notifications_view.dart';
import 'package:frontend_flutter/src/ui/settings_tab/widgets/privacy_security_view.dart';
import 'package:frontend_flutter/src/ui/widgets/top_bar.dart';
import "./widgets/home_view.dart";
class SettingsTab extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _SettingsTabState();
}
}
class _SettingsTabState extends State<SettingsTab> {
@override
Widget build(BuildContext context) {
return Navigator(
initialRoute: "settings/home",
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
switch (settings.name) {
case "settings/home":
builder = (BuildContext _) => HomeView();
break;
case "settings/interface":
builder = (BuildContext _) => InterfaceView();
break;
case "settings/notifications":
builder = (BuildContext _) => NotificationsView();
break;
case "settings/privacy_security":
builder = (BuildContext _) => PrivacySecurityView();
break;
case "settings/data_usage":
builder = (BuildContext _) => DataUsageView();
break;
// case "settings/new/groupinfo":
// final List<User> users = settings.arguments;
// builder = (BuildContext _) => NewGroupInfoView(users: users);
// break;
default:
throw Exception("Invalid route: ${settings.name}");
}
return MaterialPageRoute(builder: builder, settings: settings);
},
);
}
}

View File

@ -0,0 +1,29 @@
import "package:flutter/material.dart";
import "../../widgets/top_bar.dart";
import "../../widgets/list_button.dart";
class DataUsageView extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _DataUsageViewState();
}
}
class _DataUsageViewState extends State<DataUsageView> {
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
TopBar(
title: "Data Usage",
children: <Widget>[
BackButton(),
Spacer(),
],
),
Text('Data Usage View'),
],
);
}
}

View File

@ -0,0 +1,229 @@
import 'dart:async' show Future;
import "package:flutter/material.dart";
import 'package:frontend_flutter/src/ui/widgets/image_avatar.dart';
import "../../widgets/top_bar.dart";
import "../../widgets/list_button.dart";
class HomeView extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _HomeViewState();
}
}
class _HomeViewState extends State<HomeView> {
final _textFieldController = TextEditingController();
String name = 'Daniel Lim Hai';
String bio = 'Hey there, I am using Meep!';
@override
initState() {
super.initState();
}
@override
dispose() {
super.dispose();
_textFieldController.dispose();
}
@override
Widget build(BuildContext context) {
var _titleTheme = Theme.of(context)
.textTheme
.title
.copyWith(color: Theme.of(context).accentColor);
return Column(
children: <Widget>[
TopBar(
title: "Settings",
children: <Widget>[
Visibility(
child: BackButton(),
maintainSize: true,
maintainAnimation: true,
maintainState: true,
visible: false,
),
Spacer(),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Stack(
children: <Widget>[
Positioned(
child: ImageAvatar(
padding: EdgeInsets.all(20.0),
info: ImageAvatarInfo(lastName: 'Daniel'),
radius: 70.0,
),
),
Positioned(
top: 115.0,
left: 115.0,
child: FloatingActionButton(
backgroundColor: Theme.of(context).primaryColorDark,
onPressed: () => {},
mini: true,
child: Icon(
Icons.edit,
color: Colors.white,
),
),
),
],
),
],
),
Expanded(
child: ListView(
padding: EdgeInsets.only(top: 0.0),
children: <Widget>[
ListButton(
icon: Icons.person,
text: name,
subtitle: 'Name',
onClickCallback: () {
_displayTextFieldDialog(
context: context,
title: 'Edit Name',
existingText: name,
hintText: 'Name',
).then((text) {
//TODO: Logic for changing name
if (text != null) {
setState(() {
name = text;
print(text);
});
}
});
},
textStyle: _titleTheme,
iconColor: Theme.of(context).primaryColorDark,
),
ListButton(
icon: Icons.info,
text: bio,
subtitle: 'Bio',
onClickCallback: () {
_displayTextFieldDialog(
context: context,
title: 'Edit Bio',
existingText: bio,
hintText: 'Bio',
).then((text) {
//TODO: Logic for changing bio
if (text != null) {
setState(() {
bio = text;
print(text);
});
}
});
},
textStyle: _titleTheme,
iconColor: Theme.of(context).primaryColorDark,
),
Divider(),
ListButton(
icon: Icons.color_lens,
text: 'Interface',
onClickCallback: () {
Navigator.of(context).pushNamed("settings/interface");
},
textStyle: _titleTheme,
iconColor: Theme.of(context).primaryColorDark,
),
ListButton(
icon: Icons.notifications,
text: 'Notifications',
onClickCallback: () {
Navigator.of(context).pushNamed("settings/notifications");
},
textStyle: _titleTheme,
iconColor: Theme.of(context).primaryColorDark,
),
ListButton(
icon: Icons.security,
text: 'Privacy and Security',
onClickCallback: () {
Navigator.of(context).pushNamed("settings/privacy_security");
},
textStyle: _titleTheme,
iconColor: Theme.of(context).primaryColorDark,
),
ListButton(
icon: Icons.data_usage,
text: 'Data Usage',
onClickCallback: () {
Navigator.of(context).pushNamed("settings/data_usage");
},
textStyle: _titleTheme,
iconColor: Theme.of(context).primaryColorDark,
),
Divider(),
ListButton(
icon: Icons.exit_to_app,
text: 'Sign Out',
onClickCallback: () {},
textStyle: Theme.of(context)
.textTheme
.title
.copyWith(color: Colors.redAccent),
iconColor: Colors.redAccent,
),
],
),
),
],
);
}
_displayTextFieldDialog({
@required BuildContext context,
@required String title,
String existingText,
String hintText,
}) async {
_textFieldController.text = existingText;
// Introduce artificial delay so the button press animation can be seen
await new Future.delayed(const Duration(milliseconds: 220));
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(
title,
style: Theme.of(context).textTheme.title,
),
content: TextField(
controller: _textFieldController,
decoration: InputDecoration(hintText: hintText),
autofocus: true,
),
actions: <Widget>[
new FlatButton(
child: new Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
new FlatButton(
child: new Text('Submit'),
onPressed: () {
Navigator.of(context).pop(_textFieldController.text);
},
),
],
);
},
);
}
}

View File

@ -0,0 +1,67 @@
import "package:flutter/material.dart";
import 'package:frontend_flutter/src/ui/widgets/list_text_button.dart';
import "../../widgets/top_bar.dart";
import "../../widgets/list_button.dart";
class InterfaceView extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _InterfaceViewState();
}
}
class _InterfaceViewState extends State<InterfaceView> {
bool _enterToSend = false;
@override
Widget build(BuildContext context) {
var _buttonTextTheme = Theme.of(context)
.textTheme
.title
.copyWith(color: Theme.of(context).accentColor);
return Column(
children: <Widget>[
TopBar(
title: "Interface",
children: <Widget>[
BackButton(),
Spacer(),
],
),
Expanded(
child: ListView(
padding: EdgeInsets.only(top: 0.0),
children: <Widget>[
SwitchListTile(
title: Text('Enter to Send', style: _buttonTextTheme),
value: _enterToSend,
onChanged: (bool value) {
setState(() {
_enterToSend = value;
});
},
),
ListTextButton(
text: 'Font Size',
subtitle: 'Medium',
textStyle: _buttonTextTheme,
onClickCallback: () {
},
),
ListTextButton(
text: 'Wallpaper',
textStyle: _buttonTextTheme,
onClickCallback: () {
},
),
],
),
),
],
);
}
}

View File

@ -0,0 +1,66 @@
import "package:flutter/material.dart";
import 'package:frontend_flutter/src/ui/widgets/list_text_button.dart';
import "../../widgets/top_bar.dart";
class NotificationsView extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _NotificationsViewState();
}
}
class _NotificationsViewState extends State<NotificationsView> {
bool _inChatSounds = false;
@override
Widget build(BuildContext context) {
var _buttonTextTheme = Theme.of(context)
.textTheme
.title
.copyWith(color: Theme.of(context).accentColor);
return Column(
children: <Widget>[
TopBar(
title: "Notifications",
children: <Widget>[
BackButton(),
Spacer(),
],
),
Expanded(
child: ListView(
padding: EdgeInsets.only(top: 0.0),
children: <Widget>[
SwitchListTile(
title: Text('In-Chat Sounds', style: _buttonTextTheme),
value: _inChatSounds,
onChanged: (bool value) {
setState(() {
_inChatSounds = value;
});
},
),
ListTextButton(
text: 'Notification Sound',
subtitle: 'Default',
textStyle: _buttonTextTheme,
onClickCallback: () {
},
),
ListTextButton(
text: 'Vibrate',
subtitle: 'Default',
textStyle: _buttonTextTheme,
onClickCallback: () {
},
),
],
),
),
],
);
}
}

View File

@ -0,0 +1,29 @@
import "package:flutter/material.dart";
import "../../widgets/top_bar.dart";
import "../../widgets/list_button.dart";
class PrivacySecurityView extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _PrivacySecurityViewState();
}
}
class _PrivacySecurityViewState extends State<PrivacySecurityView> {
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
TopBar(
title: "Privacy and Security",
children: <Widget>[
BackButton(),
Spacer(),
],
),
Text('Privacy and Security View'),
],
);
}
}

View File

@ -142,7 +142,12 @@ class _ConversationItemState extends State<ConversationItem> {
// onTap: () => print('Pin'))],
secondaryActions: <Widget>[
IconSlideAction(
color: Colors.green, icon: Icons.star, onTap: () => print('Pin')),
color: Colors.green,
icon: Icons.star,
onTap: () async {
await conversationApiProvider
.pinConversation(widget.conversation.id);
}),
IconSlideAction(
color: Colors.red,
icon: Icons.delete,

View File

@ -6,11 +6,19 @@ class ListButton extends StatelessWidget {
final IconData icon;
final String text;
final OnClickCallback onClickCallback;
final String subtitle;
final TextStyle textStyle;
final TextStyle subtitleStyle;
final Color iconColor;
ListButton(
{@required this.icon,
@required this.text,
@required this.onClickCallback});
@required this.onClickCallback,
this.subtitle,
this.textStyle,
this.subtitleStyle,
this.iconColor});
@override
Widget build(BuildContext context) {
@ -27,12 +35,33 @@ class ListButton extends StatelessWidget {
children: <Widget>[
Icon(icon,
size: 30.0,
color: Theme.of(context).primaryColorDark),
color:
iconColor ?? Theme.of(context).primaryColorDark),
Padding(
padding: EdgeInsets.only(left: 20.0),
child: Text(text,
style: Theme.of(context).textTheme.title.copyWith(
color: Theme.of(context).primaryColorDark))),
padding: EdgeInsets.only(left: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
text,
style: textStyle ??
Theme.of(context).textTheme.title.copyWith(
color:
Theme.of(context).primaryColorDark),
),
(subtitle == null)
? Container()
: Padding(
padding: EdgeInsets.only(top: 3.0),
child: Text(
subtitle,
style: subtitleStyle ??
Theme.of(context).textTheme.subtitle,
),
),
],
),
),
]))));
}
}

View File

@ -0,0 +1,59 @@
import "package:flutter/material.dart";
typedef void OnClickCallback();
class ListTextButton extends StatelessWidget {
final String text;
final OnClickCallback onClickCallback;
final String subtitle;
final TextStyle textStyle;
final TextStyle subtitleStyle;
ListTextButton({
@required this.text,
@required this.onClickCallback,
this.subtitle,
this.textStyle,
this.subtitleStyle,
});
@override
Widget build(BuildContext context) {
return Material(
type: MaterialType.transparency,
elevation: 1,
child: InkWell(
onTap: onClickCallback,
child: Container(
height: (subtitle == null) ? 62.0 : null, // Default height if no subtitles
padding:
EdgeInsets.only(left: 15.0, right: 15.0, top: 12.0, bottom: 12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
text,
style: textStyle ??
Theme.of(context)
.textTheme
.title
.copyWith(color: Theme.of(context).primaryColorDark),
),
(subtitle == null)
? Container()
: Padding(
padding: EdgeInsets.only(top: 3.0),
child: Text(
subtitle,
style: subtitleStyle ??
Theme.of(context).textTheme.subtitle,
),
),
],
),
),
),
);
}
}