diff --git a/mobile/lib/domain/interfaces/sync_stream.interface.dart b/mobile/lib/domain/interfaces/sync_stream.interface.dart
index 5f61d6b52f..502cf0ad46 100644
--- a/mobile/lib/domain/interfaces/sync_stream.interface.dart
+++ b/mobile/lib/domain/interfaces/sync_stream.interface.dart
@@ -15,4 +15,13 @@ abstract interface class ISyncStreamRepository implements IDatabaseRepository {
   Future<void> updatePartnerAssetsV1(Iterable<SyncAssetV1> data);
   Future<void> deletePartnerAssetsV1(Iterable<SyncAssetDeleteV1> data);
   Future<void> updatePartnerAssetsExifV1(Iterable<SyncAssetExifV1> data);
+
+  Future<void> updateAlbumsV1(Iterable<SyncAlbumV1> data);
+  Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data);
+
+  // Future<void> updateAlbumAssetsV1(Iterable<SyncAlbumAssetV1> data);
+  // Future<void> deleteAlbumAssetsV1(Iterable<SyncAlbumAssetV1> data);
+
+  Future<void> updateAlbumUsersV1(Iterable<SyncAlbumUserV1> data);
+  Future<void> deleteAlbumUsersV1(Iterable<SyncAlbumUserDeleteV1> data);
 }
diff --git a/mobile/lib/domain/models/album/album.model.dart b/mobile/lib/domain/models/album/album.model.dart
new file mode 100644
index 0000000000..47683121ba
--- /dev/null
+++ b/mobile/lib/domain/models/album/album.model.dart
@@ -0,0 +1,68 @@
+enum AssetOrder {
+  // do not change this order!
+  asc,
+  desc,
+}
+
+// Model for an album stored in the server
+class Album {
+  final String id;
+  final String name;
+  final String description;
+  final DateTime createdAt;
+  final DateTime updatedAt;
+  final String? thumbnailAssetId;
+  final bool isActivityEnabled;
+  final AssetOrder order;
+
+  const Album({
+    required this.id,
+    required this.name,
+    required this.description,
+    required this.createdAt,
+    required this.updatedAt,
+    this.thumbnailAssetId,
+    required this.isActivityEnabled,
+    required this.order,
+  });
+
+  @override
+  String toString() {
+    return '''Album {
+   id: $id,
+   name: $name,
+   description: $description,
+   createdAt: $createdAt,
+   updatedAt: $updatedAt,
+   isActivityEnabled: $isActivityEnabled,
+   order: $order,
+   thumbnailAssetId: ${thumbnailAssetId ?? "<NA>"}
+ }''';
+  }
+
+  @override
+  bool operator ==(Object other) {
+    if (other is! Album) return false;
+    if (identical(this, other)) return true;
+    return id == other.id &&
+        name == other.name &&
+        description == other.description &&
+        createdAt == other.createdAt &&
+        updatedAt == other.updatedAt &&
+        thumbnailAssetId == other.thumbnailAssetId &&
+        isActivityEnabled == other.isActivityEnabled &&
+        order == other.order;
+  }
+
+  @override
+  int get hashCode {
+    return id.hashCode ^
+        name.hashCode ^
+        description.hashCode ^
+        createdAt.hashCode ^
+        updatedAt.hashCode ^
+        thumbnailAssetId.hashCode ^
+        isActivityEnabled.hashCode ^
+        order.hashCode;
+  }
+}
diff --git a/mobile/lib/domain/models/album_user.model.dart b/mobile/lib/domain/models/album_user.model.dart
new file mode 100644
index 0000000000..10c03a7c4b
--- /dev/null
+++ b/mobile/lib/domain/models/album_user.model.dart
@@ -0,0 +1,5 @@
+enum AlbumUserRole {
+  // do not change this order!
+  editor,
+  viewer,
+}
diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart
index 99d7de6cc5..f997bef139 100644
--- a/mobile/lib/domain/services/sync_stream.service.dart
+++ b/mobile/lib/domain/services/sync_stream.service.dart
@@ -81,6 +81,18 @@ class SyncStreamService {
         return _syncStreamRepository.deletePartnerAssetsV1(data.cast());
       case SyncEntityType.partnerAssetExifV1:
         return _syncStreamRepository.updatePartnerAssetsExifV1(data.cast());
+      case SyncEntityType.albumV1:
+        return _syncStreamRepository.updateAlbumsV1(data.cast());
+      case SyncEntityType.albumDeleteV1:
+        return _syncStreamRepository.deleteAlbumsV1(data.cast());
+      // case SyncEntityType.albumAssetV1:
+      //   return _syncStreamRepository.updateAlbumAssetsV1(data.cast());
+      // case SyncEntityType.albumAssetDeleteV1:
+      //   return _syncStreamRepository.deleteAlbumAssetsV1(data.cast());
+      case SyncEntityType.albumUserV1:
+        return _syncStreamRepository.updateAlbumUsersV1(data.cast());
+      case SyncEntityType.albumUserDeleteV1:
+        return _syncStreamRepository.deleteAlbumUsersV1(data.cast());
       default:
         _logger.warning("Unknown sync data type: $type");
     }
