mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:17:11 -05:00 
			
		
		
		
	Merge branch 'main' of github.com:immich-app/immich
This commit is contained in:
		
						commit
						97bbe42599
					
				@ -1,8 +1,7 @@
 | 
			
		||||
import 'dart:math';
 | 
			
		||||
 | 
			
		||||
import 'package:auto_route/auto_route.dart';
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:immich_mobile/modules/home/ui/user_circle_avatar.dart';
 | 
			
		||||
import 'package:immich_mobile/modules/login/models/authentication_state.model.dart';
 | 
			
		||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
 | 
			
		||||
 | 
			
		||||
@ -10,9 +9,7 @@ import 'package:immich_mobile/routing/router.dart';
 | 
			
		||||
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/modules/backup/providers/backup.provider.dart';
 | 
			
		||||
import 'package:immich_mobile/shared/models/store.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 {
 | 
			
		||||
  @override
 | 
			
		||||
@ -46,29 +43,13 @@ class HomePageAppBar extends ConsumerWidget with PreferredSizeWidget {
 | 
			
		||||
          },
 | 
			
		||||
        );
 | 
			
		||||
      } else {
 | 
			
		||||
        final String? endpoint = Store.get(StoreKey.serverEndpoint);
 | 
			
		||||
        var dummy = Random().nextInt(1024);
 | 
			
		||||
        return InkWell(
 | 
			
		||||
          onTap: () {
 | 
			
		||||
            Scaffold.of(context).openDrawer();
 | 
			
		||||
          },
 | 
			
		||||
          child: CircleAvatar(
 | 
			
		||||
            backgroundColor: Theme.of(context).primaryColor,
 | 
			
		||||
          child: const UserCircleAvatar(
 | 
			
		||||
            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),
 | 
			
		||||
                imageErrorBuilder: (context, error, stackTrace) =>
 | 
			
		||||
                    Image.memory(kTransparentImage),
 | 
			
		||||
              ),
 | 
			
		||||
            ),
 | 
			
		||||
            size: 33,
 | 
			
		||||
          ),
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,12 @@
 | 
			
		||||
import 'dart:math';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
 | 
			
		||||
import 'package:image_picker/image_picker.dart';
 | 
			
		||||
import 'package:immich_mobile/modules/home/providers/upload_profile_image.provider.dart';
 | 
			
		||||
import 'package:immich_mobile/modules/home/ui/user_circle_avatar.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/shared/models/store.dart';
 | 
			
		||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
 | 
			
		||||
import 'package:immich_mobile/shared/ui/transparent_image.dart';
 | 
			
		||||
 | 
			
		||||
class ProfileDrawerHeader extends HookConsumerWidget {
 | 
			
