mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
refactor: use cursors instead of pages
This commit is contained in:
+17
-18
@@ -13,19 +13,18 @@ part of openapi.api;
|
|||||||
class IntegrityGetReportDto {
|
class IntegrityGetReportDto {
|
||||||
/// Returns a new [IntegrityGetReportDto] instance.
|
/// Returns a new [IntegrityGetReportDto] instance.
|
||||||
IntegrityGetReportDto({
|
IntegrityGetReportDto({
|
||||||
this.page,
|
this.cursor,
|
||||||
this.size,
|
this.limit,
|
||||||
required this.type,
|
required this.type,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Minimum value: 1
|
|
||||||
///
|
///
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
/// Please note: This property should have been non-nullable! Since the specification file
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
/// does not include a default value (using the "default:" property), however, the generated
|
||||||
/// source code must fall back to having a nullable type.
|
/// source code must fall back to having a nullable type.
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||||
///
|
///
|
||||||
num? page;
|
DateTime? cursor;
|
||||||
|
|
||||||
/// Minimum value: 1
|
/// Minimum value: 1
|
||||||
///
|
///
|
||||||
@@ -34,37 +33,37 @@ class IntegrityGetReportDto {
|
|||||||
/// source code must fall back to having a nullable type.
|
/// source code must fall back to having a nullable type.
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||||
///
|
///
|
||||||
num? size;
|
num? limit;
|
||||||
|
|
||||||
IntegrityReportType type;
|
IntegrityReportType type;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is IntegrityGetReportDto &&
|
bool operator ==(Object other) => identical(this, other) || other is IntegrityGetReportDto &&
|
||||||
other.page == page &&
|
other.cursor == cursor &&
|
||||||
other.size == size &&
|
other.limit == limit &&
|
||||||
other.type == type;
|
other.type == type;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
// ignore: unnecessary_parenthesis
|
// ignore: unnecessary_parenthesis
|
||||||
(page == null ? 0 : page!.hashCode) +
|
(cursor == null ? 0 : cursor!.hashCode) +
|
||||||
(size == null ? 0 : size!.hashCode) +
|
(limit == null ? 0 : limit!.hashCode) +
|
||||||
(type.hashCode);
|
(type.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'IntegrityGetReportDto[page=$page, size=$size, type=$type]';
|
String toString() => 'IntegrityGetReportDto[cursor=$cursor, limit=$limit, type=$type]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
if (this.page != null) {
|
if (this.cursor != null) {
|
||||||
json[r'page'] = this.page;
|
json[r'cursor'] = this.cursor!.toUtc().toIso8601String();
|
||||||
} else {
|
} else {
|
||||||
// json[r'page'] = null;
|
// json[r'cursor'] = null;
|
||||||
}
|
}
|
||||||
if (this.size != null) {
|
if (this.limit != null) {
|
||||||
json[r'size'] = this.size;
|
json[r'limit'] = this.limit;
|
||||||
} else {
|
} else {
|
||||||
// json[r'size'] = null;
|
// json[r'limit'] = null;
|
||||||
}
|
}
|
||||||
json[r'type'] = this.type;
|
json[r'type'] = this.type;
|
||||||
return json;
|
return json;
|
||||||
@@ -79,8 +78,8 @@ class IntegrityGetReportDto {
|
|||||||
final json = value.cast<String, dynamic>();
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
return IntegrityGetReportDto(
|
return IntegrityGetReportDto(
|
||||||
page: num.parse('${json[r'page']}'),
|
cursor: mapDateTime(json, r'cursor', r''),
|
||||||
size: num.parse('${json[r'size']}'),
|
limit: num.parse('${json[r'limit']}'),
|
||||||
type: IntegrityReportType.fromJson(json[r'type'])!,
|
type: IntegrityReportType.fromJson(json[r'type'])!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-11
@@ -13,32 +13,42 @@ part of openapi.api;
|
|||||||
class IntegrityReportResponseDto {
|
class IntegrityReportResponseDto {
|
||||||
/// Returns a new [IntegrityReportResponseDto] instance.
|
/// Returns a new [IntegrityReportResponseDto] instance.
|
||||||
IntegrityReportResponseDto({
|
IntegrityReportResponseDto({
|
||||||
required this.hasNextPage,
|
|
||||||
this.items = const [],
|
this.items = const [],
|
||||||
|
this.nextCursor,
|
||||||
});
|
});
|
||||||
|
|
||||||
bool hasNextPage;
|
|
||||||
|
|
||||||
List<IntegrityReportDto> items;
|
List<IntegrityReportDto> items;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Please note: This property should have been non-nullable! Since the specification file
|
||||||
|
/// does not include a default value (using the "default:" property), however, the generated
|
||||||
|
/// source code must fall back to having a nullable type.
|
||||||
|
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||||
|
///
|
||||||
|
DateTime? nextCursor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is IntegrityReportResponseDto &&
|
bool operator ==(Object other) => identical(this, other) || other is IntegrityReportResponseDto &&
|
||||||
other.hasNextPage == hasNextPage &&
|
_deepEquality.equals(other.items, items) &&
|
||||||
_deepEquality.equals(other.items, items);
|
other.nextCursor == nextCursor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
// ignore: unnecessary_parenthesis
|
// ignore: unnecessary_parenthesis
|
||||||
(hasNextPage.hashCode) +
|
(items.hashCode) +
|
||||||
(items.hashCode);
|
(nextCursor == null ? 0 : nextCursor!.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'IntegrityReportResponseDto[hasNextPage=$hasNextPage, items=$items]';
|
String toString() => 'IntegrityReportResponseDto[items=$items, nextCursor=$nextCursor]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
json[r'hasNextPage'] = this.hasNextPage;
|
|
||||||
json[r'items'] = this.items;
|
json[r'items'] = this.items;
|
||||||
|
if (this.nextCursor != null) {
|
||||||
|
json[r'nextCursor'] = this.nextCursor!.toUtc().toIso8601String();
|
||||||
|
} else {
|
||||||
|
// json[r'nextCursor'] = null;
|
||||||
|
}
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,8 +61,8 @@ class IntegrityReportResponseDto {
|
|||||||
final json = value.cast<String, dynamic>();
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
return IntegrityReportResponseDto(
|
return IntegrityReportResponseDto(
|
||||||
hasNextPage: mapValueOfType<bool>(json, r'hasNextPage')!,
|
|
||||||
items: IntegrityReportDto.listFromJson(json[r'items']),
|
items: IntegrityReportDto.listFromJson(json[r'items']),
|
||||||
|
nextCursor: mapDateTime(json, r'nextCursor', r''),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -100,7 +110,6 @@ class IntegrityReportResponseDto {
|
|||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
/// The list of required keys that must be present in a JSON.
|
||||||
static const requiredKeys = <String>{
|
static const requiredKeys = <String>{
|
||||||
'hasNextPage',
|
|
||||||
'items',
|
'items',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16928,11 +16928,11 @@
|
|||||||
},
|
},
|
||||||
"IntegrityGetReportDto": {
|
"IntegrityGetReportDto": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"page": {
|
"cursor": {
|
||||||
"minimum": 1,
|
"format": "uuid",
|
||||||
"type": "number"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"size": {
|
"limit": {
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
@@ -16974,18 +16974,17 @@
|
|||||||
},
|
},
|
||||||
"IntegrityReportResponseDto": {
|
"IntegrityReportResponseDto": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"hasNextPage": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"items": {
|
"items": {
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/components/schemas/IntegrityReportDto"
|
"$ref": "#/components/schemas/IntegrityReportDto"
|
||||||
},
|
},
|
||||||
"type": "array"
|
"type": "array"
|
||||||
|
},
|
||||||
|
"nextCursor": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"hasNextPage",
|
|
||||||
"items"
|
"items"
|
||||||
],
|
],
|
||||||
"type": "object"
|
"type": "object"
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ export type ActivityStatisticsResponseDto = {
|
|||||||
likes: number;
|
likes: number;
|
||||||
};
|
};
|
||||||
export type IntegrityGetReportDto = {
|
export type IntegrityGetReportDto = {
|
||||||
page?: number;
|
cursor?: string;
|
||||||
size?: number;
|
limit?: number;
|
||||||
"type": IntegrityReportType;
|
"type": IntegrityReportType;
|
||||||
};
|
};
|
||||||
export type IntegrityReportDto = {
|
export type IntegrityReportDto = {
|
||||||
@@ -51,8 +51,8 @@ export type IntegrityReportDto = {
|
|||||||
"type": IntegrityReportType;
|
"type": IntegrityReportType;
|
||||||
};
|
};
|
||||||
export type IntegrityReportResponseDto = {
|
export type IntegrityReportResponseDto = {
|
||||||
hasNextPage: boolean;
|
|
||||||
items: IntegrityReportDto[];
|
items: IntegrityReportDto[];
|
||||||
|
nextCursor?: string;
|
||||||
};
|
};
|
||||||
export type IntegrityReportSummaryResponseDto = {
|
export type IntegrityReportSummaryResponseDto = {
|
||||||
checksum_mismatch: number;
|
checksum_mismatch: number;
|
||||||
|
|||||||
@@ -130,6 +130,14 @@ const create = (path: string, up: string[], down: string[]) => {
|
|||||||
const compare = async () => {
|
const compare = async () => {
|
||||||
const configRepository = new ConfigRepository();
|
const configRepository = new ConfigRepository();
|
||||||
const { database } = configRepository.getEnv();
|
const { database } = configRepository.getEnv();
|
||||||
|
database.config = {
|
||||||
|
connectionType: 'parts',
|
||||||
|
database: 'immich',
|
||||||
|
host: 'database',
|
||||||
|
password: 'postgres',
|
||||||
|
username: 'postgres',
|
||||||
|
port: 5432,
|
||||||
|
};
|
||||||
const db = postgres(asPostgresConnectionConfig(database.config));
|
const db = postgres(asPostgresConnectionConfig(database.config));
|
||||||
|
|
||||||
const source = schemaFromCode({ overrides: true, namingStrategy: 'default' });
|
const source = schemaFromCode({ overrides: true, namingStrategy: 'default' });
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { Type } from 'class-transformer';
|
import { Type } from 'class-transformer';
|
||||||
import { IsInt, IsOptional, Min } from 'class-validator';
|
import { IsInt, IsOptional, IsUUID, Min } from 'class-validator';
|
||||||
import { IntegrityReportType } from 'src/enum';
|
import { IntegrityReportType } from 'src/enum';
|
||||||
import { ValidateEnum } from 'src/validation';
|
import { ValidateEnum } from 'src/validation';
|
||||||
|
|
||||||
@@ -17,17 +17,15 @@ export class IntegrityGetReportDto {
|
|||||||
@ValidateEnum({ enum: IntegrityReportType, name: 'IntegrityReportType' })
|
@ValidateEnum({ enum: IntegrityReportType, name: 'IntegrityReportType' })
|
||||||
type!: IntegrityReportType;
|
type!: IntegrityReportType;
|
||||||
|
|
||||||
@IsInt()
|
|
||||||
@Min(1)
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@Type(() => Number)
|
@IsUUID()
|
||||||
page?: number;
|
cursor?: string;
|
||||||
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@Min(1)
|
@Min(1)
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@Type(() => Number)
|
@Type(() => Number)
|
||||||
size?: number;
|
limit?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class IntegrityDeleteReportDto {
|
export class IntegrityDeleteReportDto {
|
||||||
@@ -44,5 +42,5 @@ class IntegrityReportDto {
|
|||||||
|
|
||||||
export class IntegrityReportResponseDto {
|
export class IntegrityReportResponseDto {
|
||||||
items!: IntegrityReportDto[];
|
items!: IntegrityReportDto[];
|
||||||
hasNextPage!: boolean;
|
nextCursor?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,16 +31,16 @@ select
|
|||||||
"type",
|
"type",
|
||||||
"path",
|
"path",
|
||||||
"assetId",
|
"assetId",
|
||||||
"fileAssetId"
|
"fileAssetId",
|
||||||
|
"createdAt"
|
||||||
from
|
from
|
||||||
"integrity_report"
|
"integrity_report"
|
||||||
where
|
where
|
||||||
"type" = $1
|
"type" = $1
|
||||||
|
and "createdAt" <= $2
|
||||||
order by
|
order by
|
||||||
"createdAt" desc
|
"createdAt" desc
|
||||||
limit
|
limit
|
||||||
$2
|
|
||||||
offset
|
|
||||||
$3
|
$3
|
||||||
|
|
||||||
-- IntegrityRepository.getAssetPathsByPaths
|
-- IntegrityRepository.getAssetPathsByPaths
|
||||||
|
|||||||
@@ -5,11 +5,10 @@ import { DummyValue, GenerateSql } from 'src/decorators';
|
|||||||
import { IntegrityReportType } from 'src/enum';
|
import { IntegrityReportType } from 'src/enum';
|
||||||
import { DB } from 'src/schema';
|
import { DB } from 'src/schema';
|
||||||
import { IntegrityReportTable } from 'src/schema/tables/integrity-report.table';
|
import { IntegrityReportTable } from 'src/schema/tables/integrity-report.table';
|
||||||
import { paginationHelper } from 'src/utils/pagination';
|
|
||||||
|
|
||||||
export interface ReportPaginationOptions {
|
export interface ReportPaginationOptions {
|
||||||
page: number;
|
cursor?: string;
|
||||||
size: number;
|
limit: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -64,18 +63,21 @@ export class IntegrityRepository {
|
|||||||
.executeTakeFirstOrThrow();
|
.executeTakeFirstOrThrow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [{ page: 1, size: 100 }, DummyValue.STRING] })
|
@GenerateSql({ params: [{ cursor: DummyValue.NUMBER, limit: 100 }, DummyValue.STRING] })
|
||||||
async getIntegrityReports(pagination: ReportPaginationOptions, type: IntegrityReportType) {
|
async getIntegrityReports(pagination: ReportPaginationOptions, type: IntegrityReportType) {
|
||||||
const items = await this.db
|
const items = await this.db
|
||||||
.selectFrom('integrity_report')
|
.selectFrom('integrity_report')
|
||||||
.select(['id', 'type', 'path', 'assetId', 'fileAssetId'])
|
.select(['id', 'type', 'path', 'assetId', 'fileAssetId', 'createdAt'])
|
||||||
.where('type', '=', type)
|
.where('type', '=', type)
|
||||||
.orderBy('createdAt', 'desc')
|
.$if(pagination.cursor !== undefined, (eb) => eb.where('id', '<=', pagination.cursor!))
|
||||||
.limit(pagination.size + 1)
|
.orderBy('id', 'desc')
|
||||||
.offset((pagination.page - 1) * pagination.size)
|
.limit(pagination.limit + 1)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
return paginationHelper(items, pagination.size);
|
return {
|
||||||
|
items: items.slice(0, pagination.limit),
|
||||||
|
nextCursor: items[pagination.limit]?.id,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.STRING] })
|
@GenerateSql({ params: [DummyValue.STRING] })
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Kysely, sql } from 'kysely';
|
|||||||
|
|
||||||
export async function up(db: Kysely<any>): Promise<void> {
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
await sql`CREATE TABLE "integrity_report" (
|
await sql`CREATE TABLE "integrity_report" (
|
||||||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
"id" uuid NOT NULL DEFAULT immich_uuid_v7(),
|
||||||
"type" character varying NOT NULL,
|
"type" character varying NOT NULL,
|
||||||
"path" character varying NOT NULL,
|
"path" character varying NOT NULL,
|
||||||
"createdAt" timestamp with time zone NOT NULL DEFAULT now(),
|
"createdAt" timestamp with time zone NOT NULL DEFAULT now(),
|
||||||
|
|||||||
@@ -1,21 +1,13 @@
|
|||||||
|
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||||
import { IntegrityReportType } from 'src/enum';
|
import { IntegrityReportType } from 'src/enum';
|
||||||
import { AssetFileTable } from 'src/schema/tables/asset-file.table';
|
import { AssetFileTable } from 'src/schema/tables/asset-file.table';
|
||||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||||
import {
|
import { Column, CreateDateColumn, ForeignKeyColumn, Generated, Table, Timestamp, Unique } from 'src/sql-tools';
|
||||||
Column,
|
|
||||||
CreateDateColumn,
|
|
||||||
ForeignKeyColumn,
|
|
||||||
Generated,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
Table,
|
|
||||||
Timestamp,
|
|
||||||
Unique,
|
|
||||||
} from 'src/sql-tools';
|
|
||||||
|
|
||||||
@Table('integrity_report')
|
@Table('integrity_report')
|
||||||
@Unique({ columns: ['type', 'path'] })
|
@Unique({ columns: ['type', 'path'] })
|
||||||
export class IntegrityReportTable {
|
export class IntegrityReportTable {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedUuidV7Column()
|
||||||
id!: Generated<string>;
|
id!: Generated<string>;
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ export class IntegrityService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getIntegrityReport(dto: IntegrityGetReportDto): Promise<IntegrityReportResponseDto> {
|
async getIntegrityReport(dto: IntegrityGetReportDto): Promise<IntegrityReportResponseDto> {
|
||||||
return this.integrityRepository.getIntegrityReports({ page: dto.page || 1, size: dto.size || 100 }, dto.type);
|
return this.integrityRepository.getIntegrityReports({ cursor: dto.cursor, limit: dto.limit || 100 }, dto.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
getIntegrityReportCsv(type: IntegrityReportType): Readable {
|
getIntegrityReportCsv(type: IntegrityReportType): Readable {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
ManualJobName,
|
ManualJobName,
|
||||||
} from '@immich/sdk';
|
} from '@immich/sdk';
|
||||||
import {
|
import {
|
||||||
|
Button,
|
||||||
HStack,
|
HStack,
|
||||||
IconButton,
|
IconButton,
|
||||||
menuManager,
|
menuManager,
|
||||||
@@ -21,14 +22,7 @@
|
|||||||
type ContextMenuBaseProps,
|
type ContextMenuBaseProps,
|
||||||
type MenuItems,
|
type MenuItems,
|
||||||
} from '@immich/ui';
|
} from '@immich/ui';
|
||||||
import {
|
import { mdiDotsVertical, mdiDownload, mdiTrashCanOutline } from '@mdi/js';
|
||||||
mdiChevronLeft,
|
|
||||||
mdiChevronRight,
|
|
||||||
mdiDotsVertical,
|
|
||||||
mdiDownload,
|
|
||||||
mdiPageFirst,
|
|
||||||
mdiTrashCanOutline,
|
|
||||||
} from '@mdi/js';
|
|
||||||
import { onDestroy, onMount } from 'svelte';
|
import { onDestroy, onMount } from 'svelte';
|
||||||
import { t } from 'svelte-i18n';
|
import { t } from 'svelte-i18n';
|
||||||
import { SvelteSet } from 'svelte/reactivity';
|
import { SvelteSet } from 'svelte/reactivity';
|
||||||
@@ -41,18 +35,18 @@
|
|||||||
let { data }: Props = $props();
|
let { data }: Props = $props();
|
||||||
|
|
||||||
let deleting = new SvelteSet();
|
let deleting = new SvelteSet();
|
||||||
let page = $state(1);
|
|
||||||
let integrityReport = $state(data.integrityReport);
|
let integrityReport = $state(data.integrityReport);
|
||||||
|
|
||||||
async function loadPage(target: number) {
|
async function loadMore() {
|
||||||
integrityReport = await getIntegrityReport({
|
const { items, nextCursor } = await getIntegrityReport({
|
||||||
integrityGetReportDto: {
|
integrityGetReportDto: {
|
||||||
type: data.type,
|
type: data.type,
|
||||||
page: target,
|
cursor: integrityReport.nextCursor,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
page = target;
|
integrityReport.items.push(...items);
|
||||||
|
integrityReport.nextCursor = nextCursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function removeAll() {
|
async function removeAll() {
|
||||||
@@ -108,7 +102,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function download(reportId: string) {
|
function download(reportId: string) {
|
||||||
location.href = `${getBaseUrl()}/admin/maintenance/integrity/report/${reportId}/file`;
|
location.href = `${getBaseUrl()}/admin/integrity/report/${reportId}/file`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleOpen = async (event: Event, props: Partial<ContextMenuBaseProps>, reportId: string) => {
|
const handleOpen = async (event: Event, props: Partial<ContextMenuBaseProps>, reportId: string) => {
|
||||||
@@ -150,7 +144,11 @@
|
|||||||
if (jobs.integrityCheck.queueStatus.isActive) {
|
if (jobs.integrityCheck.queueStatus.isActive) {
|
||||||
expectingUpdate = true;
|
expectingUpdate = true;
|
||||||
} else if (expectingUpdate) {
|
} else if (expectingUpdate) {
|
||||||
await loadPage(page);
|
integrityReport = await getIntegrityReport({
|
||||||
|
integrityGetReportDto: {
|
||||||
|
type: data.type,
|
||||||
|
},
|
||||||
|
});
|
||||||
expectingUpdate = false;
|
expectingUpdate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,9 +193,7 @@
|
|||||||
<th class="w-1/8"></th>
|
<th class="w-1/8"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody
|
<tbody class="block w-full rounded-md border dark:border-immich-dark-gray dark:text-immich-dark-fg">
|
||||||
class="block max-h-80 w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray dark:text-immich-dark-fg"
|
|
||||||
>
|
|
||||||
{#each integrityReport.items as { id, path } (id)}
|
{#each integrityReport.items as { id, path } (id)}
|
||||||
<tr
|
<tr
|
||||||
class={`flex py-1 w-full place-items-center even:bg-subtle/20 odd:bg-subtle/80 ${deleting.has(id) || deleting.has('all') ? 'text-gray-500' : ''}`}
|
class={`flex py-1 w-full place-items-center even:bg-subtle/20 odd:bg-subtle/80 ${deleting.has(id) || deleting.has('all') ? 'text-gray-500' : ''}`}
|
||||||
@@ -216,31 +212,13 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{/each}
|
{/each}
|
||||||
</tbody>
|
</tbody>
|
||||||
<tfoot>
|
{#if integrityReport.nextCursor}
|
||||||
<HStack class="mt-4 items-center justify-end">
|
<tfoot>
|
||||||
<IconButton
|
<HStack class="mt-4 items-center justify-center">
|
||||||
disabled={page === 1}
|
<Button color="primary" onclick={() => loadMore()}>Load More</Button>
|
||||||
color="primary"
|
</HStack>
|
||||||
icon={mdiPageFirst}
|
</tfoot>
|
||||||
aria-label={$t('first_page')}
|
{/if}
|
||||||
onclick={() => loadPage(1)}
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
disabled={page === 1}
|
|
||||||
color="primary"
|
|
||||||
icon={mdiChevronLeft}
|
|
||||||
aria-label={$t('previous_page')}
|
|
||||||
onclick={() => loadPage(page - 1)}
|
|
||||||
/>
|
|
||||||
<IconButton
|
|
||||||
disabled={!integrityReport.hasNextPage}
|
|
||||||
color="primary"
|
|
||||||
icon={mdiChevronRight}
|
|
||||||
aria-label={$t('next_page')}
|
|
||||||
onclick={() => loadPage(page + 1)}
|
|
||||||
/>
|
|
||||||
</HStack>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
</table>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user