diff --git a/mobile/lib/infrastructure/entities/album_user.entity.dart b/mobile/lib/infrastructure/entities/album_user.entity.dart
new file mode 100644
index 0000000000..c9bbfbd22f
--- /dev/null
+++ b/mobile/lib/infrastructure/entities/album_user.entity.dart
@@ -0,0 +1,20 @@
+import 'package:drift/drift.dart';
+import 'package:immich_mobile/domain/models/album_user.model.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
+import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
+
+class AlbumUserEntity extends Table with DriftDefaultsMixin {
+  const AlbumUserEntity();
+
+  TextColumn get albumId =>
+      text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)();
+
+  TextColumn get userId =>
+      text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
+
+  IntColumn get role => intEnum<AlbumUserRole>()();
+
+  @override
+  Set<Column> get primaryKey => {albumId, userId};
+}
diff --git a/mobile/lib/infrastructure/entities/album_user.entity.drift.dart b/mobile/lib/infrastructure/entities/album_user.entity.drift.dart
new file mode 100644
index 0000000000..008e0bb52b
--- /dev/null
+++ b/mobile/lib/infrastructure/entities/album_user.entity.drift.dart
@@ -0,0 +1,602 @@
+// dart format width=80
+// ignore_for_file: type=lint
+import 'package:drift/drift.dart' as i0;
+import 'package:immich_mobile/infrastructure/entities/album_user.entity.drift.dart'
+    as i1;
+import 'package:immich_mobile/domain/models/album_user.model.dart' as i2;
+import 'package:immich_mobile/infrastructure/entities/album_user.entity.dart'
+    as i3;
+import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
+    as i4;
+import 'package:drift/internal/modular.dart' as i5;
+import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
+    as i6;
+
+typedef $$AlbumUserEntityTableCreateCompanionBuilder
+    = i1.AlbumUserEntityCompanion Function({
+  required String albumId,
+  required String userId,
+  required i2.AlbumUserRole role,
+});
+typedef $$AlbumUserEntityTableUpdateCompanionBuilder
+    = i1.AlbumUserEntityCompanion Function({
+  i0.Value<String> albumId,
+  i0.Value<String> userId,
+  i0.Value<i2.AlbumUserRole> role,
+});
+
+final class $$AlbumUserEntityTableReferences extends i0.BaseReferences<
+    i0.GeneratedDatabase, i1.$AlbumUserEntityTable, i1.AlbumUserEntityData> {
+  $$AlbumUserEntityTableReferences(
+      super.$_db, super.$_table, super.$_typedResult);
+
+  static i4.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) =>
+      i5.ReadDatabaseContainer(db)
+          .resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity')
+          .createAlias(i0.$_aliasNameGenerator(
+              i5.ReadDatabaseContainer(db)
+                  .resultSet<i1.$AlbumUserEntityTable>('album_user_entity')
+                  .albumId,
+              i5.ReadDatabaseContainer(db)
+                  .resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity')
+                  .id));
+
+  i4.$$RemoteAlbumEntityTableProcessedTableManager get albumId {
+    final $_column = $_itemColumn<String>('album_id')!;
+
+    final manager = i4
+        .$$RemoteAlbumEntityTableTableManager(
+            $_db,
+            i5.ReadDatabaseContainer($_db)
+                .resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'))
+        .filter((f) => f.id.sqlEquals($_column));
+    final item = $_typedResult.readTableOrNull(_albumIdTable($_db));
+    if (item == null) return manager;
+    return i0.ProcessedTableManager(
+        manager.$state.copyWith(prefetchedData: [item]));
+  }
+
+  static i6.$UserEntityTable _userIdTable(i0.GeneratedDatabase db) =>
+      i5.ReadDatabaseContainer(db)
+          .resultSet<i6.$UserEntityTable>('user_entity')
+          .createAlias(i0.$_aliasNameGenerator(
+              i5.ReadDatabaseContainer(db)
+                  .resultSet<i1.$AlbumUserEntityTable>('album_user_entity')
+                  .userId,
+              i5.ReadDatabaseContainer(db)
+                  .resultSet<i6.$UserEntityTable>('user_entity')
+                  .id));
+
+  i6.$$UserEntityTableProcessedTableManager get userId {
+    final $_column = $_itemColumn<String>('user_id')!;
+
+    final manager = i6
+        .$$UserEntityTableTableManager(
+            $_db,
+            i5.ReadDatabaseContainer($_db)
+                .resultSet<i6.$UserEntityTable>('user_entity'))
+        .filter((f) => f.id.sqlEquals($_column));
+    final item = $_typedResult.readTableOrNull(_userIdTable($_db));
+    if (item == null) return manager;
+    return i0.ProcessedTableManager(
+        manager.$state.copyWith(prefetchedData: [item]));
+  }
+}
+
+class $$AlbumUserEntityTableFilterComposer
+    extends i0.Composer<i0.GeneratedDatabase, i1.$AlbumUserEntityTable> {
+  $$AlbumUserEntityTableFilterComposer({
+    required super.$db,
+    required super.$table,
+    super.joinBuilder,
+    super.$addJoinBuilderToRootComposer,
+    super.$removeJoinBuilderFromRootComposer,
+  });
+  i0.ColumnWithTypeConverterFilters<i2.AlbumUserRole, i2.AlbumUserRole, int>
+      get role => $composableBuilder(
+          column: $table.role,
+          builder: (column) => i0.ColumnWithTypeConverterFilters(column));
+
+  i4.$$RemoteAlbumEntityTableFilterComposer get albumId {
+    final i4.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder(
+        composer: this,
+        getCurrentColumn: (t) => t.albumId,
+        referencedTable: i5.ReadDatabaseContainer($db)
+            .resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
+        getReferencedColumn: (t) => t.id,
+        builder: (joinBuilder,
+                {$addJoinBuilderToRootComposer,
+                $removeJoinBuilderFromRootComposer}) =>
+            i4.$$RemoteAlbumEntityTableFilterComposer(
+              $db: $db,
+              $table: i5.ReadDatabaseContainer($db)
+                  .resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
+              $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+              joinBuilder: joinBuilder,
+              $removeJoinBuilderFromRootComposer:
+                  $removeJoinBuilderFromRootComposer,
+            ));
+    return composer;
+  }
+
+  i6.$$UserEntityTableFilterComposer get userId {
+    final i6.$$UserEntityTableFilterComposer composer = $composerBuilder(
+        composer: this,
+        getCurrentColumn: (t) => t.userId,
+        referencedTable: i5.ReadDatabaseContainer($db)
+            .resultSet<i6.$UserEntityTable>('user_entity'),
+        getReferencedColumn: (t) => t.id,
+        builder: (joinBuilder,
+                {$addJoinBuilderToRootComposer,
+                $removeJoinBuilderFromRootComposer}) =>
+            i6.$$UserEntityTableFilterComposer(
+              $db: $db,
+              $table: i5.ReadDatabaseContainer($db)
+                  .resultSet<i6.$UserEntityTable>('user_entity'),
+              $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+              joinBuilder: joinBuilder,
+              $removeJoinBuilderFromRootComposer:
+                  $removeJoinBuilderFromRootComposer,
+            ));
+    return composer;
+  }
+}
+
+class $$AlbumUserEntityTableOrderingComposer
+    extends i0.Composer<i0.GeneratedDatabase, i1.$AlbumUserEntityTable> {
+  $$AlbumUserEntityTableOrderingComposer({
+    required super.$db,
+    required super.$table,
+    super.joinBuilder,
+    super.$addJoinBuilderToRootComposer,
+    super.$removeJoinBuilderFromRootComposer,
+  });
+  i0.ColumnOrderings<int> get role => $composableBuilder(
+      column: $table.role, builder: (column) => i0.ColumnOrderings(column));
+
+  i4.$$RemoteAlbumEntityTableOrderingComposer get albumId {
+    final i4.$$RemoteAlbumEntityTableOrderingComposer composer =
+        $composerBuilder(
+            composer: this,
+            getCurrentColumn: (t) => t.albumId,
+            referencedTable: i5.ReadDatabaseContainer($db)
+                .resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
+            getReferencedColumn: (t) => t.id,
+            builder: (joinBuilder,
+                    {$addJoinBuilderToRootComposer,
+                    $removeJoinBuilderFromRootComposer}) =>
+                i4.$$RemoteAlbumEntityTableOrderingComposer(
+                  $db: $db,
+                  $table: i5.ReadDatabaseContainer($db)
+                      .resultSet<i4.$RemoteAlbumEntityTable>(
+                          'remote_album_entity'),
+                  $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+                  joinBuilder: joinBuilder,
+                  $removeJoinBuilderFromRootComposer:
+                      $removeJoinBuilderFromRootComposer,
+                ));
+    return composer;
+  }
+
+  i6.$$UserEntityTableOrderingComposer get userId {
+    final i6.$$UserEntityTableOrderingComposer composer = $composerBuilder(
+        composer: this,
+        getCurrentColumn: (t) => t.userId,
+        referencedTable: i5.ReadDatabaseContainer($db)
+            .resultSet<i6.$UserEntityTable>('user_entity'),
+        getReferencedColumn: (t) => t.id,
+        builder: (joinBuilder,
+                {$addJoinBuilderToRootComposer,
+                $removeJoinBuilderFromRootComposer}) =>
+            i6.$$UserEntityTableOrderingComposer(
+              $db: $db,
+              $table: i5.ReadDatabaseContainer($db)
+                  .resultSet<i6.$UserEntityTable>('user_entity'),
+              $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+              joinBuilder: joinBuilder,
+              $removeJoinBuilderFromRootComposer:
+                  $removeJoinBuilderFromRootComposer,
+            ));
+    return composer;
+  }
+}
+
+class $$AlbumUserEntityTableAnnotationComposer
+    extends i0.Composer<i0.GeneratedDatabase, i1.$AlbumUserEntityTable> {
+  $$AlbumUserEntityTableAnnotationComposer({
+    required super.$db,
+    required super.$table,
+    super.joinBuilder,
+    super.$addJoinBuilderToRootComposer,
+    super.$removeJoinBuilderFromRootComposer,
+  });
+  i0.GeneratedColumnWithTypeConverter<i2.AlbumUserRole, int> get role =>
+      $composableBuilder(column: $table.role, builder: (column) => column);
+
+  i4.$$RemoteAlbumEntityTableAnnotationComposer get albumId {
+    final i4.$$RemoteAlbumEntityTableAnnotationComposer composer =
+        $composerBuilder(
+            composer: this,
+            getCurrentColumn: (t) => t.albumId,
+            referencedTable: i5.ReadDatabaseContainer($db)
+                .resultSet<i4.$RemoteAlbumEntityTable>('remote_album_entity'),
+            getReferencedColumn: (t) => t.id,
+            builder: (joinBuilder,
+                    {$addJoinBuilderToRootComposer,
+                    $removeJoinBuilderFromRootComposer}) =>
+                i4.$$RemoteAlbumEntityTableAnnotationComposer(
+                  $db: $db,
+                  $table: i5.ReadDatabaseContainer($db)
+                      .resultSet<i4.$RemoteAlbumEntityTable>(
+                          'remote_album_entity'),
+                  $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+                  joinBuilder: joinBuilder,
+                  $removeJoinBuilderFromRootComposer:
+                      $removeJoinBuilderFromRootComposer,
+                ));
+    return composer;
+  }
+
+  i6.$$UserEntityTableAnnotationComposer get userId {
+    final i6.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
+        composer: this,
+        getCurrentColumn: (t) => t.userId,
+        referencedTable: i5.ReadDatabaseContainer($db)
+            .resultSet<i6.$UserEntityTable>('user_entity'),
+        getReferencedColumn: (t) => t.id,
+        builder: (joinBuilder,
+                {$addJoinBuilderToRootComposer,
+                $removeJoinBuilderFromRootComposer}) =>
+            i6.$$UserEntityTableAnnotationComposer(
+              $db: $db,
+              $table: i5.ReadDatabaseContainer($db)
+                  .resultSet<i6.$UserEntityTable>('user_entity'),
+              $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+              joinBuilder: joinBuilder,
+              $removeJoinBuilderFromRootComposer:
+                  $removeJoinBuilderFromRootComposer,
+            ));
+    return composer;
+  }
+}
+
+class $$AlbumUserEntityTableTableManager extends i0.RootTableManager<
+    i0.GeneratedDatabase,
+    i1.$AlbumUserEntityTable,
+    i1.AlbumUserEntityData,
+    i1.$$AlbumUserEntityTableFilterComposer,
+    i1.$$AlbumUserEntityTableOrderingComposer,
+    i1.$$AlbumUserEntityTableAnnotationComposer,
+    $$AlbumUserEntityTableCreateCompanionBuilder,
+    $$AlbumUserEntityTableUpdateCompanionBuilder,
+    (i1.AlbumUserEntityData, i1.$$AlbumUserEntityTableReferences),
+    i1.AlbumUserEntityData,
+    i0.PrefetchHooks Function({bool albumId, bool userId})> {
+  $$AlbumUserEntityTableTableManager(
+      i0.GeneratedDatabase db, i1.$AlbumUserEntityTable table)
+      : super(i0.TableManagerState(
+          db: db,
+          table: table,
+          createFilteringComposer: () =>
+              i1.$$AlbumUserEntityTableFilterComposer($db: db, $table: table),
+          createOrderingComposer: () =>
+              i1.$$AlbumUserEntityTableOrderingComposer($db: db, $table: table),
+          createComputedFieldComposer: () => i1
+              .$$AlbumUserEntityTableAnnotationComposer($db: db, $table: table),
+          updateCompanionCallback: ({
+            i0.Value<String> albumId = const i0.Value.absent(),
+            i0.Value<String> userId = const i0.Value.absent(),
+            i0.Value<i2.AlbumUserRole> role = const i0.Value.absent(),
+          }) =>
+              i1.AlbumUserEntityCompanion(
+            albumId: albumId,
+            userId: userId,
+            role: role,
+          ),
+          createCompanionCallback: ({
+            required String albumId,
+            required String userId,
+            required i2.AlbumUserRole role,
+          }) =>
+              i1.AlbumUserEntityCompanion.insert(
+            albumId: albumId,
+            userId: userId,
+            role: role,
+          ),
+          withReferenceMapper: (p0) => p0
+              .map((e) => (
+                    e.readTable(table),
+                    i1.$$AlbumUserEntityTableReferences(db, table, e)
+                  ))
+              .toList(),
+          prefetchHooksCallback: ({albumId = false, userId = false}) {
+            return i0.PrefetchHooks(
+              db: db,
+              explicitlyWatchedTables: [],
+              addJoins: <
+                  T extends i0.TableManagerState<
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic>>(state) {
+                if (albumId) {
+                  state = state.withJoin(
+                    currentTable: table,
+                    currentColumn: table.albumId,
+                    referencedTable:
+                        i1.$$AlbumUserEntityTableReferences._albumIdTable(db),
+                    referencedColumn: i1.$$AlbumUserEntityTableReferences
+                        ._albumIdTable(db)
+                        .id,
+                  ) as T;
+                }
+                if (userId) {
+                  state = state.withJoin(
+                    currentTable: table,
+                    currentColumn: table.userId,
+                    referencedTable:
+                        i1.$$AlbumUserEntityTableReferences._userIdTable(db),
+                    referencedColumn:
+                        i1.$$AlbumUserEntityTableReferences._userIdTable(db).id,
+                  ) as T;
+                }
+
+                return state;
+              },
+              getPrefetchedDataCallback: (items) async {
+                return [];
+              },
+            );
+          },
+        ));
+}
+
+typedef $$AlbumUserEntityTableProcessedTableManager = i0.ProcessedTableManager<
+    i0.GeneratedDatabase,
+    i1.$AlbumUserEntityTable,
+    i1.AlbumUserEntityData,
+    i1.$$AlbumUserEntityTableFilterComposer,
+    i1.$$AlbumUserEntityTableOrderingComposer,
+    i1.$$AlbumUserEntityTableAnnotationComposer,
+    $$AlbumUserEntityTableCreateCompanionBuilder,
+    $$AlbumUserEntityTableUpdateCompanionBuilder,
+    (i1.AlbumUserEntityData, i1.$$AlbumUserEntityTableReferences),
+    i1.AlbumUserEntityData,
+    i0.PrefetchHooks Function({bool albumId, bool userId})>;
+
+class $AlbumUserEntityTable extends i3.AlbumUserEntity
+    with i0.TableInfo<$AlbumUserEntityTable, i1.AlbumUserEntityData> {
+  @override
+  final i0.GeneratedDatabase attachedDatabase;
+  final String? _alias;
+  $AlbumUserEntityTable(this.attachedDatabase, [this._alias]);
+  static const i0.VerificationMeta _albumIdMeta =
+      const i0.VerificationMeta('albumId');
+  @override
+  late final i0.GeneratedColumn<String> albumId = i0.GeneratedColumn<String>(
+      'album_id', aliasedName, false,
+      type: i0.DriftSqlType.string,
+      requiredDuringInsert: true,
+      defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
+          'REFERENCES remote_album_entity (id) ON DELETE CASCADE'));
+  static const i0.VerificationMeta _userIdMeta =
+      const i0.VerificationMeta('userId');
+  @override
+  late final i0.GeneratedColumn<String> userId = i0.GeneratedColumn<String>(
+      'user_id', aliasedName, false,
+      type: i0.DriftSqlType.string,
+      requiredDuringInsert: true,
+      defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
+          'REFERENCES user_entity (id) ON DELETE CASCADE'));
+  @override
+  late final i0.GeneratedColumnWithTypeConverter<i2.AlbumUserRole, int> role =
+      i0.GeneratedColumn<int>('role', aliasedName, false,
+              type: i0.DriftSqlType.int, requiredDuringInsert: true)
+          .withConverter<i2.AlbumUserRole>(
+              i1.$AlbumUserEntityTable.$converterrole);
+  @override
+  List<i0.GeneratedColumn> get $columns => [albumId, userId, role];
+  @override
+  String get aliasedName => _alias ?? actualTableName;
+  @override
+  String get actualTableName => $name;
+  static const String $name = 'album_user_entity';
+  @override
+  i0.VerificationContext validateIntegrity(
+      i0.Insertable<i1.AlbumUserEntityData> instance,
+      {bool isInserting = false}) {
+    final context = i0.VerificationContext();
+    final data = instance.toColumns(true);
+    if (data.containsKey('album_id')) {
+      context.handle(_albumIdMeta,
+          albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta));
+    } else if (isInserting) {
+      context.missing(_albumIdMeta);
+    }
+    if (data.containsKey('user_id')) {
+      context.handle(_userIdMeta,
+          userId.isAcceptableOrUnknown(data['user_id']!, _userIdMeta));
+    } else if (isInserting) {
+      context.missing(_userIdMeta);
+    }
+    return context;
+  }
+
+  @override
+  Set<i0.GeneratedColumn> get $primaryKey => {albumId, userId};
+  @override
+  i1.AlbumUserEntityData map(Map<String, dynamic> data, {String? tablePrefix}) {
+    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+    return i1.AlbumUserEntityData(
+      albumId: attachedDatabase.typeMapping
+          .read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!,
+      userId: attachedDatabase.typeMapping
+          .read(i0.DriftSqlType.string, data['${effectivePrefix}user_id'])!,
+      role: i1.$AlbumUserEntityTable.$converterrole.fromSql(attachedDatabase
+          .typeMapping
+          .read(i0.DriftSqlType.int, data['${effectivePrefix}role'])!),
+    );
+  }
+
+  @override
+  $AlbumUserEntityTable createAlias(String alias) {
+    return $AlbumUserEntityTable(attachedDatabase, alias);
+  }
+
+  static i0.JsonTypeConverter2<i2.AlbumUserRole, int, int> $converterrole =
+      const i0.EnumIndexConverter<i2.AlbumUserRole>(i2.AlbumUserRole.values);
+  @override
+  bool get withoutRowId => true;
+  @override
+  bool get isStrict => true;
+}
+
+class AlbumUserEntityData extends i0.DataClass
+    implements i0.Insertable<i1.AlbumUserEntityData> {
+  final String albumId;
+  final String userId;
+  final i2.AlbumUserRole role;
+  const AlbumUserEntityData(
+      {required this.albumId, required this.userId, required this.role});
+  @override
+  Map<String, i0.Expression> toColumns(bool nullToAbsent) {
+    final map = <String, i0.Expression>{};
+    map['album_id'] = i0.Variable<String>(albumId);
+    map['user_id'] = i0.Variable<String>(userId);
+    {
+      map['role'] =
+          i0.Variable<int>(i1.$AlbumUserEntityTable.$converterrole.toSql(role));
+    }
+    return map;
+  }
+
+  factory AlbumUserEntityData.fromJson(Map<String, dynamic> json,
+      {i0.ValueSerializer? serializer}) {
+    serializer ??= i0.driftRuntimeOptions.defaultSerializer;
+    return AlbumUserEntityData(
+      albumId: serializer.fromJson<String>(json['albumId']),
+      userId: serializer.fromJson<String>(json['userId']),
+      role: i1.$AlbumUserEntityTable.$converterrole
+          .fromJson(serializer.fromJson<int>(json['role'])),
+    );
+  }
+  @override
+  Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
+    serializer ??= i0.driftRuntimeOptions.defaultSerializer;
+    return <String, dynamic>{
+      'albumId': serializer.toJson<String>(albumId),
+      'userId': serializer.toJson<String>(userId),
+      'role': serializer
+          .toJson<int>(i1.$AlbumUserEntityTable.$converterrole.toJson(role)),
+    };
+  }
+
+  i1.AlbumUserEntityData copyWith(
+          {String? albumId, String? userId, i2.AlbumUserRole? role}) =>
+      i1.AlbumUserEntityData(
+        albumId: albumId ?? this.albumId,
+        userId: userId ?? this.userId,
+        role: role ?? this.role,
+      );
+  AlbumUserEntityData copyWithCompanion(i1.AlbumUserEntityCompanion data) {
+    return AlbumUserEntityData(
+      albumId: data.albumId.present ? data.albumId.value : this.albumId,
+      userId: data.userId.present ? data.userId.value : this.userId,
+      role: data.role.present ? data.role.value : this.role,
+    );
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('AlbumUserEntityData(')
+          ..write('albumId: $albumId, ')
+          ..write('userId: $userId, ')
+          ..write('role: $role')
+          ..write(')'))
+        .toString();
+  }
+
+  @override
+  int get hashCode => Object.hash(albumId, userId, role);
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      (other is i1.AlbumUserEntityData &&
+          other.albumId == this.albumId &&
+          other.userId == this.userId &&
+          other.role == this.role);
+}
+
+class AlbumUserEntityCompanion
+    extends i0.UpdateCompanion<i1.AlbumUserEntityData> {
+  final i0.Value<String> albumId;
+  final i0.Value<String> userId;
+  final i0.Value<i2.AlbumUserRole> role;
+  const AlbumUserEntityCompanion({
+    this.albumId = const i0.Value.absent(),
+    this.userId = const i0.Value.absent(),
+    this.role = const i0.Value.absent(),
+  });
+  AlbumUserEntityCompanion.insert({
+    required String albumId,
+    required String userId,
+    required i2.AlbumUserRole role,
+  })  : albumId = i0.Value(albumId),
+        userId = i0.Value(userId),
+        role = i0.Value(role);
+  static i0.Insertable<i1.AlbumUserEntityData> custom({
+    i0.Expression<String>? albumId,
+    i0.Expression<String>? userId,
+    i0.Expression<int>? role,
+  }) {
+    return i0.RawValuesInsertable({
+      if (albumId != null) 'album_id': albumId,
+      if (userId != null) 'user_id': userId,
+      if (role != null) 'role': role,
+    });
+  }
+
+  i1.AlbumUserEntityCompanion copyWith(
+      {i0.Value<String>? albumId,
+      i0.Value<String>? userId,
+      i0.Value<i2.AlbumUserRole>? role}) {
+    return i1.AlbumUserEntityCompanion(
+      albumId: albumId ?? this.albumId,
+      userId: userId ?? this.userId,
+      role: role ?? this.role,
+    );
+  }
+
+  @override
+  Map<String, i0.Expression> toColumns(bool nullToAbsent) {
+    final map = <String, i0.Expression>{};
+    if (albumId.present) {
+      map['album_id'] = i0.Variable<String>(albumId.value);
+    }
+    if (userId.present) {
+      map['user_id'] = i0.Variable<String>(userId.value);
+    }
+    if (role.present) {
+      map['role'] = i0.Variable<int>(
+          i1.$AlbumUserEntityTable.$converterrole.toSql(role.value));
+    }
+    return map;
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('AlbumUserEntityCompanion(')
+          ..write('albumId: $albumId, ')
+          ..write('userId: $userId, ')
+          ..write('role: $role')
+          ..write(')'))
+        .toString();
+  }
+}
diff --git a/mobile/lib/infrastructure/entities/remote_album.entity.dart b/mobile/lib/infrastructure/entities/remote_album.entity.dart
new file mode 100644
index 0000000000..7f9f1efa67
--- /dev/null
+++ b/mobile/lib/infrastructure/entities/remote_album.entity.dart
@@ -0,0 +1,34 @@
+import 'package:drift/drift.dart';
+import 'package:immich_mobile/domain/models/album/album.model.dart';
+import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
+import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
+
+class RemoteAlbumEntity extends Table with DriftDefaultsMixin {
+  const RemoteAlbumEntity();
+
+  TextColumn get id => text()();
+
+  TextColumn get name => text()();
+
+  TextColumn get description => text()();
+
+  DateTimeColumn get createdAt => dateTime().withDefault(currentDateAndTime)();
+
+  DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
+
+  TextColumn get ownerId =>
+      text().references(UserEntity, #id, onDelete: KeyAction.cascade)();
+
+  TextColumn get thumbnailAssetId => text()
+      .references(RemoteAssetEntity, #id, onDelete: KeyAction.setNull)
+      .nullable()();
+
+  BoolColumn get isActivityEnabled =>
+      boolean().withDefault(const Constant(true))();
+
+  IntColumn get order => intEnum<AssetOrder>()();
+
+  @override
+  Set<Column> get primaryKey => {id};
+}
diff --git a/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart
new file mode 100644
index 0000000000..cc7b642c9b
--- /dev/null
+++ b/mobile/lib/infrastructure/entities/remote_album.entity.drift.dart
@@ -0,0 +1,946 @@
+// dart format width=80
+// ignore_for_file: type=lint
+import 'package:drift/drift.dart' as i0;
+import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
+    as i1;
+import 'package:immich_mobile/domain/models/album/album.model.dart' as i2;
+import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart'
+    as i3;
+import 'package:drift/src/runtime/query_builder/query_builder.dart' as i4;
+import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'
+    as i5;
+import 'package:drift/internal/modular.dart' as i6;
+import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
+    as i7;
+
+typedef $$RemoteAlbumEntityTableCreateCompanionBuilder
+    = i1.RemoteAlbumEntityCompanion Function({
+  required String id,
+  required String name,
+  required String description,
+  i0.Value<DateTime> createdAt,
+  i0.Value<DateTime> updatedAt,
+  required String ownerId,
+  i0.Value<String?> thumbnailAssetId,
+  i0.Value<bool> isActivityEnabled,
+  required i2.AssetOrder order,
+});
+typedef $$RemoteAlbumEntityTableUpdateCompanionBuilder
+    = i1.RemoteAlbumEntityCompanion Function({
+  i0.Value<String> id,
+  i0.Value<String> name,
+  i0.Value<String> description,
+  i0.Value<DateTime> createdAt,
+  i0.Value<DateTime> updatedAt,
+  i0.Value<String> ownerId,
+  i0.Value<String?> thumbnailAssetId,
+  i0.Value<bool> isActivityEnabled,
+  i0.Value<i2.AssetOrder> order,
+});
+
+final class $$RemoteAlbumEntityTableReferences extends i0.BaseReferences<
+    i0.GeneratedDatabase,
+    i1.$RemoteAlbumEntityTable,
+    i1.RemoteAlbumEntityData> {
+  $$RemoteAlbumEntityTableReferences(
+      super.$_db, super.$_table, super.$_typedResult);
+
+  static i5.$UserEntityTable _ownerIdTable(i0.GeneratedDatabase db) =>
+      i6.ReadDatabaseContainer(db)
+          .resultSet<i5.$UserEntityTable>('user_entity')
+          .createAlias(i0.$_aliasNameGenerator(
+              i6.ReadDatabaseContainer(db)
+                  .resultSet<i1.$RemoteAlbumEntityTable>('remote_album_entity')
+                  .ownerId,
+              i6.ReadDatabaseContainer(db)
+                  .resultSet<i5.$UserEntityTable>('user_entity')
+                  .id));
+
+  i5.$$UserEntityTableProcessedTableManager get ownerId {
+    final $_column = $_itemColumn<String>('owner_id')!;
+
+    final manager = i5
+        .$$UserEntityTableTableManager(
+            $_db,
+            i6.ReadDatabaseContainer($_db)
+                .resultSet<i5.$UserEntityTable>('user_entity'))
+        .filter((f) => f.id.sqlEquals($_column));
+    final item = $_typedResult.readTableOrNull(_ownerIdTable($_db));
+    if (item == null) return manager;
+    return i0.ProcessedTableManager(
+        manager.$state.copyWith(prefetchedData: [item]));
+  }
+
+  static i7.$RemoteAssetEntityTable _thumbnailAssetIdTable(
+          i0.GeneratedDatabase db) =>
+      i6.ReadDatabaseContainer(db)
+          .resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity')
+          .createAlias(i0.$_aliasNameGenerator(
+              i6.ReadDatabaseContainer(db)
+                  .resultSet<i1.$RemoteAlbumEntityTable>('remote_album_entity')
+                  .thumbnailAssetId,
+              i6.ReadDatabaseContainer(db)
+                  .resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity')
+                  .id));
+
+  i7.$$RemoteAssetEntityTableProcessedTableManager? get thumbnailAssetId {
+    final $_column = $_itemColumn<String>('thumbnail_asset_id');
+    if ($_column == null) return null;
+    final manager = i7
+        .$$RemoteAssetEntityTableTableManager(
+            $_db,
+            i6.ReadDatabaseContainer($_db)
+                .resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'))
+        .filter((f) => f.id.sqlEquals($_column));
+    final item = $_typedResult.readTableOrNull(_thumbnailAssetIdTable($_db));
+    if (item == null) return manager;
+    return i0.ProcessedTableManager(
+        manager.$state.copyWith(prefetchedData: [item]));
+  }
+}
+
+class $$RemoteAlbumEntityTableFilterComposer
+    extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumEntityTable> {
+  $$RemoteAlbumEntityTableFilterComposer({
+    required super.$db,
+    required super.$table,
+    super.joinBuilder,
+    super.$addJoinBuilderToRootComposer,
+    super.$removeJoinBuilderFromRootComposer,
+  });
+  i0.ColumnFilters<String> get id => $composableBuilder(
+      column: $table.id, builder: (column) => i0.ColumnFilters(column));
+
+  i0.ColumnFilters<String> get name => $composableBuilder(
+      column: $table.name, builder: (column) => i0.ColumnFilters(column));
+
+  i0.ColumnFilters<String> get description => $composableBuilder(
+      column: $table.description,
+      builder: (column) => i0.ColumnFilters(column));
+
+  i0.ColumnFilters<DateTime> get createdAt => $composableBuilder(
+      column: $table.createdAt, builder: (column) => i0.ColumnFilters(column));
+
+  i0.ColumnFilters<DateTime> get updatedAt => $composableBuilder(
+      column: $table.updatedAt, builder: (column) => i0.ColumnFilters(column));
+
+  i0.ColumnFilters<bool> get isActivityEnabled => $composableBuilder(
+      column: $table.isActivityEnabled,
+      builder: (column) => i0.ColumnFilters(column));
+
+  i0.ColumnWithTypeConverterFilters<i2.AssetOrder, i2.AssetOrder, int>
+      get order => $composableBuilder(
+          column: $table.order,
+          builder: (column) => i0.ColumnWithTypeConverterFilters(column));
+
+  i5.$$UserEntityTableFilterComposer get ownerId {
+    final i5.$$UserEntityTableFilterComposer composer = $composerBuilder(
+        composer: this,
+        getCurrentColumn: (t) => t.ownerId,
+        referencedTable: i6.ReadDatabaseContainer($db)
+            .resultSet<i5.$UserEntityTable>('user_entity'),
+        getReferencedColumn: (t) => t.id,
+        builder: (joinBuilder,
+                {$addJoinBuilderToRootComposer,
+                $removeJoinBuilderFromRootComposer}) =>
+            i5.$$UserEntityTableFilterComposer(
+              $db: $db,
+              $table: i6.ReadDatabaseContainer($db)
+                  .resultSet<i5.$UserEntityTable>('user_entity'),
+              $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+              joinBuilder: joinBuilder,
+              $removeJoinBuilderFromRootComposer:
+                  $removeJoinBuilderFromRootComposer,
+            ));
+    return composer;
+  }
+
+  i7.$$RemoteAssetEntityTableFilterComposer get thumbnailAssetId {
+    final i7.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
+        composer: this,
+        getCurrentColumn: (t) => t.thumbnailAssetId,
+        referencedTable: i6.ReadDatabaseContainer($db)
+            .resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
+        getReferencedColumn: (t) => t.id,
+        builder: (joinBuilder,
+                {$addJoinBuilderToRootComposer,
+                $removeJoinBuilderFromRootComposer}) =>
+            i7.$$RemoteAssetEntityTableFilterComposer(
+              $db: $db,
+              $table: i6.ReadDatabaseContainer($db)
+                  .resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
+              $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+              joinBuilder: joinBuilder,
+              $removeJoinBuilderFromRootComposer:
+                  $removeJoinBuilderFromRootComposer,
+            ));
+    return composer;
+  }
+}
+
+class $$RemoteAlbumEntityTableOrderingComposer
+    extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumEntityTable> {
+  $$RemoteAlbumEntityTableOrderingComposer({
+    required super.$db,
+    required super.$table,
+    super.joinBuilder,
+    super.$addJoinBuilderToRootComposer,
+    super.$removeJoinBuilderFromRootComposer,
+  });
+  i0.ColumnOrderings<String> get id => $composableBuilder(
+      column: $table.id, builder: (column) => i0.ColumnOrderings(column));
+
+  i0.ColumnOrderings<String> get name => $composableBuilder(
+      column: $table.name, builder: (column) => i0.ColumnOrderings(column));
+
+  i0.ColumnOrderings<String> get description => $composableBuilder(
+      column: $table.description,
+      builder: (column) => i0.ColumnOrderings(column));
+
+  i0.ColumnOrderings<DateTime> get createdAt => $composableBuilder(
+      column: $table.createdAt,
+      builder: (column) => i0.ColumnOrderings(column));
+
+  i0.ColumnOrderings<DateTime> get updatedAt => $composableBuilder(
+      column: $table.updatedAt,
+      builder: (column) => i0.ColumnOrderings(column));
+
+  i0.ColumnOrderings<bool> get isActivityEnabled => $composableBuilder(
+      column: $table.isActivityEnabled,
+      builder: (column) => i0.ColumnOrderings(column));
+
+  i0.ColumnOrderings<int> get order => $composableBuilder(
+      column: $table.order, builder: (column) => i0.ColumnOrderings(column));
+
+  i5.$$UserEntityTableOrderingComposer get ownerId {
+    final i5.$$UserEntityTableOrderingComposer composer = $composerBuilder(
+        composer: this,
+        getCurrentColumn: (t) => t.ownerId,
+        referencedTable: i6.ReadDatabaseContainer($db)
+            .resultSet<i5.$UserEntityTable>('user_entity'),
+        getReferencedColumn: (t) => t.id,
+        builder: (joinBuilder,
+                {$addJoinBuilderToRootComposer,
+                $removeJoinBuilderFromRootComposer}) =>
+            i5.$$UserEntityTableOrderingComposer(
+              $db: $db,
+              $table: i6.ReadDatabaseContainer($db)
+                  .resultSet<i5.$UserEntityTable>('user_entity'),
+              $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+              joinBuilder: joinBuilder,
+              $removeJoinBuilderFromRootComposer:
+                  $removeJoinBuilderFromRootComposer,
+            ));
+    return composer;
+  }
+
+  i7.$$RemoteAssetEntityTableOrderingComposer get thumbnailAssetId {
+    final i7.$$RemoteAssetEntityTableOrderingComposer composer =
+        $composerBuilder(
+            composer: this,
+            getCurrentColumn: (t) => t.thumbnailAssetId,
+            referencedTable: i6.ReadDatabaseContainer($db)
+                .resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
+            getReferencedColumn: (t) => t.id,
+            builder: (joinBuilder,
+                    {$addJoinBuilderToRootComposer,
+                    $removeJoinBuilderFromRootComposer}) =>
+                i7.$$RemoteAssetEntityTableOrderingComposer(
+                  $db: $db,
+                  $table: i6.ReadDatabaseContainer($db)
+                      .resultSet<i7.$RemoteAssetEntityTable>(
+                          'remote_asset_entity'),
+                  $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+                  joinBuilder: joinBuilder,
+                  $removeJoinBuilderFromRootComposer:
+                      $removeJoinBuilderFromRootComposer,
+                ));
+    return composer;
+  }
+}
+
+class $$RemoteAlbumEntityTableAnnotationComposer
+    extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumEntityTable> {
+  $$RemoteAlbumEntityTableAnnotationComposer({
+    required super.$db,
+    required super.$table,
+    super.joinBuilder,
+    super.$addJoinBuilderToRootComposer,
+    super.$removeJoinBuilderFromRootComposer,
+  });
+  i0.GeneratedColumn<String> get id =>
+      $composableBuilder(column: $table.id, builder: (column) => column);
+
+  i0.GeneratedColumn<String> get name =>
+      $composableBuilder(column: $table.name, builder: (column) => column);
+
+  i0.GeneratedColumn<String> get description => $composableBuilder(
+      column: $table.description, builder: (column) => column);
+
+  i0.GeneratedColumn<DateTime> get createdAt =>
+      $composableBuilder(column: $table.createdAt, builder: (column) => column);
+
+  i0.GeneratedColumn<DateTime> get updatedAt =>
+      $composableBuilder(column: $table.updatedAt, builder: (column) => column);
+
+  i0.GeneratedColumn<bool> get isActivityEnabled => $composableBuilder(
+      column: $table.isActivityEnabled, builder: (column) => column);
+
+  i0.GeneratedColumnWithTypeConverter<i2.AssetOrder, int> get order =>
+      $composableBuilder(column: $table.order, builder: (column) => column);
+
+  i5.$$UserEntityTableAnnotationComposer get ownerId {
+    final i5.$$UserEntityTableAnnotationComposer composer = $composerBuilder(
+        composer: this,
+        getCurrentColumn: (t) => t.ownerId,
+        referencedTable: i6.ReadDatabaseContainer($db)
+            .resultSet<i5.$UserEntityTable>('user_entity'),
+        getReferencedColumn: (t) => t.id,
+        builder: (joinBuilder,
+                {$addJoinBuilderToRootComposer,
+                $removeJoinBuilderFromRootComposer}) =>
+            i5.$$UserEntityTableAnnotationComposer(
+              $db: $db,
+              $table: i6.ReadDatabaseContainer($db)
+                  .resultSet<i5.$UserEntityTable>('user_entity'),
+              $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+              joinBuilder: joinBuilder,
+              $removeJoinBuilderFromRootComposer:
+                  $removeJoinBuilderFromRootComposer,
+            ));
+    return composer;
+  }
+
+  i7.$$RemoteAssetEntityTableAnnotationComposer get thumbnailAssetId {
+    final i7.$$RemoteAssetEntityTableAnnotationComposer composer =
+        $composerBuilder(
+            composer: this,
+            getCurrentColumn: (t) => t.thumbnailAssetId,
+            referencedTable: i6.ReadDatabaseContainer($db)
+                .resultSet<i7.$RemoteAssetEntityTable>('remote_asset_entity'),
+            getReferencedColumn: (t) => t.id,
+            builder: (joinBuilder,
+                    {$addJoinBuilderToRootComposer,
+                    $removeJoinBuilderFromRootComposer}) =>
+                i7.$$RemoteAssetEntityTableAnnotationComposer(
+                  $db: $db,
+                  $table: i6.ReadDatabaseContainer($db)
+                      .resultSet<i7.$RemoteAssetEntityTable>(
+                          'remote_asset_entity'),
+                  $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+                  joinBuilder: joinBuilder,
+                  $removeJoinBuilderFromRootComposer:
+                      $removeJoinBuilderFromRootComposer,
+                ));
+    return composer;
+  }
+}
+
+class $$RemoteAlbumEntityTableTableManager extends i0.RootTableManager<
+    i0.GeneratedDatabase,
+    i1.$RemoteAlbumEntityTable,
+    i1.RemoteAlbumEntityData,
+    i1.$$RemoteAlbumEntityTableFilterComposer,
+    i1.$$RemoteAlbumEntityTableOrderingComposer,
+    i1.$$RemoteAlbumEntityTableAnnotationComposer,
+    $$RemoteAlbumEntityTableCreateCompanionBuilder,
+    $$RemoteAlbumEntityTableUpdateCompanionBuilder,
+    (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences),
+    i1.RemoteAlbumEntityData,
+    i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})> {
+  $$RemoteAlbumEntityTableTableManager(
+      i0.GeneratedDatabase db, i1.$RemoteAlbumEntityTable table)
+      : super(i0.TableManagerState(
+          db: db,
+          table: table,
+          createFilteringComposer: () =>
+              i1.$$RemoteAlbumEntityTableFilterComposer($db: db, $table: table),
+          createOrderingComposer: () => i1
+              .$$RemoteAlbumEntityTableOrderingComposer($db: db, $table: table),
+          createComputedFieldComposer: () =>
+              i1.$$RemoteAlbumEntityTableAnnotationComposer(
+                  $db: db, $table: table),
+          updateCompanionCallback: ({
+            i0.Value<String> id = const i0.Value.absent(),
+            i0.Value<String> name = const i0.Value.absent(),
+            i0.Value<String> description = const i0.Value.absent(),
+            i0.Value<DateTime> createdAt = const i0.Value.absent(),
+            i0.Value<DateTime> updatedAt = const i0.Value.absent(),
+            i0.Value<String> ownerId = const i0.Value.absent(),
+            i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
+            i0.Value<bool> isActivityEnabled = const i0.Value.absent(),
+            i0.Value<i2.AssetOrder> order = const i0.Value.absent(),
+          }) =>
+              i1.RemoteAlbumEntityCompanion(
+            id: id,
+            name: name,
+            description: description,
+            createdAt: createdAt,
+            updatedAt: updatedAt,
+            ownerId: ownerId,
+            thumbnailAssetId: thumbnailAssetId,
+            isActivityEnabled: isActivityEnabled,
+            order: order,
+          ),
+          createCompanionCallback: ({
+            required String id,
+            required String name,
+            required String description,
+            i0.Value<DateTime> createdAt = const i0.Value.absent(),
+            i0.Value<DateTime> updatedAt = const i0.Value.absent(),
+            required String ownerId,
+            i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
+            i0.Value<bool> isActivityEnabled = const i0.Value.absent(),
+            required i2.AssetOrder order,
+          }) =>
+              i1.RemoteAlbumEntityCompanion.insert(
+            id: id,
+            name: name,
+            description: description,
+            createdAt: createdAt,
+            updatedAt: updatedAt,
+            ownerId: ownerId,
+            thumbnailAssetId: thumbnailAssetId,
+            isActivityEnabled: isActivityEnabled,
+            order: order,
+          ),
+          withReferenceMapper: (p0) => p0
+              .map((e) => (
+                    e.readTable(table),
+                    i1.$$RemoteAlbumEntityTableReferences(db, table, e)
+                  ))
+              .toList(),
+          prefetchHooksCallback: ({ownerId = false, thumbnailAssetId = false}) {
+            return i0.PrefetchHooks(
+              db: db,
+              explicitlyWatchedTables: [],
+              addJoins: <
+                  T extends i0.TableManagerState<
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic>>(state) {
+                if (ownerId) {
+                  state = state.withJoin(
+                    currentTable: table,
+                    currentColumn: table.ownerId,
+                    referencedTable:
+                        i1.$$RemoteAlbumEntityTableReferences._ownerIdTable(db),
+                    referencedColumn: i1.$$RemoteAlbumEntityTableReferences
+                        ._ownerIdTable(db)
+                        .id,
+                  ) as T;
+                }
+                if (thumbnailAssetId) {
+                  state = state.withJoin(
+                    currentTable: table,
+                    currentColumn: table.thumbnailAssetId,
+                    referencedTable: i1.$$RemoteAlbumEntityTableReferences
+                        ._thumbnailAssetIdTable(db),
+                    referencedColumn: i1.$$RemoteAlbumEntityTableReferences
+                        ._thumbnailAssetIdTable(db)
+                        .id,
+                  ) as T;
+                }
+
+                return state;
+              },
+              getPrefetchedDataCallback: (items) async {
+                return [];
+              },
+            );
+          },
+        ));
+}
+
+typedef $$RemoteAlbumEntityTableProcessedTableManager
+    = i0.ProcessedTableManager<
+        i0.GeneratedDatabase,
+        i1.$RemoteAlbumEntityTable,
+        i1.RemoteAlbumEntityData,
+        i1.$$RemoteAlbumEntityTableFilterComposer,
+        i1.$$RemoteAlbumEntityTableOrderingComposer,
+        i1.$$RemoteAlbumEntityTableAnnotationComposer,
+        $$RemoteAlbumEntityTableCreateCompanionBuilder,
+        $$RemoteAlbumEntityTableUpdateCompanionBuilder,
+        (i1.RemoteAlbumEntityData, i1.$$RemoteAlbumEntityTableReferences),
+        i1.RemoteAlbumEntityData,
+        i0.PrefetchHooks Function({bool ownerId, bool thumbnailAssetId})>;
+
+class $RemoteAlbumEntityTable extends i3.RemoteAlbumEntity
+    with i0.TableInfo<$RemoteAlbumEntityTable, i1.RemoteAlbumEntityData> {
+  @override
+  final i0.GeneratedDatabase attachedDatabase;
+  final String? _alias;
+  $RemoteAlbumEntityTable(this.attachedDatabase, [this._alias]);
+  static const i0.VerificationMeta _idMeta = const i0.VerificationMeta('id');
+  @override
+  late final i0.GeneratedColumn<String> id = i0.GeneratedColumn<String>(
+      'id', aliasedName, false,
+      type: i0.DriftSqlType.string, requiredDuringInsert: true);
+  static const i0.VerificationMeta _nameMeta =
+      const i0.VerificationMeta('name');
+  @override
+  late final i0.GeneratedColumn<String> name = i0.GeneratedColumn<String>(
+      'name', aliasedName, false,
+      type: i0.DriftSqlType.string, requiredDuringInsert: true);
+  static const i0.VerificationMeta _descriptionMeta =
+      const i0.VerificationMeta('description');
+  @override
+  late final i0.GeneratedColumn<String> description =
+      i0.GeneratedColumn<String>('description', aliasedName, false,
+          type: i0.DriftSqlType.string, requiredDuringInsert: true);
+  static const i0.VerificationMeta _createdAtMeta =
+      const i0.VerificationMeta('createdAt');
+  @override
+  late final i0.GeneratedColumn<DateTime> createdAt =
+      i0.GeneratedColumn<DateTime>('created_at', aliasedName, false,
+          type: i0.DriftSqlType.dateTime,
+          requiredDuringInsert: false,
+          defaultValue: i4.currentDateAndTime);
+  static const i0.VerificationMeta _updatedAtMeta =
+      const i0.VerificationMeta('updatedAt');
+  @override
+  late final i0.GeneratedColumn<DateTime> updatedAt =
+      i0.GeneratedColumn<DateTime>('updated_at', aliasedName, false,
+          type: i0.DriftSqlType.dateTime,
+          requiredDuringInsert: false,
+          defaultValue: i4.currentDateAndTime);
+  static const i0.VerificationMeta _ownerIdMeta =
+      const i0.VerificationMeta('ownerId');
+  @override
+  late final i0.GeneratedColumn<String> ownerId = i0.GeneratedColumn<String>(
+      'owner_id', aliasedName, false,
+      type: i0.DriftSqlType.string,
+      requiredDuringInsert: true,
+      defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
+          'REFERENCES user_entity (id) ON DELETE CASCADE'));
+  static const i0.VerificationMeta _thumbnailAssetIdMeta =
+      const i0.VerificationMeta('thumbnailAssetId');
+  @override
+  late final i0.GeneratedColumn<String> thumbnailAssetId =
+      i0.GeneratedColumn<String>('thumbnail_asset_id', aliasedName, true,
+          type: i0.DriftSqlType.string,
+          requiredDuringInsert: false,
+          defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
+              'REFERENCES remote_asset_entity (id) ON DELETE SET NULL'));
+  static const i0.VerificationMeta _isActivityEnabledMeta =
+      const i0.VerificationMeta('isActivityEnabled');
+  @override
+  late final i0.GeneratedColumn<bool> isActivityEnabled =
+      i0.GeneratedColumn<bool>('is_activity_enabled', aliasedName, false,
+          type: i0.DriftSqlType.bool,
+          requiredDuringInsert: false,
+          defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
+              'CHECK ("is_activity_enabled" IN (0, 1))'),
+          defaultValue: const i4.Constant(true));
+  @override
+  late final i0.GeneratedColumnWithTypeConverter<i2.AssetOrder, int> order =
+      i0.GeneratedColumn<int>('order', aliasedName, false,
+              type: i0.DriftSqlType.int, requiredDuringInsert: true)
+          .withConverter<i2.AssetOrder>(
+              i1.$RemoteAlbumEntityTable.$converterorder);
+  @override
+  List<i0.GeneratedColumn> get $columns => [
+        id,
+        name,
+        description,
+        createdAt,
+        updatedAt,
+        ownerId,
+        thumbnailAssetId,
+        isActivityEnabled,
+        order
+      ];
+  @override
+  String get aliasedName => _alias ?? actualTableName;
+  @override
+  String get actualTableName => $name;
+  static const String $name = 'remote_album_entity';
+  @override
+  i0.VerificationContext validateIntegrity(
+      i0.Insertable<i1.RemoteAlbumEntityData> instance,
+      {bool isInserting = false}) {
+    final context = i0.VerificationContext();
+    final data = instance.toColumns(true);
+    if (data.containsKey('id')) {
+      context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta));
+    } else if (isInserting) {
+      context.missing(_idMeta);
+    }
+    if (data.containsKey('name')) {
+      context.handle(
+          _nameMeta, name.isAcceptableOrUnknown(data['name']!, _nameMeta));
+    } else if (isInserting) {
+      context.missing(_nameMeta);
+    }
+    if (data.containsKey('description')) {
+      context.handle(
+          _descriptionMeta,
+          description.isAcceptableOrUnknown(
+              data['description']!, _descriptionMeta));
+    } else if (isInserting) {
+      context.missing(_descriptionMeta);
+    }
+    if (data.containsKey('created_at')) {
+      context.handle(_createdAtMeta,
+          createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta));
+    }
+    if (data.containsKey('updated_at')) {
+      context.handle(_updatedAtMeta,
+          updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta));
+    }
+    if (data.containsKey('owner_id')) {
+      context.handle(_ownerIdMeta,
+          ownerId.isAcceptableOrUnknown(data['owner_id']!, _ownerIdMeta));
+    } else if (isInserting) {
+      context.missing(_ownerIdMeta);
+    }
+    if (data.containsKey('thumbnail_asset_id')) {
+      context.handle(
+          _thumbnailAssetIdMeta,
+          thumbnailAssetId.isAcceptableOrUnknown(
+              data['thumbnail_asset_id']!, _thumbnailAssetIdMeta));
+    }
+    if (data.containsKey('is_activity_enabled')) {
+      context.handle(
+          _isActivityEnabledMeta,
+          isActivityEnabled.isAcceptableOrUnknown(
+              data['is_activity_enabled']!, _isActivityEnabledMeta));
+    }
+    return context;
+  }
+
+  @override
+  Set<i0.GeneratedColumn> get $primaryKey => {id};
+  @override
+  i1.RemoteAlbumEntityData map(Map<String, dynamic> data,
+      {String? tablePrefix}) {
+    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+    return i1.RemoteAlbumEntityData(
+      id: attachedDatabase.typeMapping
+          .read(i0.DriftSqlType.string, data['${effectivePrefix}id'])!,
+      name: attachedDatabase.typeMapping
+          .read(i0.DriftSqlType.string, data['${effectivePrefix}name'])!,
+      description: attachedDatabase.typeMapping
+          .read(i0.DriftSqlType.string, data['${effectivePrefix}description'])!,
+      createdAt: attachedDatabase.typeMapping.read(
+          i0.DriftSqlType.dateTime, data['${effectivePrefix}created_at'])!,
+      updatedAt: attachedDatabase.typeMapping.read(
+          i0.DriftSqlType.dateTime, data['${effectivePrefix}updated_at'])!,
+      ownerId: attachedDatabase.typeMapping
+          .read(i0.DriftSqlType.string, data['${effectivePrefix}owner_id'])!,
+      thumbnailAssetId: attachedDatabase.typeMapping.read(
+          i0.DriftSqlType.string, data['${effectivePrefix}thumbnail_asset_id']),
+      isActivityEnabled: attachedDatabase.typeMapping.read(
+          i0.DriftSqlType.bool, data['${effectivePrefix}is_activity_enabled'])!,
+      order: i1.$RemoteAlbumEntityTable.$converterorder.fromSql(attachedDatabase
+          .typeMapping
+          .read(i0.DriftSqlType.int, data['${effectivePrefix}order'])!),
+    );
+  }
+
+  @override
+  $RemoteAlbumEntityTable createAlias(String alias) {
+    return $RemoteAlbumEntityTable(attachedDatabase, alias);
+  }
+
+  static i0.JsonTypeConverter2<i2.AssetOrder, int, int> $converterorder =
+      const i0.EnumIndexConverter<i2.AssetOrder>(i2.AssetOrder.values);
+  @override
+  bool get withoutRowId => true;
+  @override
+  bool get isStrict => true;
+}
+
+class RemoteAlbumEntityData extends i0.DataClass
+    implements i0.Insertable<i1.RemoteAlbumEntityData> {
+  final String id;
+  final String name;
+  final String description;
+  final DateTime createdAt;
+  final DateTime updatedAt;
+  final String ownerId;
+  final String? thumbnailAssetId;
+  final bool isActivityEnabled;
+  final i2.AssetOrder order;
+  const RemoteAlbumEntityData(
+      {required this.id,
+      required this.name,
+      required this.description,
+      required this.createdAt,
+      required this.updatedAt,
+      required this.ownerId,
+      this.thumbnailAssetId,
+      required this.isActivityEnabled,
+      required this.order});
+  @override
+  Map<String, i0.Expression> toColumns(bool nullToAbsent) {
+    final map = <String, i0.Expression>{};
+    map['id'] = i0.Variable<String>(id);
+    map['name'] = i0.Variable<String>(name);
+    map['description'] = i0.Variable<String>(description);
+    map['created_at'] = i0.Variable<DateTime>(createdAt);
+    map['updated_at'] = i0.Variable<DateTime>(updatedAt);
+    map['owner_id'] = i0.Variable<String>(ownerId);
+    if (!nullToAbsent || thumbnailAssetId != null) {
+      map['thumbnail_asset_id'] = i0.Variable<String>(thumbnailAssetId);
+    }
+    map['is_activity_enabled'] = i0.Variable<bool>(isActivityEnabled);
+    {
+      map['order'] = i0.Variable<int>(
+          i1.$RemoteAlbumEntityTable.$converterorder.toSql(order));
+    }
+    return map;
+  }
+
+  factory RemoteAlbumEntityData.fromJson(Map<String, dynamic> json,
+      {i0.ValueSerializer? serializer}) {
+    serializer ??= i0.driftRuntimeOptions.defaultSerializer;
+    return RemoteAlbumEntityData(
+      id: serializer.fromJson<String>(json['id']),
+      name: serializer.fromJson<String>(json['name']),
+      description: serializer.fromJson<String>(json['description']),
+      createdAt: serializer.fromJson<DateTime>(json['createdAt']),
+      updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
+      ownerId: serializer.fromJson<String>(json['ownerId']),
+      thumbnailAssetId: serializer.fromJson<String?>(json['thumbnailAssetId']),
+      isActivityEnabled: serializer.fromJson<bool>(json['isActivityEnabled']),
+      order: i1.$RemoteAlbumEntityTable.$converterorder
+          .fromJson(serializer.fromJson<int>(json['order'])),
+    );
+  }
+  @override
+  Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
+    serializer ??= i0.driftRuntimeOptions.defaultSerializer;
+    return <String, dynamic>{
+      'id': serializer.toJson<String>(id),
+      'name': serializer.toJson<String>(name),
+      'description': serializer.toJson<String>(description),
+      'createdAt': serializer.toJson<DateTime>(createdAt),
+      'updatedAt': serializer.toJson<DateTime>(updatedAt),
+      'ownerId': serializer.toJson<String>(ownerId),
+      'thumbnailAssetId': serializer.toJson<String?>(thumbnailAssetId),
+      'isActivityEnabled': serializer.toJson<bool>(isActivityEnabled),
+      'order': serializer.toJson<int>(
+          i1.$RemoteAlbumEntityTable.$converterorder.toJson(order)),
+    };
+  }
+
+  i1.RemoteAlbumEntityData copyWith(
+          {String? id,
+          String? name,
+          String? description,
+          DateTime? createdAt,
+          DateTime? updatedAt,
+          String? ownerId,
+          i0.Value<String?> thumbnailAssetId = const i0.Value.absent(),
+          bool? isActivityEnabled,
+          i2.AssetOrder? order}) =>
+      i1.RemoteAlbumEntityData(
+        id: id ?? this.id,
+        name: name ?? this.name,
+        description: description ?? this.description,
+        createdAt: createdAt ?? this.createdAt,
+        updatedAt: updatedAt ?? this.updatedAt,
+        ownerId: ownerId ?? this.ownerId,
+        thumbnailAssetId: thumbnailAssetId.present
+            ? thumbnailAssetId.value
+            : this.thumbnailAssetId,
+        isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled,
+        order: order ?? this.order,
+      );
+  RemoteAlbumEntityData copyWithCompanion(i1.RemoteAlbumEntityCompanion data) {
+    return RemoteAlbumEntityData(
+      id: data.id.present ? data.id.value : this.id,
+      name: data.name.present ? data.name.value : this.name,
+      description:
+          data.description.present ? data.description.value : this.description,
+      createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt,
+      updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
+      ownerId: data.ownerId.present ? data.ownerId.value : this.ownerId,
+      thumbnailAssetId: data.thumbnailAssetId.present
+          ? data.thumbnailAssetId.value
+          : this.thumbnailAssetId,
+      isActivityEnabled: data.isActivityEnabled.present
+          ? data.isActivityEnabled.value
+          : this.isActivityEnabled,
+      order: data.order.present ? data.order.value : this.order,
+    );
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('RemoteAlbumEntityData(')
+          ..write('id: $id, ')
+          ..write('name: $name, ')
+          ..write('description: $description, ')
+          ..write('createdAt: $createdAt, ')
+          ..write('updatedAt: $updatedAt, ')
+          ..write('ownerId: $ownerId, ')
+          ..write('thumbnailAssetId: $thumbnailAssetId, ')
+          ..write('isActivityEnabled: $isActivityEnabled, ')
+          ..write('order: $order')
+          ..write(')'))
+        .toString();
+  }
+
+  @override
+  int get hashCode => Object.hash(id, name, description, createdAt, updatedAt,
+      ownerId, thumbnailAssetId, isActivityEnabled, order);
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      (other is i1.RemoteAlbumEntityData &&
+          other.id == this.id &&
+          other.name == this.name &&
+          other.description == this.description &&
+          other.createdAt == this.createdAt &&
+          other.updatedAt == this.updatedAt &&
+          other.ownerId == this.ownerId &&
+          other.thumbnailAssetId == this.thumbnailAssetId &&
+          other.isActivityEnabled == this.isActivityEnabled &&
+          other.order == this.order);
+}
+
+class RemoteAlbumEntityCompanion
+    extends i0.UpdateCompanion<i1.RemoteAlbumEntityData> {
+  final i0.Value<String> id;
+  final i0.Value<String> name;
+  final i0.Value<String> description;
+  final i0.Value<DateTime> createdAt;
+  final i0.Value<DateTime> updatedAt;
+  final i0.Value<String> ownerId;
+  final i0.Value<String?> thumbnailAssetId;
+  final i0.Value<bool> isActivityEnabled;
+  final i0.Value<i2.AssetOrder> order;
+  const RemoteAlbumEntityCompanion({
+    this.id = const i0.Value.absent(),
+    this.name = const i0.Value.absent(),
+    this.description = const i0.Value.absent(),
+    this.createdAt = const i0.Value.absent(),
+    this.updatedAt = const i0.Value.absent(),
+    this.ownerId = const i0.Value.absent(),
+    this.thumbnailAssetId = const i0.Value.absent(),
+    this.isActivityEnabled = const i0.Value.absent(),
+    this.order = const i0.Value.absent(),
+  });
+  RemoteAlbumEntityCompanion.insert({
+    required String id,
+    required String name,
+    required String description,
+    this.createdAt = const i0.Value.absent(),
+    this.updatedAt = const i0.Value.absent(),
+    required String ownerId,
+    this.thumbnailAssetId = const i0.Value.absent(),
+    this.isActivityEnabled = const i0.Value.absent(),
+    required i2.AssetOrder order,
+  })  : id = i0.Value(id),
+        name = i0.Value(name),
+        description = i0.Value(description),
+        ownerId = i0.Value(ownerId),
+        order = i0.Value(order);
+  static i0.Insertable<i1.RemoteAlbumEntityData> custom({
+    i0.Expression<String>? id,
+    i0.Expression<String>? name,
+    i0.Expression<String>? description,
+    i0.Expression<DateTime>? createdAt,
+    i0.Expression<DateTime>? updatedAt,
+    i0.Expression<String>? ownerId,
+    i0.Expression<String>? thumbnailAssetId,
+    i0.Expression<bool>? isActivityEnabled,
+    i0.Expression<int>? order,
+  }) {
+    return i0.RawValuesInsertable({
+      if (id != null) 'id': id,
+      if (name != null) 'name': name,
+      if (description != null) 'description': description,
+      if (createdAt != null) 'created_at': createdAt,
+      if (updatedAt != null) 'updated_at': updatedAt,
+      if (ownerId != null) 'owner_id': ownerId,
+      if (thumbnailAssetId != null) 'thumbnail_asset_id': thumbnailAssetId,
+      if (isActivityEnabled != null) 'is_activity_enabled': isActivityEnabled,
+      if (order != null) 'order': order,
+    });
+  }
+
+  i1.RemoteAlbumEntityCompanion copyWith(
+      {i0.Value<String>? id,
+      i0.Value<String>? name,
+      i0.Value<String>? description,
+      i0.Value<DateTime>? createdAt,
+      i0.Value<DateTime>? updatedAt,
+      i0.Value<String>? ownerId,
+      i0.Value<String?>? thumbnailAssetId,
+      i0.Value<bool>? isActivityEnabled,
+      i0.Value<i2.AssetOrder>? order}) {
+    return i1.RemoteAlbumEntityCompanion(
+      id: id ?? this.id,
+      name: name ?? this.name,
+      description: description ?? this.description,
+      createdAt: createdAt ?? this.createdAt,
+      updatedAt: updatedAt ?? this.updatedAt,
+      ownerId: ownerId ?? this.ownerId,
+      thumbnailAssetId: thumbnailAssetId ?? this.thumbnailAssetId,
+      isActivityEnabled: isActivityEnabled ?? this.isActivityEnabled,
+      order: order ?? this.order,
+    );
+  }
+
+  @override
+  Map<String, i0.Expression> toColumns(bool nullToAbsent) {
+    final map = <String, i0.Expression>{};
+    if (id.present) {
+      map['id'] = i0.Variable<String>(id.value);
+    }
+    if (name.present) {
+      map['name'] = i0.Variable<String>(name.value);
+    }
+    if (description.present) {
+      map['description'] = i0.Variable<String>(description.value);
+    }
+    if (createdAt.present) {
+      map['created_at'] = i0.Variable<DateTime>(createdAt.value);
+    }
+    if (updatedAt.present) {
+      map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
+    }
+    if (ownerId.present) {
+      map['owner_id'] = i0.Variable<String>(ownerId.value);
+    }
+    if (thumbnailAssetId.present) {
+      map['thumbnail_asset_id'] = i0.Variable<String>(thumbnailAssetId.value);
+    }
+    if (isActivityEnabled.present) {
+      map['is_activity_enabled'] = i0.Variable<bool>(isActivityEnabled.value);
+    }
+    if (order.present) {
+      map['order'] = i0.Variable<int>(
+          i1.$RemoteAlbumEntityTable.$converterorder.toSql(order.value));
+    }
+    return map;
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('RemoteAlbumEntityCompanion(')
+          ..write('id: $id, ')
+          ..write('name: $name, ')
+          ..write('description: $description, ')
+          ..write('createdAt: $createdAt, ')
+          ..write('updatedAt: $updatedAt, ')
+          ..write('ownerId: $ownerId, ')
+          ..write('thumbnailAssetId: $thumbnailAssetId, ')
+          ..write('isActivityEnabled: $isActivityEnabled, ')
+          ..write('order: $order')
+          ..write(')'))
+        .toString();
+  }
+}
diff --git a/mobile/lib/infrastructure/entities/remote_album_asset.entity.dart b/mobile/lib/infrastructure/entities/remote_album_asset.entity.dart
new file mode 100644
index 0000000000..1dcc336ed8
--- /dev/null
+++ b/mobile/lib/infrastructure/entities/remote_album_asset.entity.dart
@@ -0,0 +1,17 @@
+import 'package:drift/drift.dart';
+import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
+import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart';
+
+class RemoteAlbumAssetEntity extends Table with DriftDefaultsMixin {
+  const RemoteAlbumAssetEntity();
+
+  TextColumn get assetId =>
+      text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)();
+
+  TextColumn get albumId =>
+      text().references(RemoteAlbumEntity, #id, onDelete: KeyAction.cascade)();
+
+  @override
+  Set<Column> get primaryKey => {assetId, albumId};
+}
diff --git a/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart
new file mode 100644
index 0000000000..ab50607c96
--- /dev/null
+++ b/mobile/lib/infrastructure/entities/remote_album_asset.entity.drift.dart
@@ -0,0 +1,565 @@
+// dart format width=80
+// ignore_for_file: type=lint
+import 'package:drift/drift.dart' as i0;
+import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
+    as i1;
+import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.dart'
+    as i2;
+import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'
+    as i3;
+import 'package:drift/internal/modular.dart' as i4;
+import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
+    as i5;
+
+typedef $$RemoteAlbumAssetEntityTableCreateCompanionBuilder
+    = i1.RemoteAlbumAssetEntityCompanion Function({
+  required String assetId,
+  required String albumId,
+});
+typedef $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder
+    = i1.RemoteAlbumAssetEntityCompanion Function({
+  i0.Value<String> assetId,
+  i0.Value<String> albumId,
+});
+
+final class $$RemoteAlbumAssetEntityTableReferences extends i0.BaseReferences<
+    i0.GeneratedDatabase,
+    i1.$RemoteAlbumAssetEntityTable,
+    i1.RemoteAlbumAssetEntityData> {
+  $$RemoteAlbumAssetEntityTableReferences(
+      super.$_db, super.$_table, super.$_typedResult);
+
+  static i3.$RemoteAssetEntityTable _assetIdTable(i0.GeneratedDatabase db) =>
+      i4.ReadDatabaseContainer(db)
+          .resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity')
+          .createAlias(i0.$_aliasNameGenerator(
+              i4.ReadDatabaseContainer(db)
+                  .resultSet<i1.$RemoteAlbumAssetEntityTable>(
+                      'remote_album_asset_entity')
+                  .assetId,
+              i4.ReadDatabaseContainer(db)
+                  .resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity')
+                  .id));
+
+  i3.$$RemoteAssetEntityTableProcessedTableManager get assetId {
+    final $_column = $_itemColumn<String>('asset_id')!;
+
+    final manager = i3
+        .$$RemoteAssetEntityTableTableManager(
+            $_db,
+            i4.ReadDatabaseContainer($_db)
+                .resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'))
+        .filter((f) => f.id.sqlEquals($_column));
+    final item = $_typedResult.readTableOrNull(_assetIdTable($_db));
+    if (item == null) return manager;
+    return i0.ProcessedTableManager(
+        manager.$state.copyWith(prefetchedData: [item]));
+  }
+
+  static i5.$RemoteAlbumEntityTable _albumIdTable(i0.GeneratedDatabase db) =>
+      i4.ReadDatabaseContainer(db)
+          .resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity')
+          .createAlias(i0.$_aliasNameGenerator(
+              i4.ReadDatabaseContainer(db)
+                  .resultSet<i1.$RemoteAlbumAssetEntityTable>(
+                      'remote_album_asset_entity')
+                  .albumId,
+              i4.ReadDatabaseContainer(db)
+                  .resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity')
+                  .id));
+
+  i5.$$RemoteAlbumEntityTableProcessedTableManager get albumId {
+    final $_column = $_itemColumn<String>('album_id')!;
+
+    final manager = i5
+        .$$RemoteAlbumEntityTableTableManager(
+            $_db,
+            i4.ReadDatabaseContainer($_db)
+                .resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'))
+        .filter((f) => f.id.sqlEquals($_column));
+    final item = $_typedResult.readTableOrNull(_albumIdTable($_db));
+    if (item == null) return manager;
+    return i0.ProcessedTableManager(
+        manager.$state.copyWith(prefetchedData: [item]));
+  }
+}
+
+class $$RemoteAlbumAssetEntityTableFilterComposer
+    extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumAssetEntityTable> {
+  $$RemoteAlbumAssetEntityTableFilterComposer({
+    required super.$db,
+    required super.$table,
+    super.joinBuilder,
+    super.$addJoinBuilderToRootComposer,
+    super.$removeJoinBuilderFromRootComposer,
+  });
+  i3.$$RemoteAssetEntityTableFilterComposer get assetId {
+    final i3.$$RemoteAssetEntityTableFilterComposer composer = $composerBuilder(
+        composer: this,
+        getCurrentColumn: (t) => t.assetId,
+        referencedTable: i4.ReadDatabaseContainer($db)
+            .resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
+        getReferencedColumn: (t) => t.id,
+        builder: (joinBuilder,
+                {$addJoinBuilderToRootComposer,
+                $removeJoinBuilderFromRootComposer}) =>
+            i3.$$RemoteAssetEntityTableFilterComposer(
+              $db: $db,
+              $table: i4.ReadDatabaseContainer($db)
+                  .resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
+              $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+              joinBuilder: joinBuilder,
+              $removeJoinBuilderFromRootComposer:
+                  $removeJoinBuilderFromRootComposer,
+            ));
+    return composer;
+  }
+
+  i5.$$RemoteAlbumEntityTableFilterComposer get albumId {
+    final i5.$$RemoteAlbumEntityTableFilterComposer composer = $composerBuilder(
+        composer: this,
+        getCurrentColumn: (t) => t.albumId,
+        referencedTable: i4.ReadDatabaseContainer($db)
+            .resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
+        getReferencedColumn: (t) => t.id,
+        builder: (joinBuilder,
+                {$addJoinBuilderToRootComposer,
+                $removeJoinBuilderFromRootComposer}) =>
+            i5.$$RemoteAlbumEntityTableFilterComposer(
+              $db: $db,
+              $table: i4.ReadDatabaseContainer($db)
+                  .resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
+              $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+              joinBuilder: joinBuilder,
+              $removeJoinBuilderFromRootComposer:
+                  $removeJoinBuilderFromRootComposer,
+            ));
+    return composer;
+  }
+}
+
+class $$RemoteAlbumAssetEntityTableOrderingComposer
+    extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumAssetEntityTable> {
+  $$RemoteAlbumAssetEntityTableOrderingComposer({
+    required super.$db,
+    required super.$table,
+    super.joinBuilder,
+    super.$addJoinBuilderToRootComposer,
+    super.$removeJoinBuilderFromRootComposer,
+  });
+  i3.$$RemoteAssetEntityTableOrderingComposer get assetId {
+    final i3.$$RemoteAssetEntityTableOrderingComposer composer =
+        $composerBuilder(
+            composer: this,
+            getCurrentColumn: (t) => t.assetId,
+            referencedTable: i4.ReadDatabaseContainer($db)
+                .resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
+            getReferencedColumn: (t) => t.id,
+            builder: (joinBuilder,
+                    {$addJoinBuilderToRootComposer,
+                    $removeJoinBuilderFromRootComposer}) =>
+                i3.$$RemoteAssetEntityTableOrderingComposer(
+                  $db: $db,
+                  $table: i4.ReadDatabaseContainer($db)
+                      .resultSet<i3.$RemoteAssetEntityTable>(
+                          'remote_asset_entity'),
+                  $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+                  joinBuilder: joinBuilder,
+                  $removeJoinBuilderFromRootComposer:
+                      $removeJoinBuilderFromRootComposer,
+                ));
+    return composer;
+  }
+
+  i5.$$RemoteAlbumEntityTableOrderingComposer get albumId {
+    final i5.$$RemoteAlbumEntityTableOrderingComposer composer =
+        $composerBuilder(
+            composer: this,
+            getCurrentColumn: (t) => t.albumId,
+            referencedTable: i4.ReadDatabaseContainer($db)
+                .resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
+            getReferencedColumn: (t) => t.id,
+            builder: (joinBuilder,
+                    {$addJoinBuilderToRootComposer,
+                    $removeJoinBuilderFromRootComposer}) =>
+                i5.$$RemoteAlbumEntityTableOrderingComposer(
+                  $db: $db,
+                  $table: i4.ReadDatabaseContainer($db)
+                      .resultSet<i5.$RemoteAlbumEntityTable>(
+                          'remote_album_entity'),
+                  $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+                  joinBuilder: joinBuilder,
+                  $removeJoinBuilderFromRootComposer:
+                      $removeJoinBuilderFromRootComposer,
+                ));
+    return composer;
+  }
+}
+
+class $$RemoteAlbumAssetEntityTableAnnotationComposer
+    extends i0.Composer<i0.GeneratedDatabase, i1.$RemoteAlbumAssetEntityTable> {
+  $$RemoteAlbumAssetEntityTableAnnotationComposer({
+    required super.$db,
+    required super.$table,
+    super.joinBuilder,
+    super.$addJoinBuilderToRootComposer,
+    super.$removeJoinBuilderFromRootComposer,
+  });
+  i3.$$RemoteAssetEntityTableAnnotationComposer get assetId {
+    final i3.$$RemoteAssetEntityTableAnnotationComposer composer =
+        $composerBuilder(
+            composer: this,
+            getCurrentColumn: (t) => t.assetId,
+            referencedTable: i4.ReadDatabaseContainer($db)
+                .resultSet<i3.$RemoteAssetEntityTable>('remote_asset_entity'),
+            getReferencedColumn: (t) => t.id,
+            builder: (joinBuilder,
+                    {$addJoinBuilderToRootComposer,
+                    $removeJoinBuilderFromRootComposer}) =>
+                i3.$$RemoteAssetEntityTableAnnotationComposer(
+                  $db: $db,
+                  $table: i4.ReadDatabaseContainer($db)
+                      .resultSet<i3.$RemoteAssetEntityTable>(
+                          'remote_asset_entity'),
+                  $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+                  joinBuilder: joinBuilder,
+                  $removeJoinBuilderFromRootComposer:
+                      $removeJoinBuilderFromRootComposer,
+                ));
+    return composer;
+  }
+
+  i5.$$RemoteAlbumEntityTableAnnotationComposer get albumId {
+    final i5.$$RemoteAlbumEntityTableAnnotationComposer composer =
+        $composerBuilder(
+            composer: this,
+            getCurrentColumn: (t) => t.albumId,
+            referencedTable: i4.ReadDatabaseContainer($db)
+                .resultSet<i5.$RemoteAlbumEntityTable>('remote_album_entity'),
+            getReferencedColumn: (t) => t.id,
+            builder: (joinBuilder,
+                    {$addJoinBuilderToRootComposer,
+                    $removeJoinBuilderFromRootComposer}) =>
+                i5.$$RemoteAlbumEntityTableAnnotationComposer(
+                  $db: $db,
+                  $table: i4.ReadDatabaseContainer($db)
+                      .resultSet<i5.$RemoteAlbumEntityTable>(
+                          'remote_album_entity'),
+                  $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
+                  joinBuilder: joinBuilder,
+                  $removeJoinBuilderFromRootComposer:
+                      $removeJoinBuilderFromRootComposer,
+                ));
+    return composer;
+  }
+}
+
+class $$RemoteAlbumAssetEntityTableTableManager extends i0.RootTableManager<
+    i0.GeneratedDatabase,
+    i1.$RemoteAlbumAssetEntityTable,
+    i1.RemoteAlbumAssetEntityData,
+    i1.$$RemoteAlbumAssetEntityTableFilterComposer,
+    i1.$$RemoteAlbumAssetEntityTableOrderingComposer,
+    i1.$$RemoteAlbumAssetEntityTableAnnotationComposer,
+    $$RemoteAlbumAssetEntityTableCreateCompanionBuilder,
+    $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder,
+    (i1.RemoteAlbumAssetEntityData, i1.$$RemoteAlbumAssetEntityTableReferences),
+    i1.RemoteAlbumAssetEntityData,
+    i0.PrefetchHooks Function({bool assetId, bool albumId})> {
+  $$RemoteAlbumAssetEntityTableTableManager(
+      i0.GeneratedDatabase db, i1.$RemoteAlbumAssetEntityTable table)
+      : super(i0.TableManagerState(
+          db: db,
+          table: table,
+          createFilteringComposer: () =>
+              i1.$$RemoteAlbumAssetEntityTableFilterComposer(
+                  $db: db, $table: table),
+          createOrderingComposer: () =>
+              i1.$$RemoteAlbumAssetEntityTableOrderingComposer(
+                  $db: db, $table: table),
+          createComputedFieldComposer: () =>
+              i1.$$RemoteAlbumAssetEntityTableAnnotationComposer(
+                  $db: db, $table: table),
+          updateCompanionCallback: ({
+            i0.Value<String> assetId = const i0.Value.absent(),
+            i0.Value<String> albumId = const i0.Value.absent(),
+          }) =>
+              i1.RemoteAlbumAssetEntityCompanion(
+            assetId: assetId,
+            albumId: albumId,
+          ),
+          createCompanionCallback: ({
+            required String assetId,
+            required String albumId,
+          }) =>
+              i1.RemoteAlbumAssetEntityCompanion.insert(
+            assetId: assetId,
+            albumId: albumId,
+          ),
+          withReferenceMapper: (p0) => p0
+              .map((e) => (
+                    e.readTable(table),
+                    i1.$$RemoteAlbumAssetEntityTableReferences(db, table, e)
+                  ))
+              .toList(),
+          prefetchHooksCallback: ({assetId = false, albumId = false}) {
+            return i0.PrefetchHooks(
+              db: db,
+              explicitlyWatchedTables: [],
+              addJoins: <
+                  T extends i0.TableManagerState<
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic,
+                      dynamic>>(state) {
+                if (assetId) {
+                  state = state.withJoin(
+                    currentTable: table,
+                    currentColumn: table.assetId,
+                    referencedTable: i1.$$RemoteAlbumAssetEntityTableReferences
+                        ._assetIdTable(db),
+                    referencedColumn: i1.$$RemoteAlbumAssetEntityTableReferences
+                        ._assetIdTable(db)
+                        .id,
+                  ) as T;
+                }
+                if (albumId) {
+                  state = state.withJoin(
+                    currentTable: table,
+                    currentColumn: table.albumId,
+                    referencedTable: i1.$$RemoteAlbumAssetEntityTableReferences
+                        ._albumIdTable(db),
+                    referencedColumn: i1.$$RemoteAlbumAssetEntityTableReferences
+                        ._albumIdTable(db)
+                        .id,
+                  ) as T;
+                }
+
+                return state;
+              },
+              getPrefetchedDataCallback: (items) async {
+                return [];
+              },
+            );
+          },
+        ));
+}
+
+typedef $$RemoteAlbumAssetEntityTableProcessedTableManager
+    = i0.ProcessedTableManager<
+        i0.GeneratedDatabase,
+        i1.$RemoteAlbumAssetEntityTable,
+        i1.RemoteAlbumAssetEntityData,
+        i1.$$RemoteAlbumAssetEntityTableFilterComposer,
+        i1.$$RemoteAlbumAssetEntityTableOrderingComposer,
+        i1.$$RemoteAlbumAssetEntityTableAnnotationComposer,
+        $$RemoteAlbumAssetEntityTableCreateCompanionBuilder,
+        $$RemoteAlbumAssetEntityTableUpdateCompanionBuilder,
+        (
+          i1.RemoteAlbumAssetEntityData,
+          i1.$$RemoteAlbumAssetEntityTableReferences
+        ),
+        i1.RemoteAlbumAssetEntityData,
+        i0.PrefetchHooks Function({bool assetId, bool albumId})>;
+
+class $RemoteAlbumAssetEntityTable extends i2.RemoteAlbumAssetEntity
+    with
+        i0.TableInfo<$RemoteAlbumAssetEntityTable,
+            i1.RemoteAlbumAssetEntityData> {
+  @override
+  final i0.GeneratedDatabase attachedDatabase;
+  final String? _alias;
+  $RemoteAlbumAssetEntityTable(this.attachedDatabase, [this._alias]);
+  static const i0.VerificationMeta _assetIdMeta =
+      const i0.VerificationMeta('assetId');
+  @override
+  late final i0.GeneratedColumn<String> assetId = i0.GeneratedColumn<String>(
+      'asset_id', aliasedName, false,
+      type: i0.DriftSqlType.string,
+      requiredDuringInsert: true,
+      defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
+          'REFERENCES remote_asset_entity (id) ON DELETE CASCADE'));
+  static const i0.VerificationMeta _albumIdMeta =
+      const i0.VerificationMeta('albumId');
+  @override
+  late final i0.GeneratedColumn<String> albumId = i0.GeneratedColumn<String>(
+      'album_id', aliasedName, false,
+      type: i0.DriftSqlType.string,
+      requiredDuringInsert: true,
+      defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
+          'REFERENCES remote_album_entity (id) ON DELETE CASCADE'));
+  @override
+  List<i0.GeneratedColumn> get $columns => [assetId, albumId];
+  @override
+  String get aliasedName => _alias ?? actualTableName;
+  @override
+  String get actualTableName => $name;
+  static const String $name = 'remote_album_asset_entity';
+  @override
+  i0.VerificationContext validateIntegrity(
+      i0.Insertable<i1.RemoteAlbumAssetEntityData> instance,
+      {bool isInserting = false}) {
+    final context = i0.VerificationContext();
+    final data = instance.toColumns(true);
+    if (data.containsKey('asset_id')) {
+      context.handle(_assetIdMeta,
+          assetId.isAcceptableOrUnknown(data['asset_id']!, _assetIdMeta));
+    } else if (isInserting) {
+      context.missing(_assetIdMeta);
+    }
+    if (data.containsKey('album_id')) {
+      context.handle(_albumIdMeta,
+          albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta));
+    } else if (isInserting) {
+      context.missing(_albumIdMeta);
+    }
+    return context;
+  }
+
+  @override
+  Set<i0.GeneratedColumn> get $primaryKey => {assetId, albumId};
+  @override
+  i1.RemoteAlbumAssetEntityData map(Map<String, dynamic> data,
+      {String? tablePrefix}) {
+    final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
+    return i1.RemoteAlbumAssetEntityData(
+      assetId: attachedDatabase.typeMapping
+          .read(i0.DriftSqlType.string, data['${effectivePrefix}asset_id'])!,
+      albumId: attachedDatabase.typeMapping
+          .read(i0.DriftSqlType.string, data['${effectivePrefix}album_id'])!,
+    );
+  }
+
+  @override
+  $RemoteAlbumAssetEntityTable createAlias(String alias) {
+    return $RemoteAlbumAssetEntityTable(attachedDatabase, alias);
+  }
+
+  @override
+  bool get withoutRowId => true;
+  @override
+  bool get isStrict => true;
+}
+
+class RemoteAlbumAssetEntityData extends i0.DataClass
+    implements i0.Insertable<i1.RemoteAlbumAssetEntityData> {
+  final String assetId;
+  final String albumId;
+  const RemoteAlbumAssetEntityData(
+      {required this.assetId, required this.albumId});
+  @override
+  Map<String, i0.Expression> toColumns(bool nullToAbsent) {
+    final map = <String, i0.Expression>{};
+    map['asset_id'] = i0.Variable<String>(assetId);
+    map['album_id'] = i0.Variable<String>(albumId);
+    return map;
+  }
+
+  factory RemoteAlbumAssetEntityData.fromJson(Map<String, dynamic> json,
+      {i0.ValueSerializer? serializer}) {
+    serializer ??= i0.driftRuntimeOptions.defaultSerializer;
+    return RemoteAlbumAssetEntityData(
+      assetId: serializer.fromJson<String>(json['assetId']),
+      albumId: serializer.fromJson<String>(json['albumId']),
+    );
+  }
+  @override
+  Map<String, dynamic> toJson({i0.ValueSerializer? serializer}) {
+    serializer ??= i0.driftRuntimeOptions.defaultSerializer;
+    return <String, dynamic>{
+      'assetId': serializer.toJson<String>(assetId),
+      'albumId': serializer.toJson<String>(albumId),
+    };
+  }
+
+  i1.RemoteAlbumAssetEntityData copyWith({String? assetId, String? albumId}) =>
+      i1.RemoteAlbumAssetEntityData(
+        assetId: assetId ?? this.assetId,
+        albumId: albumId ?? this.albumId,
+      );
+  RemoteAlbumAssetEntityData copyWithCompanion(
+      i1.RemoteAlbumAssetEntityCompanion data) {
+    return RemoteAlbumAssetEntityData(
+      assetId: data.assetId.present ? data.assetId.value : this.assetId,
+      albumId: data.albumId.present ? data.albumId.value : this.albumId,
+    );
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('RemoteAlbumAssetEntityData(')
+          ..write('assetId: $assetId, ')
+          ..write('albumId: $albumId')
+          ..write(')'))
+        .toString();
+  }
+
+  @override
+  int get hashCode => Object.hash(assetId, albumId);
+  @override
+  bool operator ==(Object other) =>
+      identical(this, other) ||
+      (other is i1.RemoteAlbumAssetEntityData &&
+          other.assetId == this.assetId &&
+          other.albumId == this.albumId);
+}
+
+class RemoteAlbumAssetEntityCompanion
+    extends i0.UpdateCompanion<i1.RemoteAlbumAssetEntityData> {
+  final i0.Value<String> assetId;
+  final i0.Value<String> albumId;
+  const RemoteAlbumAssetEntityCompanion({
+    this.assetId = const i0.Value.absent(),
+    this.albumId = const i0.Value.absent(),
+  });
+  RemoteAlbumAssetEntityCompanion.insert({
+    required String assetId,
+    required String albumId,
+  })  : assetId = i0.Value(assetId),
+        albumId = i0.Value(albumId);
+  static i0.Insertable<i1.RemoteAlbumAssetEntityData> custom({
+    i0.Expression<String>? assetId,
+    i0.Expression<String>? albumId,
+  }) {
+    return i0.RawValuesInsertable({
+      if (assetId != null) 'asset_id': assetId,
+      if (albumId != null) 'album_id': albumId,
+    });
+  }
+
+  i1.RemoteAlbumAssetEntityCompanion copyWith(
+      {i0.Value<String>? assetId, i0.Value<String>? albumId}) {
+    return i1.RemoteAlbumAssetEntityCompanion(
+      assetId: assetId ?? this.assetId,
+      albumId: albumId ?? this.albumId,
+    );
+  }
+
+  @override
+  Map<String, i0.Expression> toColumns(bool nullToAbsent) {
+    final map = <String, i0.Expression>{};
+    if (assetId.present) {
+      map['asset_id'] = i0.Variable<String>(assetId.value);
+    }
+    if (albumId.present) {
+      map['album_id'] = i0.Variable<String>(albumId.value);
+    }
+    return map;
+  }
+
+  @override
+  String toString() {
+    return (StringBuffer('RemoteAlbumAssetEntityCompanion(')
+          ..write('assetId: $assetId, ')
+          ..write('albumId: $albumId')
+          ..write(')'))
+        .toString();
+  }
+}
diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart
index 4ad60276a2..f414cd9f67 100644
--- a/mobile/lib/infrastructure/repositories/db.repository.dart
+++ b/mobile/lib/infrastructure/repositories/db.repository.dart
@@ -3,12 +3,15 @@ import 'dart:async';
 import 'package:drift/drift.dart';
 import 'package:drift_flutter/drift_flutter.dart';
 import 'package:immich_mobile/domain/interfaces/db.interface.dart';