		||||
  const ProfileDrawerHeader({
 | 
			
		||||
@ -18,31 +15,15 @@ class ProfileDrawerHeader extends HookConsumerWidget {
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    final String endpoint = Store.get(StoreKey.serverEndpoint);
 | 
			
		||||
    AuthenticationState authState = ref.watch(authenticationProvider);
 | 
			
		||||
    final uploadProfileImageStatus =
 | 
			
		||||
        ref.watch(uploadProfileImageProvider).status;
 | 
			
		||||
    var dummy = Random().nextInt(1024);
 | 
			
		||||
    final isDarkMode = Theme.of(context).brightness == Brightness.dark;
 | 
			
		||||
 | 
			
		||||
    buildUserProfileImage() {
 | 
			
		||||
      var userImage = CircleAvatar(
 | 
			
		||||
        backgroundColor: Theme.of(context).primaryColor,
 | 
			
		||||
      var userImage = const UserCircleAvatar(
 | 
			
		||||
        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),
 | 
			
		||||
            imageErrorBuilder: (context, error, stackTrace) =>
 | 
			
		||||
                Image.memory(kTransparentImage),
 | 
			
		||||
          ),
 | 
			
		||||
        ),
 | 
			
		||||
        size: 66,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      if (authState.profileImagePath.isEmpty) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										44
									
								
								mobile/lib/modules/home/ui/user_circle_avatar.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								mobile/lib/modules/home/ui/user_circle_avatar.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
			
		||||
import 'dart:math';
 | 
			
		||||
 | 
			
		||||
import 'package:flutter/material.dart';
 | 
			
		||||
import 'package:hooks_riverpod/hooks_riverpod.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/shared/models/store.dart';
 | 
			
		||||
import 'package:immich_mobile/shared/ui/transparent_image.dart';
 | 
			
		||||
 | 
			
		||||
class UserCircleAvatar extends ConsumerWidget {
 | 
			
		||||
  final double radius;
 | 
			
		||||
  final double size;
 | 
			
		||||
  const UserCircleAvatar({super.key, required this.radius, required this.size});
 | 
			
		||||
 | 
			
		||||
  @override
 | 
			
		||||
  Widget build(BuildContext context, WidgetRef ref) {
 | 
			
		||||
    AuthenticationState authState = ref.watch(authenticationProvider);
 | 
			
		||||
 | 
			
		||||
    var profileImageUrl =
 | 
			
		||||
        '${Store.get(StoreKey.serverEndpoint)}/user/profile-image/${authState.userId}?d=${Random().nextInt(1024)}';
 | 
			
		||||
    return CircleAvatar(
 | 
			
		||||
      backgroundColor: Theme.of(context).primaryColor,
 | 
			
		||||
      radius: radius,
 | 
			
		||||
      child: ClipRRect(
 | 
			
		||||
        borderRadius: BorderRadius.circular(50),
 | 
			
		||||
        child: FadeInImage(
 | 
			
		||||
          fit: BoxFit.cover,
 | 
			
		||||
          placeholder: MemoryImage(kTransparentImage),
 | 
			
		||||
          width: size,
 | 
			
		||||
          height: size,
 | 
			
		||||
          image: NetworkImage(
 | 
			
		||||
            profileImageUrl,
 | 
			
		||||
            headers: {
 | 
			
		||||
              "Authorization": "Bearer ${Store.get(StoreKey.accessToken)}"
 | 
			
		||||
            },
 | 
			
		||||
          ),
 | 
			
		||||
          fadeInDuration: const Duration(milliseconds: 200),
 | 
			
		||||
          imageErrorBuilder: (context, error, stackTrace) =>
 | 
			
		||||
              Image.memory(kTransparentImage),
 | 
			
		||||
        ),
 | 
			
		||||
      ),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										12
									
								
								mobile/openapi/doc/ServerInfoApi.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12
									
								
								mobile/openapi/doc/ServerInfoApi.md
									
									
									
										generated
									
									
									
								
							@ -25,6 +25,16 @@ Method | HTTP request | Description
 | 
			
		||||
### Example
 | 
			
		||||
```dart
 | 
			
		||||
import 'package:openapi/api.dart';
 | 
			
		||||
// TODO Configure HTTP Bearer authorization: bearer
 | 
			
		||||
// Case 1. Use String Token
 | 
			
		||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
 | 
			
		||||
// Case 2. Use Function which generate token.
 | 
			
		||||
// String yourTokenGeneratorFunction() { ... }
 | 
			
		||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
 | 
			
		||||
// TODO Configure API key authorization: cookie
 | 
			
		||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
 | 
			
		||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
 | 
			
		||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
 | 
			
		||||
 | 
			
		||||
final api_instance = ServerInfoApi();
 | 
			
		||||
 | 
			
		||||
@ -45,7 +55,7 @@ This endpoint does not need any parameter.
 | 
			
		||||
 | 
			
		||||
### Authorization
 | 
			
		||||
 | 
			
		||||
No authorization required
 | 
			
		||||
[bearer](../README.md#bearer), [cookie](../README.md#cookie)
 | 
			
		||||
 | 
			
		||||
### HTTP request headers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								mobile/openapi/doc/UserApi.md
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										24
									
								
								mobile/openapi/doc/UserApi.md
									
									
									
										generated
									
									
									
								
							@ -292,6 +292,16 @@ This endpoint does not need any parameter.
 | 
			
		||||
### Example
 | 
			
		||||
```dart
 | 
			
		||||
import 'package:openapi/api.dart';
 | 
			
		||||
// TODO Configure HTTP Bearer authorization: bearer
 | 
			
		||||
// Case 1. Use String Token
 | 
			
		||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
 | 
			
		||||
// Case 2. Use Function which generate token.
 | 
			
		||||
// String yourTokenGeneratorFunction() { ... }
 | 
			
		||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
 | 
			
		||||
// TODO Configure API key authorization: cookie
 | 
			
		||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
 | 
			
		||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
 | 
			
		||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
 | 
			
		||||
 | 
			
		||||
final api_instance = UserApi();
 | 
			
		||||
final userId = userId_example; // String | 
 | 
			
		||||
@ -316,7 +326,7 @@ Name | Type | Description  | Notes
 | 
			
		||||
 | 
			
		||||
### Authorization
 | 
			
		||||
 | 
			
		||||
No authorization required
 | 
			
		||||
[bearer](../README.md#bearer), [cookie](../README.md#cookie)
 | 
			
		||||
 | 
			
		||||
### HTTP request headers
 | 
			
		||||
 | 
			
		||||
@ -335,6 +345,16 @@ No authorization required
 | 
			
		||||
### Example
 | 
			
		||||
```dart
 | 
			
		||||
import 'package:openapi/api.dart';
 | 
			
		||||
// TODO Configure HTTP Bearer authorization: bearer
 | 
			
		||||
// Case 1. Use String Token
 | 
			
		||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
 | 
			
		||||
// Case 2. Use Function which generate token.
 | 
			
		||||
// String yourTokenGeneratorFunction() { ... }
 | 
			
		||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
 | 
			
		||||
// TODO Configure API key authorization: cookie
 | 
			
		||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
 | 
			
		||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
 | 
			
		||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
 | 
			
		||||
 | 
			
		||||
final api_instance = UserApi();
 | 
			
		||||
final userId = userId_example; // String | 
 | 
			
		||||
@ -359,7 +379,7 @@ Name | Type | Description  | Notes
 | 
			
		||||
 | 
			
		||||
### Authorization
 | 
			
		||||
 | 
			
		||||
No authorization required
 | 
			
		||||
[bearer](../README.md#bearer), [cookie](../README.md#cookie)
 | 
			
		||||
 | 
			
		||||
### HTTP request headers
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -14,6 +14,7 @@ import { Authenticated } from '../decorators/authenticated.decorator';
 | 
			
		||||
export class ServerInfoController {
 | 
			
		||||
  constructor(private service: ServerInfoService) {}
 | 
			
		||||
 | 
			
		||||
  @Authenticated()
 | 
			
		||||
  @Get()
 | 
			
		||||
  getServerInfo(): Promise<ServerInfoResponseDto> {
 | 
			
		||||
    return this.service.getInfo();
 | 
			
		||||
 | 
			
		||||
@ -44,6 +44,7 @@ export class UserController {
 | 
			
		||||
    return this.service.getAllUsers(authUser, isAll);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Authenticated()
 | 
			
		||||
  @Get('/info/:userId')
 | 
			
		||||
  getUserById(@Param('userId') userId: string): Promise<UserResponseDto> {
 | 
			
		||||
    return this.service.getUserById(userId);
 | 
			
		||||
@ -87,8 +88,8 @@ export class UserController {
 | 
			
		||||
    return this.service.updateUser(authUser, updateUserDto);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @UseInterceptors(FileInterceptor('file', profileImageUploadOption))
 | 
			
		||||
  @Authenticated()
 | 
			
		||||
  @UseInterceptors(FileInterceptor('file', profileImageUploadOption))
 | 
			
		||||
  @ApiConsumes('multipart/form-data')
 | 
			
		||||
  @ApiBody({
 | 
			
		||||
    description: 'A new avatar for the user',
 | 
			
		||||
@ -102,6 +103,7 @@ export class UserController {
 | 
			
		||||
    return this.service.createProfileImage(authUser, fileInfo);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Authenticated()
 | 
			
		||||
  @Get('/profile-image/:userId')
 | 
			
		||||
  @Header('Cache-Control', 'max-age=600')
 | 
			
		||||
  async getProfileImage(@Param('userId') userId: string, @Response({ passthrough: true }) res: Res): Promise<any> {
 | 
			
		||||
 | 
			
		||||
@ -943,6 +943,14 @@
 | 
			
		||||
        },
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "Server Info"
 | 
			
		||||
        ],
 | 
			
		||||
        "security": [
 | 
			
		||||
          {
 | 
			
		||||
            "bearer": []
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "cookie": []
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
@ -1482,6 +1490,14 @@
 | 
			
		||||
        },
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "User"
 | 
			
		||||
        ],
 | 
			
		||||
        "security": [
 | 
			
		||||
          {
 | 
			
		||||
            "bearer": []
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "cookie": []
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
@ -1694,6 +1710,14 @@
 | 
			
		||||
        },
 | 
			
		||||
        "tags": [
 | 
			
		||||
          "User"
 | 
			
		||||
        ],
 | 
			
		||||
        "security": [
 | 
			
		||||
          {
 | 
			
		||||
            "bearer": []
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            "cookie": []
 | 
			
		||||
          }
 | 
			
		||||
        ]
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										18
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										18
									
								
								web/src/api/open-api/api.ts
									
									
									
										generated
									
									
									
								
							@ -7043,6 +7043,12 @@ export const ServerInfoApiAxiosParamCreator = function (configuration?: Configur
 | 
			
		||||
            const localVarHeaderParameter = {} as any;
 | 
			
		||||
            const localVarQueryParameter = {} as any;
 | 
			
		||||
 | 
			
		||||
            // authentication bearer required
 | 
			
		||||
            // http bearer authentication required
 | 
			
		||||
            await setBearerAuthToObject(localVarHeaderParameter, configuration)
 | 
			
		||||
 | 
			
		||||
            // authentication cookie required
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
            setSearchParams(localVarUrlObj, localVarQueryParameter);
 | 
			
		||||
@ -8586,6 +8592,12 @@ export const UserApiAxiosParamCreator = function (configuration?: Configuration)
 | 
			
		||||
            const localVarHeaderParameter = {} as any;
 | 
			
		||||
            const localVarQueryParameter = {} as any;
 | 
			
		||||
 | 
			
		||||
            // authentication bearer required
 | 
			
		||||
            // http bearer authentication required
 | 
			
		||||
            await setBearerAuthToObject(localVarHeaderParameter, configuration)
 | 
			
		||||
 | 
			
		||||
            // authentication cookie required
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
            setSearchParams(localVarUrlObj, localVarQueryParameter);
 | 
			
		||||
@ -8619,6 +8631,12 @@ export const UserApiAxiosParamCreator = function (configuration?: Configuration)
 | 
			
		||||
            const localVarHeaderParameter = {} as any;
 | 
			
		||||
            const localVarQueryParameter = {} as any;
 | 
			
		||||
 | 
			
		||||
            // authentication bearer required
 | 
			
		||||
            // http bearer authentication required
 | 
			
		||||
            await setBearerAuthToObject(localVarHeaderParameter, configuration)
 | 
			
		||||
 | 
			
		||||
            // authentication cookie required
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
            setSearchParams(localVarUrlObj, localVarQueryParameter);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user