feat(server): visibility column (#17939)

* feat: private view

* pr feedback

* sql generation

* feat: visibility column

* fix: set visibility value as the same as the still part after unlinked live photos

* fix: test

* pr feedback
This commit is contained in:
Alex
2025-05-06 12:12:48 -05:00
committed by GitHub
parent 016d7a6ceb
commit d33ce13561
90 changed files with 1137 additions and 867 deletions
+20 -21
View File
@@ -6,7 +6,7 @@ import { Stack } from 'src/database';
import { AssetFiles, AssetJobStatus, Assets, DB, Exif } from 'src/db';
import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
import { MapAsset } from 'src/dtos/asset-response.dto';
import { AssetFileType, AssetOrder, AssetStatus, AssetType } from 'src/enum';
import { AssetFileType, AssetOrder, AssetStatus, AssetType, AssetVisibility } from 'src/enum';
import {
anyUuid,
asUuid,
@@ -14,6 +14,7 @@ import {
removeUndefinedKeys,
truncatedDate,
unnest,
withDefaultVisibility,
withExif,
withFaces,
withFacesAndPeople,
@@ -30,8 +31,8 @@ export type AssetStats = Record<AssetType, number>;
export interface AssetStatsOptions {
isFavorite?: boolean;
isArchived?: boolean;
isTrashed?: boolean;
visibility?: AssetVisibility;
}
export interface LivePhotoSearchOptions {
@@ -52,7 +53,6 @@ export enum TimeBucketSize {
}
export interface AssetBuilderOptions {
isArchived?: boolean;
isFavorite?: boolean;
isTrashed?: boolean;
isDuplicate?: boolean;
@@ -64,6 +64,7 @@ export interface AssetBuilderOptions {
exifInfo?: boolean;
status?: AssetStatus;
assetType?: AssetType;
visibility?: AssetVisibility;
}
export interface TimeBucketOptions extends AssetBuilderOptions {
@@ -258,8 +259,7 @@ export class AssetRepository {
.where('asset_job_status.previewAt', 'is not', null)
.where(sql`(assets."localDateTime" at time zone 'UTC')::date`, '=', sql`today.date`)
.where('assets.ownerId', '=', anyUuid(ownerIds))
.where('assets.isVisible', '=', true)
.where('assets.isArchived', '=', false)
.where('assets.visibility', '=', AssetVisibility.TIMELINE)
.where((eb) =>
eb.exists((qb) =>
qb
@@ -348,7 +348,7 @@ export class AssetRepository {
.select(['deviceAssetId'])
.where('ownerId', '=', asUuid(ownerId))
.where('deviceId', '=', deviceId)
.where('isVisible', '=', true)
.where('visibility', '!=', AssetVisibility.HIDDEN)
.where('deletedAt', 'is', null)
.execute();
@@ -393,7 +393,7 @@ export class AssetRepository {
.whereRef('stacked.stackId', '=', 'asset_stack.id')
.whereRef('stacked.id', '!=', 'asset_stack.primaryAssetId')
.where('stacked.deletedAt', 'is', null)
.where('stacked.isArchived', '=', false)
.where('stacked.visibility', '=', AssetVisibility.TIMELINE)
.groupBy('asset_stack.id')
.as('stacked_assets'),
(join) => join.on('asset_stack.id', 'is not', null),
@@ -503,7 +503,7 @@ export class AssetRepository {
.executeTakeFirst();
}
getStatistics(ownerId: string, { isArchived, isFavorite, isTrashed }: AssetStatsOptions): Promise<AssetStats> {
getStatistics(ownerId: string, { visibility, isFavorite, isTrashed }: AssetStatsOptions): Promise<AssetStats> {
return this.db
.selectFrom('assets')
.select((eb) => eb.fn.countAll<number>().filterWhere('type', '=', AssetType.AUDIO).as(AssetType.AUDIO))
@@ -511,8 +511,8 @@ export class AssetRepository {
.select((eb) => eb.fn.countAll<number>().filterWhere('type', '=', AssetType.VIDEO).as(AssetType.VIDEO))
.select((eb) => eb.fn.countAll<number>().filterWhere('type', '=', AssetType.OTHER).as(AssetType.OTHER))
.where('ownerId', '=', asUuid(ownerId))
.where('isVisible', '=', true)
.$if(isArchived !== undefined, (qb) => qb.where('isArchived', '=', isArchived!))
.$if(visibility === undefined, withDefaultVisibility)
.$if(!!visibility, (qb) => qb.where('assets.visibility', '=', visibility!))
.$if(isFavorite !== undefined, (qb) => qb.where('isFavorite', '=', isFavorite!))
.$if(!!isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED))
.where('deletedAt', isTrashed ? 'is not' : 'is', null)
@@ -525,7 +525,7 @@ export class AssetRepository {
.selectAll('assets')
.$call(withExif)
.where('ownerId', '=', anyUuid(userIds))
.where('isVisible', '=', true)
.where('visibility', '!=', AssetVisibility.HIDDEN)
.where('deletedAt', 'is', null)
.orderBy((eb) => eb.fn('random'))
.limit(take)
@@ -542,7 +542,8 @@ export class AssetRepository {
.select(truncatedDate<Date>(options.size).as('timeBucket'))
.$if(!!options.isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED))
.where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null)
.where('assets.isVisible', '=', true)
.$if(options.visibility === undefined, withDefaultVisibility)
.$if(!!options.visibility, (qb) => qb.where('assets.visibility', '=', options.visibility!))
.$if(!!options.albumId, (qb) =>
qb
.innerJoin('albums_assets_assets', 'assets.id', 'albums_assets_assets.assetsId')
@@ -559,7 +560,6 @@ export class AssetRepository {
.where((eb) => eb.or([eb('assets.stackId', 'is', null), eb(eb.table('asset_stack'), 'is not', null)])),
)
.$if(!!options.userIds, (qb) => qb.where('assets.ownerId', '=', anyUuid(options.userIds!)))
.$if(options.isArchived !== undefined, (qb) => qb.where('assets.isArchived', '=', options.isArchived!))
.$if(options.isFavorite !== undefined, (qb) => qb.where('assets.isFavorite', '=', options.isFavorite!))
.$if(!!options.assetType, (qb) => qb.where('assets.type', '=', options.assetType!))
.$if(options.isDuplicate !== undefined, (qb) =>
@@ -594,7 +594,6 @@ export class AssetRepository {
)
.$if(!!options.personId, (qb) => hasPeople(qb, [options.personId!]))
.$if(!!options.userIds, (qb) => qb.where('assets.ownerId', '=', anyUuid(options.userIds!)))
.$if(options.isArchived !== undefined, (qb) => qb.where('assets.isArchived', '=', options.isArchived!))
.$if(options.isFavorite !== undefined, (qb) => qb.where('assets.isFavorite', '=', options.isFavorite!))
.$if(!!options.withStacked, (qb) =>
qb
@@ -610,7 +609,7 @@ export class AssetRepository {
.select((eb) => eb.fn.count(eb.table('stacked')).as('assetCount'))
.whereRef('stacked.stackId', '=', 'asset_stack.id')
.where('stacked.deletedAt', 'is', null)
.where('stacked.isArchived', '=', false)
.where('stacked.visibility', '!=', AssetVisibility.ARCHIVE)
.groupBy('asset_stack.id')
.as('stacked_assets'),
(join) => join.on('asset_stack.id', 'is not', null),
@@ -624,7 +623,8 @@ export class AssetRepository {
.$if(!!options.isTrashed, (qb) => qb.where('assets.status', '!=', AssetStatus.DELETED))
.$if(!!options.tagId, (qb) => withTagId(qb, options.tagId!))
.where('assets.deletedAt', options.isTrashed ? 'is not' : 'is', null)
.where('assets.isVisible', '=', true)
.$if(options.visibility == undefined, withDefaultVisibility)
.$if(!!options.visibility, (qb) => qb.where('assets.visibility', '=', options.visibility!))
.where(truncatedDate(options.size), '=', timeBucket.replace(/^[+-]/, ''))
.orderBy('assets.localDateTime', options.order ?? 'desc')
.execute();
@@ -658,7 +658,7 @@ export class AssetRepository {
.where('assets.duplicateId', 'is not', null)
.$narrowType<{ duplicateId: NotNull }>()
.where('assets.deletedAt', 'is', null)
.where('assets.isVisible', '=', true)
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
.where('assets.stackId', 'is', null)
.groupBy('assets.duplicateId'),
)
@@ -703,8 +703,7 @@ export class AssetRepository {
.select(['assetId as data', 'exif.city as value'])
.$narrowType<{ value: NotNull }>()
.where('ownerId', '=', asUuid(ownerId))
.where('isVisible', '=', true)
.where('isArchived', '=', false)
.where('visibility', '=', AssetVisibility.TIMELINE)
.where('type', '=', AssetType.IMAGE)
.where('deletedAt', 'is', null)
.limit(maxFields)
@@ -743,7 +742,7 @@ export class AssetRepository {
)
.select((eb) => eb.fn.toJson(eb.table('stacked_assets')).$castTo<Stack | null>().as('stack'))
.where('assets.ownerId', '=', asUuid(ownerId))
.where('assets.isVisible', '=', true)
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
.where('assets.updatedAt', '<=', updatedUntil)
.$if(!!lastId, (qb) => qb.where('assets.id', '>', lastId!))
.orderBy('assets.id')
@@ -771,7 +770,7 @@ export class AssetRepository {
)
.select((eb) => eb.fn.toJson(eb.table('stacked_assets').$castTo<Stack | null>()).as('stack'))
.where('assets.ownerId', '=', anyUuid(options.userIds))
.where('assets.isVisible', '=', true)
.where('assets.visibility', '!=', AssetVisibility.HIDDEN)
.where('assets.updatedAt', '>', options.updatedAfter)
.limit(options.limit)
.execute();