+import 'package:immich_mobile/infrastructure/entities/album_user.entity.dart';
 import 'package:immich_mobile/infrastructure/entities/exif.entity.dart';
 import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart';
 import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart';
 import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
-import 'package:immich_mobile/infrastructure/entities/partner.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/remote_album.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.dart';
 import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
+import 'package:immich_mobile/infrastructure/entities/partner.entity.dart';
 import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
 import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart';
 import 'package:isar/isar.dart';
@@ -38,8 +41,11 @@ class IsarDatabaseRepository implements IDatabaseRepository {
     LocalAlbumEntity,
     LocalAssetEntity,
     LocalAlbumAssetEntity,
-    RemoteAssetEntity,
     RemoteExifEntity,
+    RemoteAssetEntity,
+    RemoteAlbumEntity,
+    RemoteAlbumAssetEntity,
+    AlbumUserEntity,
   ],
 )
 class Drift extends $Drift implements IDatabaseRepository {
diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart
index d1bda93653..54c12ecde5 100644
--- a/mobile/lib/infrastructure/repositories/db.repository.drift.dart
+++ b/mobile/lib/infrastructure/repositories/db.repository.drift.dart
@@ -17,6 +17,12 @@ import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.
     as i7;
 import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'
     as i8;
+import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart'
+    as i9;
+import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart'
+    as i10;
+import 'package:immich_mobile/infrastructure/entities/album_user.entity.drift.dart'
+    as i11;
 
 abstract class $Drift extends i0.GeneratedDatabase {
   $Drift(i0.QueryExecutor e) : super(e);
@@ -36,6 +42,12 @@ abstract class $Drift extends i0.GeneratedDatabase {
       i7.$RemoteAssetEntityTable(this);
   late final i8.$RemoteExifEntityTable remoteExifEntity =
       i8.$RemoteExifEntityTable(this);
+  late final i9.$RemoteAlbumEntityTable remoteAlbumEntity =
+      i9.$RemoteAlbumEntityTable(this);
+  late final i10.$RemoteAlbumAssetEntityTable remoteAlbumAssetEntity =
+      i10.$RemoteAlbumAssetEntityTable(this);
+  late final i11.$AlbumUserEntityTable albumUserEntity =
+      i11.$AlbumUserEntityTable(this);
   @override
   Iterable<i0.TableInfo<i0.Table, Object?>> get allTables =>
       allSchemaEntities.whereType<i0.TableInfo<i0.Table, Object?>>();
@@ -49,6 +61,9 @@ abstract class $Drift extends i0.GeneratedDatabase {
         localAlbumAssetEntity,
         remoteAssetEntity,
         remoteExifEntity,
+        remoteAlbumEntity,
+        remoteAlbumAssetEntity,
+        albumUserEntity,
         i5.idxLocalAssetChecksum,
         i7.uQRemoteAssetOwnerChecksum
       ];
@@ -108,6 +123,50 @@ abstract class $Drift extends i0.GeneratedDatabase {
               i0.TableUpdate('remote_exif_entity', kind: i0.UpdateKind.delete),
             ],
           ),
+          i0.WritePropagation(
+            on: i0.TableUpdateQuery.onTableName('user_entity',
+                limitUpdateKind: i0.UpdateKind.delete),
+            result: [
+              i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.delete),
+            ],
+          ),
+          i0.WritePropagation(
+            on: i0.TableUpdateQuery.onTableName('remote_asset_entity',
+                limitUpdateKind: i0.UpdateKind.delete),
+            result: [
+              i0.TableUpdate('remote_album_entity', kind: i0.UpdateKind.update),
+            ],
+          ),
+          i0.WritePropagation(
+            on: i0.TableUpdateQuery.onTableName('remote_asset_entity',
+                limitUpdateKind: i0.UpdateKind.delete),
+            result: [
+              i0.TableUpdate('remote_album_asset_entity',
+                  kind: i0.UpdateKind.delete),
+            ],
+          ),
+          i0.WritePropagation(
+            on: i0.TableUpdateQuery.onTableName('remote_album_entity',
+                limitUpdateKind: i0.UpdateKind.delete),
+            result: [
+              i0.TableUpdate('remote_album_asset_entity',
+                  kind: i0.UpdateKind.delete),
+            ],
+          ),
+          i0.WritePropagation(
+            on: i0.TableUpdateQuery.onTableName('remote_album_entity',
+                limitUpdateKind: i0.UpdateKind.delete),
+            result: [
+              i0.TableUpdate('album_user_entity', kind: i0.UpdateKind.delete),
+            ],
+          ),
+          i0.WritePropagation(
+            on: i0.TableUpdateQuery.onTableName('user_entity',
+                limitUpdateKind: i0.UpdateKind.delete),
+            result: [
+              i0.TableUpdate('album_user_entity', kind: i0.UpdateKind.delete),
+            ],
+          ),
         ],
       );
   @override
