Sfoglia il codice sorgente

feat(web, mobile): Options to show archived assets in map (#4293)

* Add include archive setting to map on web

* open api

* better naming for web isArchived variable

* add withArchived setting to mobile

* (e2e): tests for mapMarker endpoint and isArchived

* isArchived to mobile

* chore: cleanup test

* chore: optimize e2e

---------

Co-authored-by: shalong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
Daniel Dietzler 1 anno fa
parent
commit
e571880c16

+ 18 - 5
cli/src/api/open-api/api.ts

@@ -6278,13 +6278,14 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
         },
         /**
          * 
+         * @param {boolean} [isArchived] 
          * @param {boolean} [isFavorite] 
          * @param {string} [fileCreatedAfter] 
          * @param {string} [fileCreatedBefore] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        getMapMarkers: async (isFavorite?: boolean, fileCreatedAfter?: string, fileCreatedBefore?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+        getMapMarkers: async (isArchived?: boolean, isFavorite?: boolean, fileCreatedAfter?: string, fileCreatedBefore?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
             const localVarPath = `/asset/map-marker`;
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
@@ -6306,6 +6307,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
             // http bearer authentication required
             await setBearerAuthToObject(localVarHeaderParameter, configuration)
 
+            if (isArchived !== undefined) {
+                localVarQueryParameter['isArchived'] = isArchived;
+            }
+
             if (isFavorite !== undefined) {
                 localVarQueryParameter['isFavorite'] = isFavorite;
             }
@@ -7134,14 +7139,15 @@ export const AssetApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
+         * @param {boolean} [isArchived] 
          * @param {boolean} [isFavorite] 
          * @param {string} [fileCreatedAfter] 
          * @param {string} [fileCreatedBefore] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async getMapMarkers(isFavorite?: boolean, fileCreatedAfter?: string, fileCreatedBefore?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<MapMarkerResponseDto>>> {
-            const localVarAxiosArgs = await localVarAxiosParamCreator.getMapMarkers(isFavorite, fileCreatedAfter, fileCreatedBefore, options);
+        async getMapMarkers(isArchived?: boolean, isFavorite?: boolean, fileCreatedAfter?: string, fileCreatedBefore?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<MapMarkerResponseDto>>> {
+            const localVarAxiosArgs = await localVarAxiosParamCreator.getMapMarkers(isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore, options);
             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
         },
         /**
@@ -7428,7 +7434,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
          * @throws {RequiredError}
          */
         getMapMarkers(requestParameters: AssetApiGetMapMarkersRequest = {}, options?: AxiosRequestConfig): AxiosPromise<Array<MapMarkerResponseDto>> {
-            return localVarFp.getMapMarkers(requestParameters.isFavorite, requestParameters.fileCreatedAfter, requestParameters.fileCreatedBefore, options).then((request) => request(axios, basePath));
+            return localVarFp.getMapMarkers(requestParameters.isArchived, requestParameters.isFavorite, requestParameters.fileCreatedAfter, requestParameters.fileCreatedBefore, options).then((request) => request(axios, basePath));
         },
         /**
          * 
@@ -7846,6 +7852,13 @@ export interface AssetApiGetDownloadInfoRequest {
  * @interface AssetApiGetMapMarkersRequest
  */
 export interface AssetApiGetMapMarkersRequest {
+    /**
+     * 
+     * @type {boolean}
+     * @memberof AssetApiGetMapMarkers
+     */
+    readonly isArchived?: boolean
+
     /**
      * 
      * @type {boolean}
@@ -8374,7 +8387,7 @@ export class AssetApi extends BaseAPI {
      * @memberof AssetApi
      */
     public getMapMarkers(requestParameters: AssetApiGetMapMarkersRequest = {}, options?: AxiosRequestConfig) {
-        return AssetApiFp(this.configuration).getMapMarkers(requestParameters.isFavorite, requestParameters.fileCreatedAfter, requestParameters.fileCreatedBefore, options).then((request) => request(this.axios, this.basePath));
+        return AssetApiFp(this.configuration).getMapMarkers(requestParameters.isArchived, requestParameters.isFavorite, requestParameters.fileCreatedAfter, requestParameters.fileCreatedBefore, options).then((request) => request(this.axios, this.basePath));
     }
 
     /**

+ 1 - 0
mobile/assets/i18n/en-US.json

@@ -312,6 +312,7 @@
   "map_settings_dialog_title": "Map Settings",
   "map_settings_dark_mode": "Dark mode",
   "map_settings_only_show_favorites": "Show Favorite Only",
+  "map_settings_include_show_archived": "Include Archived",
   "map_settings_only_relative_range": "Date range",
   "map_settings_dialog_cancel": "Cancel",
   "map_settings_dialog_save": "Save",

+ 9 - 3
mobile/lib/modules/map/models/map_state.model.dart

@@ -1,29 +1,33 @@
 class MapState {
   final bool isDarkTheme;
   final bool showFavoriteOnly;
+  final bool includeArchived;
   final int relativeTime;
 
   MapState({
     this.isDarkTheme = false,
     this.showFavoriteOnly = false,
+    this.includeArchived = false,
     this.relativeTime = 0,
   });
 
   MapState copyWith({
     bool? isDarkTheme,
     bool? showFavoriteOnly,
+    bool? includeArchived,
     int? relativeTime,
   }) {
     return MapState(
       isDarkTheme: isDarkTheme ?? this.isDarkTheme,
       showFavoriteOnly: showFavoriteOnly ?? this.showFavoriteOnly,
+      includeArchived: includeArchived ?? this.includeArchived,
       relativeTime: relativeTime ?? this.relativeTime,
     );
   }
 
   @override
   String toString() {
-    return 'MapSettingsState(isDarkTheme: $isDarkTheme, showFavoriteOnly: $showFavoriteOnly, relativeTime: $relativeTime)';
+    return 'MapSettingsState(isDarkTheme: $isDarkTheme, showFavoriteOnly: $showFavoriteOnly, relativeTime: $relativeTime, includeArchived: $includeArchived)';
   }
 
   @override
@@ -33,13 +37,15 @@ class MapState {
     return other is MapState &&
         other.isDarkTheme == isDarkTheme &&
         other.showFavoriteOnly == showFavoriteOnly &&
-        other.relativeTime == relativeTime;
+        other.relativeTime == relativeTime &&
+        other.includeArchived == includeArchived;
   }
 
   @override
   int get hashCode {
     return isDarkTheme.hashCode ^
         showFavoriteOnly.hashCode ^
-        relativeTime.hashCode;
+        relativeTime.hashCode ^
+        includeArchived.hashCode;
   }
 }

+ 6 - 0
mobile/lib/modules/map/providers/map_marker.provider.dart

@@ -10,6 +10,7 @@ final mapMarkersProvider =
   final mapState = ref.read(mapStateNotifier);
   DateTime? fileCreatedAfter;
   bool? isFavorite;
+  bool? isIncludeArchived;
 
   if (mapState.relativeTime != 0) {
     fileCreatedAfter =
@@ -20,8 +21,13 @@ final mapMarkersProvider =
     isFavorite = true;
   }
 
+  if (!mapState.includeArchived) {
+    isIncludeArchived = false;
+  }
+
   final markers = await service.getMapMarkers(
     isFavorite: isFavorite,
+    withArchived: isIncludeArchived,
     fileCreatedAfter: fileCreatedAfter,
   );
 

+ 11 - 1
mobile/lib/modules/map/providers/map_state.provider.dart

@@ -11,6 +11,8 @@ class MapStateNotifier extends StateNotifier<MapState> {
                 .getSetting<bool>(AppSettingsEnum.mapThemeMode),
             showFavoriteOnly: appSettingsProvider
                 .getSetting<bool>(AppSettingsEnum.mapShowFavoriteOnly),
+            includeArchived: appSettingsProvider
+                .getSetting<bool>(AppSettingsEnum.mapIncludeArchived),
             relativeTime: appSettingsProvider
                 .getSetting<int>(AppSettingsEnum.mapRelativeDate),
           ),
@@ -31,11 +33,19 @@ class MapStateNotifier extends StateNotifier<MapState> {
   void switchFavoriteOnly(bool isFavoriteOnly) {
     appSettingsProvider.setSetting(
       AppSettingsEnum.mapShowFavoriteOnly,
-      appSettingsProvider,
+      isFavoriteOnly,
     );
     state = state.copyWith(showFavoriteOnly: isFavoriteOnly);
   }
 
+  void switchIncludeArchived(bool isIncludeArchived) {
+    appSettingsProvider.setSetting(
+      AppSettingsEnum.mapIncludeArchived,
+      isIncludeArchived,
+    );
+    state = state.copyWith(includeArchived: isIncludeArchived);
+  }
+
   void setRelativeTime(int relativeTime) {
     appSettingsProvider.setSetting(
       AppSettingsEnum.mapRelativeDate,

+ 2 - 0
mobile/lib/modules/map/services/map.service.dart

@@ -23,12 +23,14 @@ class MapSerivce {
 
   Future<List<MapMarkerResponseDto>> getMapMarkers({
     bool? isFavorite,
+    bool? withArchived,
     DateTime? fileCreatedAfter,
     DateTime? fileCreatedBefore,
   }) async {
     try {
       final markers = await _apiService.assetApi.getMapMarkers(
         isFavorite: isFavorite,
+        isArchived: withArchived,
         fileCreatedAfter: fileCreatedAfter,
         fileCreatedBefore: fileCreatedBefore,
       );

+ 20 - 0
mobile/lib/modules/map/ui/map_settings_dialog.dart

@@ -13,6 +13,7 @@ class MapSettingsDialog extends HookConsumerWidget {
     final mapSettings = ref.read(mapStateNotifier);
     final isDarkMode = useState(mapSettings.isDarkTheme);
     final showFavoriteOnly = useState(mapSettings.showFavoriteOnly);
+    final showIncludeArchived = useState(mapSettings.includeArchived);
     final showRelativeDate = useState(mapSettings.relativeTime);
     final ThemeData theme = Theme.of(context);
 
@@ -48,6 +49,22 @@ class MapSettingsDialog extends HookConsumerWidget {
       );
     }
 
+    Widget buildIncludeArchivedSetting() {
+      return SwitchListTile.adaptive(
+        value: showIncludeArchived.value,
+        onChanged: (value) {
+          showIncludeArchived.value = value;
+        },
+        activeColor: theme.primaryColor,
+        dense: true,
+        title: Text(
+          "map_settings_include_show_archived".tr(),
+          style:
+              theme.textTheme.labelLarge?.copyWith(fontWeight: FontWeight.bold),
+        ),
+      );
+    }
+
     Widget buildDateRangeSetting() {
       final now = DateTime.now();
       return DropdownMenu(
@@ -127,6 +144,8 @@ class MapSettingsDialog extends HookConsumerWidget {
             mapSettingsNotifier.switchTheme(isDarkMode.value);
             mapSettingsNotifier.switchFavoriteOnly(showFavoriteOnly.value);
             mapSettingsNotifier.setRelativeTime(showRelativeDate.value);
+            mapSettingsNotifier
+                .switchIncludeArchived(showIncludeArchived.value);
             Navigator.of(context).pop();
           },
           style: TextButton.styleFrom(
@@ -166,6 +185,7 @@ class MapSettingsDialog extends HookConsumerWidget {
             children: [
               buildMapThemeSetting(),
               buildFavoriteOnlySetting(),
+              buildIncludeArchivedSetting(),
               const SizedBox(
                 height: 10,
               ),

+ 2 - 1
mobile/lib/modules/map/views/map_page.dart

@@ -155,7 +155,8 @@ class MapPageState extends ConsumerState<MapPage> {
     ref.listen(mapStateNotifier, (previous, next) {
       bool shouldRefetch =
           previous?.showFavoriteOnly != next.showFavoriteOnly ||
-              previous?.relativeTime != next.relativeTime;
+              previous?.relativeTime != next.relativeTime ||
+              previous?.includeArchived != next.includeArchived;
       if (shouldRefetch) {
         refetchMarkers.value = shouldRefetch;
         ref.invalidate(mapMarkersProvider);

+ 1 - 0
mobile/lib/modules/settings/services/app_settings.service.dart

@@ -48,6 +48,7 @@ enum AppSettingsEnum<T> {
   preferRemoteImage<bool>(StoreKey.preferRemoteImage, null, false),
   mapThemeMode<bool>(StoreKey.mapThemeMode, null, false),
   mapShowFavoriteOnly<bool>(StoreKey.mapShowFavoriteOnly, null, false),
+  mapIncludeArchived<bool>(StoreKey.mapIncludeArchived, null, false),
   mapRelativeDate<int>(StoreKey.mapRelativeDate, null, 0),
   allowSelfSignedSSLCert<bool>(StoreKey.selfSignedCert, null, false),
   ;

+ 1 - 0
mobile/lib/shared/models/store.dart

@@ -179,6 +179,7 @@ enum StoreKey<T> {
   mapShowFavoriteOnly<bool>(118, type: bool),
   mapRelativeDate<int>(119, type: int),
   selfSignedCert<bool>(120, type: bool),
+  mapIncludeArchived<bool>(121, type: bool),
   ;
 
   const StoreKey(

+ 4 - 2
mobile/openapi/doc/AssetApi.md

@@ -902,7 +902,7 @@ Name | Type | Description  | Notes
 [[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
 
 # **getMapMarkers**
-> List<MapMarkerResponseDto> getMapMarkers(isFavorite, fileCreatedAfter, fileCreatedBefore)
+> List<MapMarkerResponseDto> getMapMarkers(isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore)
 
 
 
@@ -925,12 +925,13 @@ import 'package:openapi/api.dart';
 //defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
 
 final api_instance = AssetApi();
+final isArchived = true; // bool | 
 final isFavorite = true; // bool | 
 final fileCreatedAfter = 2013-10-20T19:20:30+01:00; // DateTime | 
 final fileCreatedBefore = 2013-10-20T19:20:30+01:00; // DateTime | 
 
 try {
-    final result = api_instance.getMapMarkers(isFavorite, fileCreatedAfter, fileCreatedBefore);
+    final result = api_instance.getMapMarkers(isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore);
     print(result);
 } catch (e) {
     print('Exception when calling AssetApi->getMapMarkers: $e\n');
@@ -941,6 +942,7 @@ try {
 
 Name | Type | Description  | Notes
 ------------- | ------------- | ------------- | -------------
+ **isArchived** | **bool**|  | [optional] 
  **isFavorite** | **bool**|  | [optional] 
  **fileCreatedAfter** | **DateTime**|  | [optional] 
  **fileCreatedBefore** | **DateTime**|  | [optional] 

+ 10 - 3
mobile/openapi/lib/api/asset_api.dart

@@ -909,12 +909,14 @@ class AssetApi {
   /// Performs an HTTP 'GET /asset/map-marker' operation and returns the [Response].
   /// Parameters:
   ///
+  /// * [bool] isArchived:
+  ///
   /// * [bool] isFavorite:
   ///
   /// * [DateTime] fileCreatedAfter:
   ///
   /// * [DateTime] fileCreatedBefore:
-  Future<Response> getMapMarkersWithHttpInfo({ bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, }) async {
+  Future<Response> getMapMarkersWithHttpInfo({ bool? isArchived, bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, }) async {
     // ignore: prefer_const_declarations
     final path = r'/asset/map-marker';
 
@@ -925,6 +927,9 @@ class AssetApi {
     final headerParams = <String, String>{};
     final formParams = <String, String>{};
 
+    if (isArchived != null) {
+      queryParams.addAll(_queryParams('', 'isArchived', isArchived));
+    }
     if (isFavorite != null) {
       queryParams.addAll(_queryParams('', 'isFavorite', isFavorite));
     }
@@ -951,13 +956,15 @@ class AssetApi {
 
   /// Parameters:
   ///
+  /// * [bool] isArchived:
+  ///
   /// * [bool] isFavorite:
   ///
   /// * [DateTime] fileCreatedAfter:
   ///
   /// * [DateTime] fileCreatedBefore:
-  Future<List<MapMarkerResponseDto>?> getMapMarkers({ bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, }) async {
-    final response = await getMapMarkersWithHttpInfo( isFavorite: isFavorite, fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, );
+  Future<List<MapMarkerResponseDto>?> getMapMarkers({ bool? isArchived, bool? isFavorite, DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, }) async {
+    final response = await getMapMarkersWithHttpInfo( isArchived: isArchived, isFavorite: isFavorite, fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, );
     if (response.statusCode >= HttpStatus.badRequest) {
       throw ApiException(response.statusCode, await _decodeBodyBytes(response));
     }

+ 1 - 1
mobile/openapi/test/asset_api_test.dart

@@ -102,7 +102,7 @@ void main() {
       // TODO
     });
 
-    //Future<List<MapMarkerResponseDto>> getMapMarkers({ bool isFavorite, DateTime fileCreatedAfter, DateTime fileCreatedBefore }) async
+    //Future<List<MapMarkerResponseDto>> getMapMarkers({ bool isArchived, bool isFavorite, DateTime fileCreatedAfter, DateTime fileCreatedBefore }) async
     test('test getMapMarkers', () async {
       // TODO
     });

+ 8 - 0
server/immich-openapi-specs.json

@@ -1406,6 +1406,14 @@
       "get": {
         "operationId": "getMapMarkers",
         "parameters": [
+          {
+            "name": "isArchived",
+            "required": false,
+            "in": "query",
+            "schema": {
+              "type": "boolean"
+            }
+          },
           {
             "name": "isFavorite",
             "required": false,

+ 1 - 0
server/src/domain/asset/asset.repository.ts

@@ -22,6 +22,7 @@ export interface LivePhotoSearchOptions {
 }
 
 export interface MapMarkerSearchOptions {
+  isArchived?: boolean;
   isFavorite?: boolean;
   fileCreatedBefore?: Date;
   fileCreatedAfter?: Date;

+ 6 - 0
server/src/domain/asset/dto/map-marker.dto.ts

@@ -4,6 +4,12 @@ import { IsBoolean, IsDate } from 'class-validator';
 import { Optional, toBoolean } from '../../domain.util';
 
 export class MapMarkerDto {
+  @ApiProperty()
+  @Optional()
+  @IsBoolean()
+  @Transform(toBoolean)
+  isArchived?: boolean;
+
   @ApiProperty()
   @Optional()
   @IsBoolean()

+ 2 - 2
server/src/infra/repositories/asset.repository.ts

@@ -355,7 +355,7 @@ export class AssetRepository implements IAssetRepository {
   }
 
   async getMapMarkers(ownerId: string, options: MapMarkerSearchOptions = {}): Promise<MapMarker[]> {
-    const { isFavorite, fileCreatedAfter, fileCreatedBefore } = options;
+    const { isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore } = options;
 
     const assets = await this.repository.find({
       select: {
@@ -368,7 +368,7 @@ export class AssetRepository implements IAssetRepository {
       where: {
         ownerId,
         isVisible: true,
-        isArchived: false,
+        isArchived,
         exifInfo: {
           latitude: Not(IsNull()),
           longitude: Not(IsNull()),

+ 59 - 11
server/test/e2e/asset.e2e-spec.ts

@@ -56,6 +56,7 @@ const createAsset = (
     deviceAssetId: `test_${id}`,
     deviceId: 'e2e-test',
     libraryId,
+    isVisible: true,
     fileCreatedAt: createdAt,
     fileModifiedAt: new Date(),
     type: AssetType.IMAGE,
@@ -89,19 +90,25 @@ describe(`${AssetController.name} (e2e)`, () => {
     await api.authApi.adminSignUp(server);
     const admin = await api.authApi.adminLogin(server);
 
-    const libraries = await api.libraryApi.getAll(server, admin.accessToken);
-    const defaultLibrary = libraries[0];
-
-    await api.userApi.create(server, admin.accessToken, user1Dto);
-    user1 = await api.authApi.login(server, { email: user1Dto.email, password: user1Dto.password });
+    const [libraries] = await Promise.all([
+      api.libraryApi.getAll(server, admin.accessToken),
+      api.userApi.create(server, admin.accessToken, user1Dto),
+      api.userApi.create(server, admin.accessToken, user2Dto),
+    ]);
 
-    asset1 = await createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-01-01'));
-    asset2 = await createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-01-02'));
-    asset3 = await createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-02-01'));
+    const defaultLibrary = libraries[0];
 
-    await api.userApi.create(server, admin.accessToken, user2Dto);
-    user2 = await api.authApi.login(server, { email: user2Dto.email, password: user2Dto.password });
-    asset4 = await createAsset(assetRepository, user2, defaultLibrary.id, new Date('1970-01-01'));
+    [user1, user2] = await Promise.all([
+      api.authApi.login(server, { email: user1Dto.email, password: user1Dto.password }),
+      api.authApi.login(server, { email: user2Dto.email, password: user2Dto.password }),
+    ]);
+
+    [asset1, asset2, asset3, asset4] = await Promise.all([
+      createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-01-01')),
+      createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-01-02')),
+      createAsset(assetRepository, user1, defaultLibrary.id, new Date('1970-02-01')),
+      createAsset(assetRepository, user2, defaultLibrary.id, new Date('1970-01-01')),
+    ]);
   });
 
   afterAll(async () => {
@@ -515,4 +522,45 @@ describe(`${AssetController.name} (e2e)`, () => {
       );
     });
   });
+
+  describe('GET /asset/map-marker', () => {
+    beforeEach(async () => {
+      await assetRepository.save({ id: asset1.id, isArchived: true });
+      await assetRepository.upsertExif({ assetId: asset1.id, latitude: 0, longitude: 0 });
+      await assetRepository.upsertExif({ assetId: asset2.id, latitude: 0, longitude: 0 });
+    });
+
+    it('should require authentication', async () => {
+      const { status, body } = await request(server).get('/asset/map-marker');
+
+      expect(status).toBe(401);
+      expect(body).toEqual(errorStub.unauthorized);
+    });
+
+    it('should get map markers for all non-archived assets', async () => {
+      const { status, body } = await request(server)
+        .get('/asset/map-marker')
+        .set('Authorization', `Bearer ${user1.accessToken}`);
+
+      expect(status).toBe(200);
+      expect(body).toHaveLength(2);
+      expect(body).toEqual(
+        expect.arrayContaining([
+          expect.objectContaining({ id: asset1.id }),
+          expect.objectContaining({ id: asset2.id }),
+        ]),
+      );
+    });
+
+    it('should get all map markers', async () => {
+      const { status, body } = await request(server)
+        .get('/asset/map-marker')
+        .set('Authorization', `Bearer ${user1.accessToken}`)
+        .query({ isArchived: false });
+
+      expect(status).toBe(200);
+      expect(body).toHaveLength(1);
+      expect(body).toEqual([expect.objectContaining({ id: asset2.id })]);
+    });
+  });
 });

+ 18 - 5
web/src/api/open-api/api.ts

@@ -6278,13 +6278,14 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
         },
         /**
          * 
+         * @param {boolean} [isArchived] 
          * @param {boolean} [isFavorite] 
          * @param {string} [fileCreatedAfter] 
          * @param {string} [fileCreatedBefore] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        getMapMarkers: async (isFavorite?: boolean, fileCreatedAfter?: string, fileCreatedBefore?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+        getMapMarkers: async (isArchived?: boolean, isFavorite?: boolean, fileCreatedAfter?: string, fileCreatedBefore?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
             const localVarPath = `/asset/map-marker`;
             // use dummy base URL string because the URL constructor only accepts absolute URLs.
             const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
@@ -6306,6 +6307,10 @@ export const AssetApiAxiosParamCreator = function (configuration?: Configuration
             // http bearer authentication required
             await setBearerAuthToObject(localVarHeaderParameter, configuration)
 
+            if (isArchived !== undefined) {
+                localVarQueryParameter['isArchived'] = isArchived;
+            }
+
             if (isFavorite !== undefined) {
                 localVarQueryParameter['isFavorite'] = isFavorite;
             }
@@ -7134,14 +7139,15 @@ export const AssetApiFp = function(configuration?: Configuration) {
         },
         /**
          * 
+         * @param {boolean} [isArchived] 
          * @param {boolean} [isFavorite] 
          * @param {string} [fileCreatedAfter] 
          * @param {string} [fileCreatedBefore] 
          * @param {*} [options] Override http request option.
          * @throws {RequiredError}
          */
-        async getMapMarkers(isFavorite?: boolean, fileCreatedAfter?: string, fileCreatedBefore?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<MapMarkerResponseDto>>> {
-            const localVarAxiosArgs = await localVarAxiosParamCreator.getMapMarkers(isFavorite, fileCreatedAfter, fileCreatedBefore, options);
+        async getMapMarkers(isArchived?: boolean, isFavorite?: boolean, fileCreatedAfter?: string, fileCreatedBefore?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<MapMarkerResponseDto>>> {
+            const localVarAxiosArgs = await localVarAxiosParamCreator.getMapMarkers(isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore, options);
             return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
         },
         /**
@@ -7428,7 +7434,7 @@ export const AssetApiFactory = function (configuration?: Configuration, basePath
          * @throws {RequiredError}
          */
         getMapMarkers(requestParameters: AssetApiGetMapMarkersRequest = {}, options?: AxiosRequestConfig): AxiosPromise<Array<MapMarkerResponseDto>> {
-            return localVarFp.getMapMarkers(requestParameters.isFavorite, requestParameters.fileCreatedAfter, requestParameters.fileCreatedBefore, options).then((request) => request(axios, basePath));
+            return localVarFp.getMapMarkers(requestParameters.isArchived, requestParameters.isFavorite, requestParameters.fileCreatedAfter, requestParameters.fileCreatedBefore, options).then((request) => request(axios, basePath));
         },
         /**
          * 
@@ -7846,6 +7852,13 @@ export interface AssetApiGetDownloadInfoRequest {
  * @interface AssetApiGetMapMarkersRequest
  */
 export interface AssetApiGetMapMarkersRequest {
+    /**
+     * 
+     * @type {boolean}
+     * @memberof AssetApiGetMapMarkers
+     */
+    readonly isArchived?: boolean
+
     /**
      * 
      * @type {boolean}
@@ -8374,7 +8387,7 @@ export class AssetApi extends BaseAPI {
      * @memberof AssetApi
      */
     public getMapMarkers(requestParameters: AssetApiGetMapMarkersRequest = {}, options?: AxiosRequestConfig) {
-        return AssetApiFp(this.configuration).getMapMarkers(requestParameters.isFavorite, requestParameters.fileCreatedAfter, requestParameters.fileCreatedBefore, options).then((request) => request(this.axios, this.basePath));
+        return AssetApiFp(this.configuration).getMapMarkers(requestParameters.isArchived, requestParameters.isFavorite, requestParameters.fileCreatedAfter, requestParameters.fileCreatedBefore, options).then((request) => request(this.axios, this.basePath));
     }
 
     /**

+ 1 - 0
web/src/lib/components/map-page/map-settings-modal.svelte

@@ -32,6 +32,7 @@
     >
       <SettingSwitch title="Allow dark mode" bind:checked={settings.allowDarkMode} />
       <SettingSwitch title="Only favorites" bind:checked={settings.onlyFavorites} />
+      <SettingSwitch title="Include archived" bind:checked={settings.includeArchived} />
       {#if customDateRange}
         <div in:fly={{ y: 10, duration: 200 }} class="flex flex-col gap-4">
           <div class="flex items-center justify-between gap-8">

+ 2 - 0
web/src/lib/stores/preferences.store.ts

@@ -21,6 +21,7 @@ export const locale = persisted<string | undefined>('locale', undefined, {
 
 export interface MapSettings {
   allowDarkMode: boolean;
+  includeArchived: boolean;
   onlyFavorites: boolean;
   relativeDate: string;
   dateAfter: string;
@@ -29,6 +30,7 @@ export interface MapSettings {
 
 export const mapSettings = persisted<MapSettings>('map-settings', {
   allowDarkMode: true,
+  includeArchived: false,
   onlyFavorites: false,
   relativeDate: '',
   dateAfter: '',

+ 2 - 1
web/src/routes/(user)/map/+page.svelte

@@ -44,11 +44,12 @@
     }
     abortController = new AbortController();
 
-    const { onlyFavorites } = $mapSettings;
+    const { includeArchived, onlyFavorites } = $mapSettings;
     const { fileCreatedAfter, fileCreatedBefore } = getFileCreatedDates();
 
     const { data } = await api.assetApi.getMapMarkers(
       {
+        isArchived: includeArchived && undefined,
         isFavorite: onlyFavorites || undefined,
         fileCreatedAfter: fileCreatedAfter || undefined,
         fileCreatedBefore,