mirror of
https://github.com/ente-io/ente.git
synced 2025-07-13 18:38:43 +00:00
218 lines
6.7 KiB
Dart
218 lines
6.7 KiB
Dart
import "package:collection/collection.dart";
|
|
import 'package:flutter/material.dart';
|
|
import "package:logging/logging.dart";
|
|
import "package:photos/extensions/user_extension.dart";
|
|
import "package:photos/models/api/collection/user.dart";
|
|
import "package:photos/models/file/file.dart";
|
|
import "package:photos/services/machine_learning/face_ml/person/person_service.dart";
|
|
import "package:photos/theme/colors.dart";
|
|
import 'package:photos/theme/ente_theme.dart';
|
|
import "package:photos/ui/viewer/search/result/person_face_widget.dart";
|
|
import 'package:tuple/tuple.dart';
|
|
|
|
enum AvatarType { small, mini, tiny, extra }
|
|
|
|
class UserAvatarWidget extends StatefulWidget {
|
|
final User user;
|
|
final AvatarType type;
|
|
final int currentUserID;
|
|
final bool thumbnailView;
|
|
|
|
const UserAvatarWidget(
|
|
this.user, {
|
|
super.key,
|
|
this.currentUserID = -1,
|
|
this.type = AvatarType.mini,
|
|
this.thumbnailView = false,
|
|
});
|
|
|
|
@override
|
|
State<UserAvatarWidget> createState() => _UserAvatarWidgetState();
|
|
static const strokeWidth = 1.0;
|
|
}
|
|
|
|
class _UserAvatarWidgetState extends State<UserAvatarWidget> {
|
|
Future<String?>? _personID;
|
|
EnteFile? _faceThumbnail;
|
|
final _logger = Logger("_UserAvatarWidgetState");
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
if (PersonService
|
|
.instance.emailToPartialPersonDataMapCache[widget.user.email] !=
|
|
null) {
|
|
_personID = PersonService.instance.getPersons().then((people) async {
|
|
final person = people.firstWhereOrNull(
|
|
(person) => person.data.email == widget.user.email,
|
|
);
|
|
if (person != null) {
|
|
_faceThumbnail =
|
|
await PersonService.instance.getRecentFileOfPerson(person);
|
|
}
|
|
return person?.remoteID;
|
|
});
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final double size = getAvatarSize(widget.type);
|
|
|
|
return _personID != null
|
|
? Container(
|
|
padding: const EdgeInsets.all(0.5),
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
border: Border.all(
|
|
color: widget.thumbnailView
|
|
? strokeMutedDark
|
|
: getEnteColorScheme(context).strokeMuted,
|
|
width: UserAvatarWidget.strokeWidth,
|
|
strokeAlign: BorderSide.strokeAlignOutside,
|
|
),
|
|
),
|
|
child: SizedBox(
|
|
height: size,
|
|
width: size,
|
|
child: FutureBuilder(
|
|
future: _personID,
|
|
builder: (context, snapshot) {
|
|
if (snapshot.hasData) {
|
|
final personID = snapshot.data as String;
|
|
return ClipOval(
|
|
child: PersonFaceWidget(
|
|
_faceThumbnail!,
|
|
personId: personID,
|
|
),
|
|
);
|
|
} else if (snapshot.hasError) {
|
|
_logger.severe("Error loading personID", snapshot.error);
|
|
return _FirstLetterAvatar(
|
|
user: widget.user,
|
|
currentUserID: widget.currentUserID,
|
|
thumbnailView: widget.thumbnailView,
|
|
type: widget.type,
|
|
);
|
|
} else if (snapshot.connectionState == ConnectionState.done &&
|
|
snapshot.data == null) {
|
|
return _FirstLetterAvatar(
|
|
user: widget.user,
|
|
currentUserID: widget.currentUserID,
|
|
thumbnailView: widget.thumbnailView,
|
|
type: widget.type,
|
|
);
|
|
}
|
|
return const SizedBox.shrink();
|
|
},
|
|
),
|
|
),
|
|
)
|
|
: _FirstLetterAvatar(
|
|
user: widget.user,
|
|
currentUserID: widget.currentUserID,
|
|
thumbnailView: widget.thumbnailView,
|
|
type: widget.type,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _FirstLetterAvatar extends StatefulWidget {
|
|
final User user;
|
|
final int currentUserID;
|
|
final bool thumbnailView;
|
|
final AvatarType type;
|
|
const _FirstLetterAvatar({
|
|
required this.user,
|
|
required this.currentUserID,
|
|
required this.thumbnailView,
|
|
required this.type,
|
|
});
|
|
|
|
@override
|
|
State<_FirstLetterAvatar> createState() => _FirstLetterAvatarState();
|
|
}
|
|
|
|
class _FirstLetterAvatarState extends State<_FirstLetterAvatar> {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final colorScheme = getEnteColorScheme(context);
|
|
final displayChar =
|
|
(widget.user.displayName == null || widget.user.displayName!.isEmpty)
|
|
? ((widget.user.email.isEmpty)
|
|
? " "
|
|
: widget.user.email.substring(0, 1))
|
|
: widget.user.displayName!.substring(0, 1);
|
|
Color decorationColor;
|
|
if (widget.user.id == null ||
|
|
widget.user.id! <= 0 ||
|
|
widget.user.id == widget.currentUserID) {
|
|
decorationColor = Colors.black;
|
|
} else {
|
|
decorationColor = colorScheme.avatarColors[
|
|
(widget.user.id!).remainder(colorScheme.avatarColors.length)];
|
|
}
|
|
|
|
final avatarStyle = getAvatarStyle(context, widget.type);
|
|
final double size = avatarStyle.item1;
|
|
final TextStyle textStyle = avatarStyle.item2;
|
|
return Container(
|
|
padding: const EdgeInsets.all(0.5),
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
border: Border.all(
|
|
color: widget.thumbnailView
|
|
? strokeMutedDark
|
|
: getEnteColorScheme(context).strokeMuted,
|
|
width: UserAvatarWidget.strokeWidth,
|
|
strokeAlign: BorderSide.strokeAlignOutside,
|
|
),
|
|
),
|
|
child: SizedBox(
|
|
height: size,
|
|
width: size,
|
|
child: CircleAvatar(
|
|
backgroundColor: decorationColor,
|
|
child: Text(
|
|
displayChar.toUpperCase(),
|
|
// fixed color
|
|
style: textStyle.copyWith(color: Colors.white),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Tuple2<double, TextStyle> getAvatarStyle(
|
|
BuildContext context,
|
|
AvatarType type,
|
|
) {
|
|
final enteTextTheme = getEnteTextTheme(context);
|
|
switch (type) {
|
|
case AvatarType.small:
|
|
return Tuple2(32.0, enteTextTheme.small);
|
|
case AvatarType.mini:
|
|
return Tuple2(24.0, enteTextTheme.mini);
|
|
case AvatarType.tiny:
|
|
return Tuple2(18.0, enteTextTheme.tiny);
|
|
case AvatarType.extra:
|
|
return Tuple2(18.0, enteTextTheme.tiny);
|
|
}
|
|
}
|
|
}
|
|
|
|
double getAvatarSize(
|
|
AvatarType type,
|
|
) {
|
|
switch (type) {
|
|
case AvatarType.small:
|
|
return 32.0;
|
|
case AvatarType.mini:
|
|
return 24.0;
|
|
case AvatarType.tiny:
|
|
return 18.0;
|
|
case AvatarType.extra:
|
|
return 18.0;
|
|
}
|
|
}
|