@@ -134,4 +193,11 @@ class $DriftManager {
       i7.$$RemoteAssetEntityTableTableManager(_db, _db.remoteAssetEntity);
   i8.$$RemoteExifEntityTableTableManager get remoteExifEntity =>
       i8.$$RemoteExifEntityTableTableManager(_db, _db.remoteExifEntity);
+  i9.$$RemoteAlbumEntityTableTableManager get remoteAlbumEntity =>
+      i9.$$RemoteAlbumEntityTableTableManager(_db, _db.remoteAlbumEntity);
+  i10.$$RemoteAlbumAssetEntityTableTableManager get remoteAlbumAssetEntity =>
+      i10.$$RemoteAlbumAssetEntityTableTableManager(
+          _db, _db.remoteAlbumAssetEntity);
+  i11.$$AlbumUserEntityTableTableManager get albumUserEntity =>
+      i11.$$AlbumUserEntityTableTableManager(_db, _db.albumUserEntity);
 }
diff --git a/mobile/lib/infrastructure/repositories/sync_api.repository.dart b/mobile/lib/infrastructure/repositories/sync_api.repository.dart
index ca24eef60f..70f940c10b 100644
--- a/mobile/lib/infrastructure/repositories/sync_api.repository.dart
+++ b/mobile/lib/infrastructure/repositories/sync_api.repository.dart
@@ -50,6 +50,9 @@ class SyncApiRepository implements ISyncApiRepository {
           SyncRequestType.partnerAssetsV1,
           SyncRequestType.assetExifsV1,
           SyncRequestType.partnerAssetExifsV1,
+          SyncRequestType.albumsV1,
+          // SyncRequestType.albumAssetsV1,
+          SyncRequestType.albumUsersV1,
         ],
       ).toJson(),
     );
