blob: a04b479b838a077d00f41079ebdd4df7a9ecace6 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:fidl_fuchsia_modular_auth/fidl_async.dart';
import 'package:flutter/material.dart';
import 'package:lib.widgets/model.dart';
import 'package:lib.widgets/widgets.dart';
import 'user_picker_base_shell_model.dart';
const double _kUserAvatarSizeLarge = 56.0;
const double _kUserAvatarSizeSmall = 48.0;
const double _kButtonWidthLarge = 128.0;
const double _kButtonWidthSmall = 116.0;
const double _kButtonFontSizeLarge = 16.0;
const double _kButtonFontSizeSmall = 14.0;
const TextStyle _kTextStyle = const TextStyle(
color: Colors.white,
fontSize: 10.0,
letterSpacing: 1.0,
fontWeight: FontWeight.w300,
);
final BorderRadius _kButtonBorderRadiusPhone =
new BorderRadius.circular(_kUserAvatarSizeSmall / 2.0);
final BorderRadius _kButtonBorderRadiusLarge =
new BorderRadius.circular(_kUserAvatarSizeLarge / 2.0);
/// Shows the list of users and allows the user to add new users
class UserList extends StatelessWidget {
/// True if login should be disabled.
final bool loginDisabled;
/// Constructor.
const UserList({this.loginDisabled = false});
Widget _buildUserCircle({
Account account,
VoidCallback onTap,
bool isSmall,
}) {
double size = isSmall ? _kUserAvatarSizeSmall : _kUserAvatarSizeLarge;
return new GestureDetector(
onTap: () => onTap?.call(),
child: new Container(
height: size,
width: size,
child: new Alphatar.fromNameAndUrl(
name: account.displayName,
avatarUrl: _getImageUrl(account),
size: size,
),
),
);
}
Widget _buildIconButton({
VoidCallback onTap,
bool isSmall,
IconData icon,
}) {
double size = isSmall ? _kUserAvatarSizeSmall : _kUserAvatarSizeLarge;
return _buildUserActionButton(
onTap: () => onTap?.call(),
width: size,
isSmall: isSmall,
child: new Center(
child: new Icon(
icon,
color: Colors.white,
size: size / 2.0,
),
),
);
}
Widget _buildUserActionButton({
Widget child,
VoidCallback onTap,
bool isSmall,
double width,
bool isDisabled = false,
}) {
return new GestureDetector(
onTap: isDisabled ? null : () => onTap?.call(),
child: new Container(
height: isSmall ? _kUserAvatarSizeSmall : _kUserAvatarSizeLarge,
width: width ?? (isSmall ? _kButtonWidthSmall : _kButtonWidthLarge),
alignment: FractionalOffset.center,
margin: const EdgeInsets.only(left: 16.0),
decoration: new BoxDecoration(
borderRadius:
isSmall ? _kButtonBorderRadiusPhone : _kButtonBorderRadiusLarge,
border: new Border.all(
color: isDisabled ? Colors.grey : Colors.white,
width: 1.0,
),
),
child: child,
),
);
}
Widget _buildExpandedUserActions({
UserPickerBaseShellModel model,
bool isSmall,
}) {
double fontSize = isSmall ? _kButtonFontSizeSmall : _kButtonFontSizeLarge;
if (loginDisabled) {
return new Row(
children: <Widget>[
_buildUserActionButton(
child: new Text(
'RESET',
style: new TextStyle(
fontSize: fontSize,
color: Colors.white,
),
),
onTap: model.resetTapped,
isSmall: isSmall,
),
_buildUserActionButton(
child: new Text(
'WIFI',
style: new TextStyle(
fontSize: fontSize,
color: Colors.white,
),
),
onTap: model.wifiTapped,
isSmall: isSmall,
),
_buildUserActionButton(
child: new Text(
'LOGIN DISABLED => No SessionShell configured',
style: new TextStyle(
fontSize: fontSize,
color: Colors.white,
),
),
onTap: () {},
isSmall: isSmall,
isDisabled: true,
),
],
);
}
return new Row(
children: <Widget>[
_buildIconButton(
onTap: () => model.hideUserActions(),
isSmall: isSmall,
icon: Icons.close,
),
_buildUserActionButton(
child: new Text(
'RESET',
style: new TextStyle(
fontSize: fontSize,
color: Colors.white,
),
),
onTap: model.resetTapped,
isSmall: isSmall,
),
_buildUserActionButton(
child: new Text(
'WIFI',
style: new TextStyle(
fontSize: fontSize,
color: Colors.white,
),
),
onTap: model.wifiTapped,
isSmall: isSmall,
),
_buildUserActionButton(
child: new Text(
'LOGIN',
style: new TextStyle(
fontSize: fontSize,
color: Colors.white,
),
),
onTap: () {
model
..createAndLoginUser()
..hideUserActions();
},
isSmall: isSmall,
),
_buildUserActionButton(
child: new Text(
'GUEST',
style: new TextStyle(
fontSize: fontSize,
color: Colors.white,
),
),
onTap: () {
model
..login(null)
..hideUserActions();
},
isSmall: isSmall,
),
],
);
}
String _getImageUrl(Account account) {
if (account.imageUrl == null) {
return null;
}
Uri uri = Uri.parse(account.imageUrl);
if (uri.queryParameters['sz'] != null) {
Map<String, dynamic> queryParameters = new Map<String, dynamic>.from(
uri.queryParameters,
);
queryParameters['sz'] = '160';
uri = uri.replace(queryParameters: queryParameters);
}
return uri.toString();
}
Widget _buildUserEntry({
Account account,
VoidCallback onTap,
bool removable = true,
bool isSmall,
UserPickerBaseShellModel model,
}) {
Widget userCard = _buildUserCircle(
account: account,
onTap: onTap,
isSmall: isSmall,
);
if (!removable) {
return userCard;
}
Widget userImage = new LongPressDraggable<Account>(
child: userCard,
feedback: userCard,
data: account,
childWhenDragging: new Opacity(opacity: 0.0, child: userCard),
feedbackOffset: Offset.zero,
dragAnchor: DragAnchor.child,
maxSimultaneousDrags: 1,
onDragStarted: () => model.addDraggedUser(account),
onDraggableCanceled: (_, __) => model.removeDraggedUser(account),
);
if (model.showingUserActions) {
return new Padding(
padding: const EdgeInsets.only(left: 16.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
new Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: new Text(
account.displayName,
textAlign: TextAlign.center,
maxLines: 1,
style: _kTextStyle,
),
),
userImage,
],
),
);
} else {
return new Padding(
padding: const EdgeInsets.only(left: 16.0),
child: userImage,
);
}
}
Widget _buildUserList(UserPickerBaseShellModel model) {
return new LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
List<Widget> children = <Widget>[];
bool isSmall =
constraints.maxWidth < 600.0 || constraints.maxHeight < 600.0;
if (model.showingUserActions) {
children.add(
new Align(
alignment: FractionalOffset.bottomCenter,
child: _buildExpandedUserActions(
model: model,
isSmall: isSmall,
),
),
);
} else {
children.add(
new Align(
alignment: FractionalOffset.bottomCenter,
child: _buildIconButton(
onTap: model.showUserActions,
isSmall: isSmall,
icon: Icons.add,
),
),
);
}
children.addAll(
model.accounts.map(
(Account account) => new Align(
alignment: FractionalOffset.bottomCenter,
child: _buildUserEntry(
account: account,
onTap: () {
model
..login(account.id)
..hideUserActions();
},
isSmall: isSmall,
model: model,
),
),
),
);
return new Container(
height: (isSmall ? _kUserAvatarSizeSmall : _kUserAvatarSizeLarge) +
24.0 +
(model.showingUserActions ? 24.0 : 0.0),
child: new AnimatedOpacity(
duration: const Duration(milliseconds: 250),
opacity: model.showingRemoveUserTarget ? 0.0 : 1.0,
child: new ListView(
padding: const EdgeInsets.only(
bottom: 24.0,
right: 24.0,
),
scrollDirection: Axis.horizontal,
reverse: true,
physics: const BouncingScrollPhysics(),
shrinkWrap: true,
children: children,
),
),
);
},
);
}
@override
Widget build(BuildContext context) =>
new ScopedModelDescendant<UserPickerBaseShellModel>(builder: (
BuildContext context,
Widget child,
UserPickerBaseShellModel model,
) {
if (model.showingLoadingSpinner) {
return new Stack(
fit: StackFit.passthrough,
children: <Widget>[
new Center(
child: new Container(
width: 64.0,
height: 64.0,
child: const FuchsiaSpinner(),
),
),
],
);
} else {
return new Stack(
fit: StackFit.passthrough,
children: <Widget>[
_buildUserList(model),
],
);
}
});
}