1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2025-01-06 11:56:46 +01:00

feat(mobile): Uses profile photo for user avatar drawer (#1738)

* uses profile photo for user avatar drawer

* Added some styling to the profile picture

* made the whole profile photo a gesture detector

* fixed image updating

* invalidates cachednetworkimage when new profile photo is uploaded

* Revert "invalidates cachednetworkimage when new profile photo is uploaded"

This reverts commit 17c83be556.

* Add fadeInImage to loading user profile

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
This commit is contained in:
martyfuhry 2023-02-12 22:32:16 -05:00 committed by GitHub
parent caac3bfc95
commit 12a6a7d95a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 153 additions and 43 deletions

View file

@ -1,6 +1,11 @@
import 'dart:math';
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/hive_box.dart';
import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/routing/router.dart';
@ -8,15 +13,16 @@ import 'package:immich_mobile/modules/backup/models/backup_state.model.dart';
import 'package:immich_mobile/shared/models/server_info_state.model.dart'; import 'package:immich_mobile/shared/models/server_info_state.model.dart';
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart'; import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
import 'package:immich_mobile/shared/providers/server_info.provider.dart'; import 'package:immich_mobile/shared/providers/server_info.provider.dart';
import 'package:immich_mobile/shared/ui/transparent_image.dart';
class HomePageAppBar extends ConsumerWidget with PreferredSizeWidget { class HomePageAppBar extends ConsumerWidget with PreferredSizeWidget {
@override @override
Size get preferredSize => const Size.fromHeight(kToolbarHeight); Size get preferredSize => const Size.fromHeight(kToolbarHeight);
const HomePageAppBar({ const HomePageAppBar({
Key? key, super.key,
this.onPopBack, this.onPopBack,
}) : super(key: key); });
final Function? onPopBack; final Function? onPopBack;
@ -26,6 +32,46 @@ class HomePageAppBar extends ConsumerWidget with PreferredSizeWidget {
bool isEnableAutoBackup = backupState.backgroundBackup || bool isEnableAutoBackup = backupState.backgroundBackup ||
ref.watch(authenticationProvider).deviceInfo.isAutoBackup; ref.watch(authenticationProvider).deviceInfo.isAutoBackup;
final ServerInfoState serverInfoState = ref.watch(serverInfoProvider); final ServerInfoState serverInfoState = ref.watch(serverInfoProvider);
AuthenticationState authState = ref.watch(authenticationProvider);
buildProfilePhoto() {
if (authState.profileImagePath.isEmpty) {
return IconButton(
splashRadius: 25,
icon: const Icon(
Icons.face_outlined,
size: 30,
),
onPressed: () {
Scaffold.of(context).openDrawer();
},
);
} else {
String endpoint = Hive.box(userInfoBox).get(serverEndpointKey);
var dummy = Random().nextInt(1024);
return InkWell(
onTap: () {
Scaffold.of(context).openDrawer();
},
child: CircleAvatar(
backgroundColor: Theme.of(context).primaryColor,
radius: 18,
child: ClipRRect(
borderRadius: BorderRadius.circular(50),
child: FadeInImage.memoryNetwork(
fit: BoxFit.cover,
placeholder: kTransparentImage,
width: 33,
height: 33,
image:
'$endpoint/user/profile-image/${authState.userId}?d=${dummy++}',
fadeInDuration: const Duration(milliseconds: 200),
),
),
),
);
}
}
return AppBar( return AppBar(
backgroundColor: Theme.of(context).appBarTheme.backgroundColor, backgroundColor: Theme.of(context).appBarTheme.backgroundColor,
@ -38,18 +84,8 @@ class HomePageAppBar extends ConsumerWidget with PreferredSizeWidget {
builder: (BuildContext context) { builder: (BuildContext context) {
return Stack( return Stack(
children: [ children: [
Positioned( Center(
top: 5, child: buildProfilePhoto(),
child: IconButton(
splashRadius: 25,
icon: const Icon(
Icons.face_outlined,
size: 30,
),
onPressed: () {
Scaffold.of(context).openDrawer();
},
),
), ),
if (serverInfoState.isVersionMismatch) if (serverInfoState.isVersionMismatch)
Positioned( Positioned(

View file

@ -10,6 +10,7 @@ import 'package:immich_mobile/modules/home/providers/upload_profile_image.provid
import 'package:immich_mobile/modules/login/models/authentication_state.model.dart'; import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart'; import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
import 'package:immich_mobile/shared/ui/transparent_image.dart';
class ProfileDrawerHeader extends HookConsumerWidget { class ProfileDrawerHeader extends HookConsumerWidget {
const ProfileDrawerHeader({ const ProfileDrawerHeader({
@ -26,6 +27,23 @@ class ProfileDrawerHeader extends HookConsumerWidget {
final isDarkMode = Theme.of(context).brightness == Brightness.dark; final isDarkMode = Theme.of(context).brightness == Brightness.dark;
buildUserProfileImage() { buildUserProfileImage() {
var userImage = CircleAvatar(
backgroundColor: Theme.of(context).primaryColor,
radius: 35,
child: ClipRRect(
borderRadius: BorderRadius.circular(50),
child: FadeInImage.memoryNetwork(
fit: BoxFit.cover,
placeholder: kTransparentImage,
width: 66,
height: 66,
image:
'$endpoint/user/profile-image/${authState.userId}?d=${dummy++}',
fadeInDuration: const Duration(milliseconds: 200),
),
),
);
if (authState.profileImagePath.isEmpty) { if (authState.profileImagePath.isEmpty) {
return const CircleAvatar( return const CircleAvatar(
radius: 35, radius: 35,
@ -36,16 +54,10 @@ class ProfileDrawerHeader extends HookConsumerWidget {
if (uploadProfileImageStatus == UploadProfileStatus.idle) { if (uploadProfileImageStatus == UploadProfileStatus.idle) {
if (authState.profileImagePath.isNotEmpty) { if (authState.profileImagePath.isNotEmpty) {
return CircleAvatar( return userImage;
radius: 35,
backgroundImage: NetworkImage(
'$endpoint/user/profile-image/${authState.userId}?d=${dummy++}',
),
backgroundColor: Colors.transparent,
);
} else { } else {
return const CircleAvatar( return const CircleAvatar(
radius: 35, radius: 33,
backgroundImage: AssetImage('assets/immich-logo-no-outline.png'), backgroundImage: AssetImage('assets/immich-logo-no-outline.png'),
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
); );
@ -53,13 +65,7 @@ class ProfileDrawerHeader extends HookConsumerWidget {
} }
if (uploadProfileImageStatus == UploadProfileStatus.success) { if (uploadProfileImageStatus == UploadProfileStatus.success) {
return CircleAvatar( return userImage;
radius: 35,
backgroundImage: NetworkImage(
'$endpoint/user/profile-image/${authState.userId}?d=${dummy++}',
),
backgroundColor: Colors.transparent,
);
} }
if (uploadProfileImageStatus == UploadProfileStatus.failure) { if (uploadProfileImageStatus == UploadProfileStatus.failure) {
@ -98,7 +104,7 @@ class ProfileDrawerHeader extends HookConsumerWidget {
useEffect( useEffect(
() { () {
buildUserProfileImage(); // buildUserProfileImage();
return null; return null;
}, },
[], [],
@ -126,17 +132,17 @@ class ProfileDrawerHeader extends HookConsumerWidget {
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Stack( GestureDetector(
onTap: pickUserProfileImage,
child: Stack(
clipBehavior: Clip.none, clipBehavior: Clip.none,
children: [ children: [
buildUserProfileImage(), buildUserProfileImage(),
Positioned( Positioned(
bottom: 0, bottom: 0,
right: -5, right: -5,
child: GestureDetector(
onTap: pickUserProfileImage,
child: Material( child: Material(
color: Colors.grey[100], color: isDarkMode ? Colors.grey[700] : Colors.grey[100],
elevation: 3, elevation: 3,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(50.0), borderRadius: BorderRadius.circular(50.0),
@ -151,9 +157,9 @@ class ProfileDrawerHeader extends HookConsumerWidget {
), ),
), ),
), ),
),
], ],
), ),
),
Text( Text(
"${authState.firstName} ${authState.lastName}", "${authState.firstName} ${authState.lastName}",
style: TextStyle( style: TextStyle(

View file

@ -0,0 +1,68 @@
import 'dart:typed_data';
final Uint8List kTransparentImage = Uint8List.fromList(<int>[
0x89,
0x50,
0x4E,
0x47,
0x0D,
0x0A,
0x1A,
0x0A,
0x00,
0x00,
0x00,
0x0D,
0x49,
0x48,
0x44,
0x52,
0x00,
0x00,
0x00,
0x01,
0x00,
0x00,
0x00,
0x01,
0x08,
0x06,
0x00,
0x00,
0x00,
0x1F,
0x15,
0xC4,
0x89,
0x00,
0x00,
0x00,
0x0A,
0x49,
0x44,
0x41,
0x54,
0x78,
0x9C,
0x63,
0x00,
0x01,
0x00,
0x00,
0x05,
0x00,
0x01,
0x0D,
0x0A,
0x2D,
0xB4,
0x00,
0x00,
0x00,
0x00,
0x49,
0x45,
0x4E,
0x44,
0xAE,
]);

View file

@ -6,7 +6,7 @@
"packages": { "packages": {
"": { "": {
"name": "immich", "name": "immich",
"version": "1.45.0", "version": "1.46.1",
"license": "UNLICENSED", "license": "UNLICENSED",
"dependencies": { "dependencies": {
"@nestjs/bull": "^0.6.2", "@nestjs/bull": "^0.6.2",