@@ -140,4 +143,10 @@ const _kResponseMap = <SyncEntityType, Function(Object)>{
   SyncEntityType.partnerAssetV1: SyncAssetV1.fromJson,
   SyncEntityType.partnerAssetDeleteV1: SyncAssetDeleteV1.fromJson,
   SyncEntityType.partnerAssetExifV1: SyncAssetExifV1.fromJson,
+  SyncEntityType.albumV1: SyncAlbumV1.fromJson,
+  SyncEntityType.albumDeleteV1: SyncAlbumDeleteV1.fromJson,
+  // SyncEntityType.albumAssetV1: SyncAlbumAssetV1.fromJson,
+  // SyncEntityType.albumAssetDeleteV1: SyncAlbumAssetDeleteV1.fromJson,
+  SyncEntityType.albumUserV1: SyncAlbumUserV1.fromJson,
+  SyncEntityType.albumUserDeleteV1: SyncAlbumUserDeleteV1.fromJson,
 };
diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart
index 7aa8fc6efe..472bc9a6b5 100644
--- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart
+++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart
@@ -3,12 +3,19 @@ import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart';
 import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
 import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart';
 import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart';
+import 'package:immich_mobile/domain/models/album/album.model.dart';
+import 'package:immich_mobile/domain/models/album_user.model.dart';
+import 'package:immich_mobile/infrastructure/entities/album_user.entity.drift.dart';
+import 'package:immich_mobile/infrastructure/entities/remote_album.entity.drift.dart';
+// import 'package:immich_mobile/infrastructure/entities/remote_album_asset.entity.drift.dart';
 import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart';
 import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart';
 import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
 import 'package:logging/logging.dart';
-import 'package:openapi/api.dart' as api show AssetVisibility;
-import 'package:openapi/api.dart' hide AssetVisibility;
+import 'package:openapi/api.dart' as api
+    show AssetVisibility, AssetOrder, AlbumUserRole;
+import 'package:openapi/api.dart'
+    hide AssetVisibility, AssetOrder, AlbumUserRole;
 
 class DriftSyncStreamRepository extends DriftDatabaseRepository
     implements ISyncStreamRepository {
@@ -161,6 +168,135 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository
     }
   }
 
+  @override
+  Future<void> updateAlbumsV1(Iterable<SyncAlbumV1> data) async {
+    try {
+      await _db.batch((batch) {
+        for (final album in data) {
+          final companion = RemoteAlbumEntityCompanion(
+            name: Value(album.name),
+            description: Value(album.description),
+            ownerId: Value(album.ownerId),
+            thumbnailAssetId: Value(album.thumbnailAssetId),
+            createdAt: Value(album.createdAt),
+            updatedAt: Value(album.updatedAt),
+            isActivityEnabled: Value(album.isActivityEnabled),
+            order: Value(album.order.toAssetOrder()),
+          );
+
+          batch.insert(
+            _db.remoteAlbumEntity,
+            companion.copyWith(id: Value(album.id)),
+            onConflict: DoUpdate((_) => companion),
+          );
+        }
+      });
+    } catch (e, s) {
+      _logger.severe('Error while processing updateAlbumsV1', e, s);
+      rethrow;
+    }
+  }
+
+  @override
+  Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data) async {
+    try {
+      _db.batch((batch) {
+        for (final album in data) {
+          batch.delete(
+            _db.remoteAlbumEntity,
+            RemoteAlbumEntityCompanion(id: Value(album.albumId)),
+          );
+        }
+      });
+    } catch (e, s) {
+      _logger.severe('Error while processing deleteAlbumsV1', e, s);
+      rethrow;
+    }
+  }
+
+  // @override
+  // Future<void> updateAlbumAssetsV1(Iterable<SyncAlbumAssetV1> data) async {
+  //   try {
+  //     await _db.remoteAlbumAssetEntity.insertAll(
+  //       data.map(
+  //         (albumAsset) => RemoteAlbumAssetEntityCompanion.insert(
+  //           albumId: albumAsset.albumId,
+  //           assetId: albumAsset.assetId,
+  //         ),
+  //       ),
+  //       mode: InsertMode.insertOrIgnore,
+  //     );
+  //   } catch (e, s) {
+  //     _logger.severe('Error while processing updateAlbumAssetsV1', e, s);
+  //     rethrow;
+  //   }
+  // }
+
+  // @override
+  // Future<void> deleteAlbumAssetsV1(Iterable<SyncAlbumAssetDeleteV1> data) async {
+  //   try {
+  //     await _db.batch((batch) {
+  //       for (final albumAsset in data) {
+  //         batch.delete(
+  //           _db.remoteAlbumAssetEntity,
+  //           RemoteAlbumAssetEntityCompanion(
+  //             albumId: Value(albumAsset.albumId),
+  //             assetId: Value(albumAsset.assetId),
+  //           ),
+  //         );
+  //       }
+  //     });
+  //   } catch (e, s) {
+  //     _logger.severe('Error while processing deleteAlbumAssetsV1', e, s);
+  //     rethrow;
+  //   }
+  // }
+
+  @override
+  Future<void> updateAlbumUsersV1(Iterable<SyncAlbumUserV1> data) async {
+    try {
+      await _db.batch((batch) {
+        for (final albumUser in data) {
+          final companion = AlbumUserEntityCompanion(
+            role: Value(albumUser.role.toAlbumUserRole()),
+          );
+
+          batch.insert(
+            _db.albumUserEntity,
+            companion.copyWith(
+              albumId: Value(albumUser.albumId),
+              userId: Value(albumUser.userId),
+            ),
+            onConflict: DoUpdate((_) => companion),
+          );
+        }
+      });
+    } catch (e, s) {
+      _logger.severe('Error while processing updateAlbumUsersV1', e, s);
+      rethrow;
+    }
+  }
+
+  @override
+  Future<void> deleteAlbumUsersV1(Iterable<SyncAlbumUserDeleteV1> data) async {
+    try {
+      await _db.batch((batch) {
+        for (final albumUser in data) {
+          batch.delete(
+            _db.albumUserEntity,
+            AlbumUserEntityCompanion(
+              albumId: Value(albumUser.albumId),
+              userId: Value(albumUser.userId),
+            ),
+          );
+        }
+      });
+    } catch (e, s) {
+      _logger.severe('Error while processing deleteAlbumUsersV1', e, s);
+      rethrow;
+    }
+  }
+
   Future<void> _updateAssetsV1(Iterable<SyncAssetV1> data) =>
       _db.batch((batch) {
         for (final asset in data) {
@@ -251,3 +387,19 @@ extension on api.AssetVisibility {
         _ => throw Exception('Unknown AssetVisibility value: $this'),
       };
 }
+
+extension on api.AssetOrder {
+  AssetOrder toAssetOrder() => switch (this) {
+        api.AssetOrder.asc => AssetOrder.asc,
+        api.AssetOrder.desc => AssetOrder.desc,
+        _ => throw Exception('Unknown AssetOrder value: $this'),
+      };
+}
+
+extension on api.AlbumUserRole {
+  AlbumUserRole toAlbumUserRole() => switch (this) {
+        api.AlbumUserRole.editor => AlbumUserRole.editor,
+        api.AlbumUserRole.viewer => AlbumUserRole.viewer,
+        _ => throw Exception('Unknown AlbumUserRole value: $this'),
+      };
+}
diff --git a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart
index edbbd23796..0e532375d0 100644
--- a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart
+++ b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart
@@ -63,8 +63,10 @@ final _features = [
     icon: Icons.delete_sweep_rounded,
     onTap: (_, ref) async {
       final db = ref.read(driftProvider);
-      await db.remoteAssetEntity.deleteAll();
       await db.remoteExifEntity.deleteAll();
+      await db.remoteAssetEntity.deleteAll();
+      await db.remoteAlbumEntity.deleteAll();
+      await db.remoteAlbumAssetEntity.deleteAll();
     },
   ),
   _Feature(
diff --git a/mobile/lib/presentation/pages/dev/media_stat.page.dart b/mobile/lib/presentation/pages/dev/media_stat.page.dart
index c074e524bf..4a1562900e 100644
--- a/mobile/lib/presentation/pages/dev/media_stat.page.dart
+++ b/mobile/lib/presentation/pages/dev/media_stat.page.dart
@@ -132,6 +132,10 @@ final _remoteStats = [
     name: 'Exif Entities',
     load: (db) => db.managers.remoteExifEntity.count(),
   ),
+  _Stat(
+    name: 'Remote Albums',
+    load: (db) => db.managers.remoteAlbumEntity.count(),
+  ),
 ];
 
 @RoutePage()
diff --git a/mobile/lib/repositories/auth.repository.dart b/mobile/lib/repositories/auth.repository.dart
index 01d2684faf..69a2fa0244 100644
--- a/mobile/lib/repositories/auth.repository.dart
+++ b/mobile/lib/repositories/auth.repository.dart
@@ -35,8 +35,10 @@ class AuthRepository extends DatabaseRepository implements IAuthRepository {
         db.albums.clear(),
         db.eTags.clear(),
         db.users.clear(),
-        _drift.remoteAssetEntity.deleteAll(),
         _drift.remoteExifEntity.deleteAll(),
+        _drift.remoteAssetEntity.deleteAll(),
+        _drift.remoteAlbumEntity.deleteAll(),
+        _drift.remoteAlbumAssetEntity.deleteAll(),
       ]);
     });
   }