Compare commits
2 Commits
chore-hand
...
sqlite-rem
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
377b886bd6 | ||
|
|
5d99eabe05 |
@@ -7,7 +7,7 @@ sidebar_position: 3
|
|||||||
|
|
||||||
Dev Containers provide a consistent, reproducible development environment using Docker containers. With a single click, you can get started with an Immich development environment on Mac, Linux, Windows, or in the cloud using GitHub Codespaces.
|
Dev Containers provide a consistent, reproducible development environment using Docker containers. With a single click, you can get started with an Immich development environment on Mac, Linux, Windows, or in the cloud using GitHub Codespaces.
|
||||||
|
|
||||||
Get started fast!
|
[](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/immich-app/immich/)
|
||||||
|
|
||||||
[](https://codespaces.new/immich-app/immich/)
|
[](https://codespaces.new/immich-app/immich/)
|
||||||
|
|
||||||
@@ -71,7 +71,7 @@ cd immich
|
|||||||
|
|
||||||
The immich dev containers read environment variables from your shell environment, not from `.env` files. This allows them to work in cloud environments without pre-configuration.
|
The immich dev containers read environment variables from your shell environment, not from `.env` files. This allows them to work in cloud environments without pre-configuration.
|
||||||
|
|
||||||
:::important Configuration
|
:::important Required Configuration
|
||||||
When running locally, and if you want to create (or use an existing) DB and/or photo storage folder, you must set the `UPLOAD_LOCATION` variable in your shell environment before launching the Dev Container. This determines where uploaded files are stored and also where the DB stores it data.
|
When running locally, and if you want to create (or use an existing) DB and/or photo storage folder, you must set the `UPLOAD_LOCATION` variable in your shell environment before launching the Dev Container. This determines where uploaded files are stored and also where the DB stores it data.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -88,10 +88,6 @@ source ~/.bashrc
|
|||||||
|
|
||||||
### Step 3: Launch the Dev Container
|
### Step 3: Launch the Dev Container
|
||||||
|
|
||||||
:::tip
|
|
||||||
Immich development makes extensive use of specialized [base images](https://github.com/immich-app/base-images) for its docker-compose based development. For this reason, you won't be able to use VSCode's **_Clone Repository in a Container Volume_** command.
|
|
||||||
:::
|
|
||||||
|
|
||||||
#### Using VS Code UI:
|
#### Using VS Code UI:
|
||||||
|
|
||||||
1. Open the cloned repository in VS Code
|
1. Open the cloned repository in VS Code
|
||||||
|
|||||||
@@ -373,12 +373,10 @@
|
|||||||
"admin_password": "Admin Password",
|
"admin_password": "Admin Password",
|
||||||
"administration": "Administration",
|
"administration": "Administration",
|
||||||
"advanced": "Advanced",
|
"advanced": "Advanced",
|
||||||
"advanced_settings_beta_timeline_subtitle": "Try the new app experience.",
|
|
||||||
"advanced_settings_beta_timeline_title": "Beta Timeline",
|
|
||||||
"advanced_settings_enable_alternate_media_filter_subtitle": "Use this option to filter media during sync based on alternate criteria. Only try this if you have issues with the app detecting all albums.",
|
"advanced_settings_enable_alternate_media_filter_subtitle": "Use this option to filter media during sync based on alternate criteria. Only try this if you have issues with the app detecting all albums.",
|
||||||
"advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Use alternate device album sync filter",
|
"advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Use alternate device album sync filter",
|
||||||
"advanced_settings_log_level_title": "Log level: {level}",
|
"advanced_settings_log_level_title": "Log level: {level}",
|
||||||
"advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from local assets. Activate this setting to load remote images instead.",
|
"advanced_settings_prefer_remote_subtitle": "Some devices are painfully slow to load thumbnails from assets on the device. Activate this setting to load remote images instead.",
|
||||||
"advanced_settings_prefer_remote_title": "Prefer remote images",
|
"advanced_settings_prefer_remote_title": "Prefer remote images",
|
||||||
"advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request",
|
"advanced_settings_proxy_headers_subtitle": "Define proxy headers Immich should send with each network request",
|
||||||
"advanced_settings_proxy_headers_title": "Proxy Headers",
|
"advanced_settings_proxy_headers_title": "Proxy Headers",
|
||||||
|
|||||||
1
mobile/drift_schemas/main/drift_schema_v2.json
generated
1
mobile/drift_schemas/main/drift_schema_v2.json
generated
File diff suppressed because one or more lines are too long
@@ -68,9 +68,7 @@ enum StoreKey<T> {
|
|||||||
manageLocalMediaAndroid<bool>._(137),
|
manageLocalMediaAndroid<bool>._(137),
|
||||||
|
|
||||||
// Experimental stuff
|
// Experimental stuff
|
||||||
photoManagerCustomFilter<bool>._(1000),
|
photoManagerCustomFilter<bool>._(1000);
|
||||||
betaPromptShown<bool>._(1001),
|
|
||||||
betaTimeline<bool>._(1002);
|
|
||||||
|
|
||||||
const StoreKey._(this.id);
|
const StoreKey._(this.id);
|
||||||
final int id;
|
final int id;
|
||||||
|
|||||||
@@ -93,8 +93,6 @@ class StoreService {
|
|||||||
await _storeRepository.deleteAll();
|
await _storeRepository.deleteAll();
|
||||||
_cache.clear();
|
_cache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get isBetaTimelineEnabled => tryGet(StoreKey.betaTimeline) ?? false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class StoreKeyNotFoundException implements Exception {
|
class StoreKeyNotFoundException implements Exception {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'dart:async';
|
|||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
|
||||||
import 'package:immich_mobile/constants/constants.dart';
|
import 'package:immich_mobile/constants/constants.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/setting.model.dart';
|
import 'package:immich_mobile/domain/models/setting.model.dart';
|
||||||
@@ -113,19 +112,8 @@ class TimelineService {
|
|||||||
totalAssets - _bufferOffset,
|
totalAssets - _bufferOffset,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
_buffer = await _assetSource(offset, count);
|
||||||
try {
|
_bufferOffset = offset;
|
||||||
_buffer = await _assetSource(offset, count);
|
|
||||||
_bufferOffset = offset;
|
|
||||||
} catch (e) {
|
|
||||||
if (e.toString().contains('database has been locked')) {
|
|
||||||
debugPrint(
|
|
||||||
"TimelineService::loadAssets - Database locked, returning cached assets",
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// change the state's total assets count only after the buffer is reloaded
|
// change the state's total assets count only after the buffer is reloaded
|
||||||
@@ -165,22 +153,8 @@ class TimelineService {
|
|||||||
: (len > kTimelineAssetLoadBatchSize ? index : index + count - len),
|
: (len > kTimelineAssetLoadBatchSize ? index : index + count - len),
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
_buffer = await _assetSource(start, len);
|
||||||
_buffer = await _assetSource(start, len);
|
_bufferOffset = start;
|
||||||
_bufferOffset = start;
|
|
||||||
} catch (e) {
|
|
||||||
if (e.toString().contains('database has been locked') &&
|
|
||||||
_buffer.isNotEmpty) {
|
|
||||||
debugPrint(
|
|
||||||
"TimelineService::loadAssets - Database locked, returning cached assets",
|
|
||||||
);
|
|
||||||
if (hasRange(index, count)) {
|
|
||||||
return getAssets(index, count);
|
|
||||||
}
|
|
||||||
return <BaseAsset>[];
|
|
||||||
}
|
|
||||||
rethrow;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getAssets(index, count);
|
return getAssets(index, count);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import 'dart:async';
|
|||||||
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:drift_flutter/drift_flutter.dart';
|
import 'package:drift_flutter/drift_flutter.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
import 'package:immich_mobile/domain/interfaces/db.interface.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/exif.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.entity.dart';
|
||||||
@@ -18,7 +17,6 @@ import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart';
|
|||||||
import 'package:immich_mobile/infrastructure/entities/stack.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/stack.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.steps.dart';
|
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
|
|
||||||
import 'db.repository.drift.dart';
|
import 'db.repository.drift.dart';
|
||||||
@@ -70,36 +68,10 @@ class Drift extends $Drift implements IDatabaseRepository {
|
|||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get schemaVersion => 2;
|
int get schemaVersion => 1;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
MigrationStrategy get migration => MigrationStrategy(
|
MigrationStrategy get migration => MigrationStrategy(
|
||||||
onUpgrade: (m, from, to) async {
|
|
||||||
// Run migration steps without foreign keys and re-enable them later
|
|
||||||
await customStatement('PRAGMA foreign_keys = OFF');
|
|
||||||
|
|
||||||
await m.runMigrationSteps(
|
|
||||||
from: from,
|
|
||||||
to: to,
|
|
||||||
steps: migrationSteps(
|
|
||||||
from1To2: (m, _) async {
|
|
||||||
for (final entity in allSchemaEntities) {
|
|
||||||
await m.drop(entity);
|
|
||||||
await m.create(entity);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (kDebugMode) {
|
|
||||||
// Fail if the migration broke foreign keys
|
|
||||||
final wrongFKs =
|
|
||||||
await customSelect('PRAGMA foreign_key_check').get();
|
|
||||||
assert(wrongFKs.isEmpty, '${wrongFKs.map((e) => e.data)}');
|
|
||||||
}
|
|
||||||
|
|
||||||
await customStatement('PRAGMA foreign_keys = ON;');
|
|
||||||
},
|
|
||||||
beforeOpen: (details) async {
|
beforeOpen: (details) async {
|
||||||
await customStatement('PRAGMA foreign_keys = ON');
|
await customStatement('PRAGMA foreign_keys = ON');
|
||||||
await customStatement('PRAGMA synchronous = NORMAL');
|
await customStatement('PRAGMA synchronous = NORMAL');
|
||||||
|
|||||||
@@ -1,864 +0,0 @@
|
|||||||
// dart format width=80
|
|
||||||
import 'package:drift/internal/versioned_schema.dart' as i0;
|
|
||||||
import 'package:drift/drift.dart' as i1;
|
|
||||||
import 'dart:typed_data' as i2;
|
|
||||||
import 'package:drift/drift.dart'; // ignore_for_file: type=lint,unused_import
|
|
||||||
|
|
||||||
// GENERATED BY drift_dev, DO NOT MODIFY.
|
|
||||||
final class Schema2 extends i0.VersionedSchema {
|
|
||||||
Schema2({required super.database}) : super(version: 2);
|
|
||||||
@override
|
|
||||||
late final List<i1.DatabaseSchemaEntity> entities = [
|
|
||||||
userEntity,
|
|
||||||
remoteAssetEntity,
|
|
||||||
localAssetEntity,
|
|
||||||
idxLocalAssetChecksum,
|
|
||||||
uQRemoteAssetOwnerChecksum,
|
|
||||||
idxRemoteAssetChecksum,
|
|
||||||
userMetadataEntity,
|
|
||||||
partnerEntity,
|
|
||||||
localAlbumEntity,
|
|
||||||
localAlbumAssetEntity,
|
|
||||||
remoteExifEntity,
|
|
||||||
remoteAlbumEntity,
|
|
||||||
remoteAlbumAssetEntity,
|
|
||||||
remoteAlbumUserEntity,
|
|
||||||
memoryEntity,
|
|
||||||
memoryAssetEntity,
|
|
||||||
stackEntity,
|
|
||||||
];
|
|
||||||
late final Shape0 userEntity = Shape0(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'user_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: [
|
|
||||||
'PRIMARY KEY(id)',
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
_column_0,
|
|
||||||
_column_1,
|
|
||||||
_column_2,
|
|
||||||
_column_3,
|
|
||||||
_column_4,
|
|
||||||
_column_5,
|
|
||||||
_column_6,
|
|
||||||
_column_7,
|
|
||||||
],
|
|
||||||
attachedDatabase: database,
|
|
||||||
),
|
|
||||||
alias: null);
|
|
||||||
late final Shape1 remoteAssetEntity = Shape1(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'remote_asset_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: [
|
|
||||||
'PRIMARY KEY(id)',
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
_column_1,
|
|
||||||
_column_8,
|
|
||||||
_column_9,
|
|
||||||
_column_5,
|
|
||||||
_column_10,
|
|
||||||
_column_11,
|
|
||||||
_column_12,
|
|
||||||
_column_0,
|
|
||||||
_column_13,
|
|
||||||
_column_14,
|
|
||||||
_column_15,
|
|
||||||
_column_16,
|
|
||||||
_column_17,
|
|
||||||
_column_18,
|
|
||||||
_column_19,
|
|
||||||
_column_20,
|
|
||||||
],
|
|
||||||
attachedDatabase: database,
|
|
||||||
),
|
|
||||||
alias: null);
|
|
||||||
late final Shape2 localAssetEntity = Shape2(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'local_asset_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: [
|
|
||||||
'PRIMARY KEY(id)',
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
_column_1,
|
|
||||||
_column_8,
|
|
||||||
_column_9,
|
|
||||||
_column_5,
|
|
||||||
_column_10,
|
|
||||||
_column_11,
|
|
||||||
_column_12,
|
|
||||||
_column_0,
|
|
||||||
_column_21,
|
|
||||||
_column_14,
|
|
||||||
_column_22,
|
|
||||||
],
|
|
||||||
attachedDatabase: database,
|
|
||||||
),
|
|
||||||
alias: null);
|
|
||||||
final i1.Index idxLocalAssetChecksum = i1.Index('idx_local_asset_checksum',
|
|
||||||
'CREATE INDEX idx_local_asset_checksum ON local_asset_entity (checksum)');
|
|
||||||
final i1.Index uQRemoteAssetOwnerChecksum = i1.Index(
|
|
||||||
'UQ_remote_asset_owner_checksum',
|
|
||||||
'CREATE UNIQUE INDEX UQ_remote_asset_owner_checksum ON remote_asset_entity (checksum, owner_id)');
|
|
||||||
final i1.Index idxRemoteAssetChecksum = i1.Index('idx_remote_asset_checksum',
|
|
||||||
'CREATE INDEX idx_remote_asset_checksum ON remote_asset_entity (checksum)');
|
|
||||||
late final Shape3 userMetadataEntity = Shape3(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'user_metadata_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: [
|
|
||||||
'PRIMARY KEY(user_id, "key")',
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
_column_23,
|
|
||||||
_column_24,
|
|
||||||
_column_25,
|
|
||||||
],
|
|
||||||
attachedDatabase: database,
|
|
||||||
),
|
|
||||||
alias: null);
|
|
||||||
late final Shape4 partnerEntity = Shape4(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'partner_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: [
|
|
||||||
'PRIMARY KEY(shared_by_id, shared_with_id)',
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
_column_26,
|
|
||||||
_column_27,
|
|
||||||
_column_28,
|
|
||||||
],
|
|
||||||
attachedDatabase: database,
|
|
||||||
),
|
|
||||||
alias: null);
|
|
||||||
late final Shape5 localAlbumEntity = Shape5(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'local_album_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: [
|
|
||||||
'PRIMARY KEY(id)',
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
_column_0,
|
|
||||||
_column_1,
|
|
||||||
_column_5,
|
|
||||||
_column_29,
|
|
||||||
_column_30,
|
|
||||||
_column_31,
|
|
||||||
],
|
|
||||||
attachedDatabase: database,
|
|
||||||
),
|
|
||||||
alias: null);
|
|
||||||
late final Shape6 localAlbumAssetEntity = Shape6(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'local_album_asset_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: [
|
|
||||||
'PRIMARY KEY(asset_id, album_id)',
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
_column_32,
|
|
||||||
_column_33,
|
|
||||||
],
|
|
||||||
attachedDatabase: database,
|
|
||||||
),
|
|
||||||
alias: null);
|
|
||||||
late final Shape7 remoteExifEntity = Shape7(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'remote_exif_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: [
|
|
||||||
'PRIMARY KEY(asset_id)',
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
_column_34,
|
|
||||||
_column_35,
|
|
||||||
_column_36,
|
|
||||||
_column_37,
|
|
||||||
_column_38,
|
|
||||||
_column_39,
|
|
||||||
_column_11,
|
|
||||||
_column_10,
|
|
||||||
_column_40,
|
|
||||||
_column_41,
|
|
||||||
_column_42,
|
|
||||||
_column_43,
|
|
||||||
_column_44,
|
|
||||||
_column_45,
|
|
||||||
_column_46,
|
|
||||||
_column_47,
|
|
||||||
_column_48,
|
|
||||||
_column_49,
|
|
||||||
_column_50,
|
|
||||||
_column_51,
|
|
||||||
_column_52,
|
|
||||||
_column_53,
|
|
||||||
],
|
|
||||||
attachedDatabase: database,
|
|
||||||
),
|
|
||||||
alias: null);
|
|
||||||
late final Shape8 remoteAlbumEntity = Shape8(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'remote_album_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: [
|
|
||||||
'PRIMARY KEY(id)',
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
_column_0,
|
|
||||||
_column_1,
|
|
||||||
_column_54,
|
|
||||||
_column_9,
|
|
||||||
_column_5,
|
|
||||||
_column_15,
|
|
||||||
_column_55,
|
|
||||||
_column_56,
|
|
||||||
_column_57,
|
|
||||||
],
|
|
||||||
attachedDatabase: database,
|
|
||||||
),
|
|
||||||
alias: null);
|
|
||||||
late final Shape6 remoteAlbumAssetEntity = Shape6(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'remote_album_asset_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: [
|
|
||||||
'PRIMARY KEY(asset_id, album_id)',
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
_column_34,
|
|
||||||
_column_58,
|
|
||||||
],
|
|
||||||
attachedDatabase: database,
|
|
||||||
),
|
|
||||||
alias: null);
|
|
||||||
late final Shape9 remoteAlbumUserEntity = Shape9(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'remote_album_user_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: [
|
|
||||||
'PRIMARY KEY(album_id, user_id)',
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
_column_58,
|
|
||||||
_column_23,
|
|
||||||
_column_59,
|
|
||||||
],
|
|
||||||
attachedDatabase: database,
|
|
||||||
),
|
|
||||||
alias: null);
|
|
||||||
late final Shape10 memoryEntity = Shape10(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'memory_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: [
|
|
||||||
'PRIMARY KEY(id)',
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
_column_0,
|
|
||||||
_column_9,
|
|
||||||
_column_5,
|
|
||||||
_column_18,
|
|
||||||
_column_15,
|
|
||||||
_column_8,
|
|
||||||
_column_60,
|
|
||||||
_column_61,
|
|
||||||
_column_62,
|
|
||||||
_column_63,
|
|
||||||
_column_64,
|
|
||||||
_column_65,
|
|
||||||
],
|
|
||||||
attachedDatabase: database,
|
|
||||||
),
|
|
||||||
alias: null);
|
|
||||||
late final Shape11 memoryAssetEntity = Shape11(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'memory_asset_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: [
|
|
||||||
'PRIMARY KEY(asset_id, memory_id)',
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
_column_34,
|
|
||||||
_column_66,
|
|
||||||
],
|
|
||||||
attachedDatabase: database,
|
|
||||||
),
|
|
||||||
alias: null);
|
|
||||||
late final Shape12 stackEntity = Shape12(
|
|
||||||
source: i0.VersionedTable(
|
|
||||||
entityName: 'stack_entity',
|
|
||||||
withoutRowId: true,
|
|
||||||
isStrict: true,
|
|
||||||
tableConstraints: [
|
|
||||||
'PRIMARY KEY(id)',
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
_column_0,
|
|
||||||
_column_9,
|
|
||||||
_column_5,
|
|
||||||
_column_15,
|
|
||||||
_column_67,
|
|
||||||
],
|
|
||||||
attachedDatabase: database,
|
|
||||||
),
|
|
||||||
alias: null);
|
|
||||||
}
|
|
||||||
|
|
||||||
class Shape0 extends i0.VersionedTable {
|
|
||||||
Shape0({required super.source, required super.alias}) : super.aliased();
|
|
||||||
i1.GeneratedColumn<String> get id =>
|
|
||||||
columnsByName['id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get name =>
|
|
||||||
columnsByName['name']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<bool> get isAdmin =>
|
|
||||||
columnsByName['is_admin']! as i1.GeneratedColumn<bool>;
|
|
||||||
i1.GeneratedColumn<String> get email =>
|
|
||||||
columnsByName['email']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get profileImagePath =>
|
|
||||||
columnsByName['profile_image_path']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<DateTime> get updatedAt =>
|
|
||||||
columnsByName['updated_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<int> get quotaSizeInBytes =>
|
|
||||||
columnsByName['quota_size_in_bytes']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<int> get quotaUsageInBytes =>
|
|
||||||
columnsByName['quota_usage_in_bytes']! as i1.GeneratedColumn<int>;
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.GeneratedColumn<String> _column_0(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('id', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<String> _column_1(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('name', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<bool> _column_2(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<bool>('is_admin', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.bool,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'CHECK ("is_admin" IN (0, 1))'),
|
|
||||||
defaultValue: const CustomExpression('0'));
|
|
||||||
i1.GeneratedColumn<String> _column_3(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('email', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<String> _column_4(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('profile_image_path', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<DateTime> _column_5(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<DateTime>('updated_at', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.dateTime,
|
|
||||||
defaultValue: const CustomExpression('CURRENT_TIMESTAMP'));
|
|
||||||
i1.GeneratedColumn<int> _column_6(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<int>('quota_size_in_bytes', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.int);
|
|
||||||
i1.GeneratedColumn<int> _column_7(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<int>('quota_usage_in_bytes', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0'));
|
|
||||||
|
|
||||||
class Shape1 extends i0.VersionedTable {
|
|
||||||
Shape1({required super.source, required super.alias}) : super.aliased();
|
|
||||||
i1.GeneratedColumn<String> get name =>
|
|
||||||
columnsByName['name']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<int> get type =>
|
|
||||||
columnsByName['type']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<DateTime> get createdAt =>
|
|
||||||
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<DateTime> get updatedAt =>
|
|
||||||
columnsByName['updated_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<int> get width =>
|
|
||||||
columnsByName['width']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<int> get height =>
|
|
||||||
columnsByName['height']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<int> get durationInSeconds =>
|
|
||||||
columnsByName['duration_in_seconds']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<String> get id =>
|
|
||||||
columnsByName['id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get checksum =>
|
|
||||||
columnsByName['checksum']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<bool> get isFavorite =>
|
|
||||||
columnsByName['is_favorite']! as i1.GeneratedColumn<bool>;
|
|
||||||
i1.GeneratedColumn<String> get ownerId =>
|
|
||||||
columnsByName['owner_id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<DateTime> get localDateTime =>
|
|
||||||
columnsByName['local_date_time']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<String> get thumbHash =>
|
|
||||||
columnsByName['thumb_hash']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<DateTime> get deletedAt =>
|
|
||||||
columnsByName['deleted_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<String> get livePhotoVideoId =>
|
|
||||||
columnsByName['live_photo_video_id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<int> get visibility =>
|
|
||||||
columnsByName['visibility']! as i1.GeneratedColumn<int>;
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.GeneratedColumn<int> _column_8(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<int>('type', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.int);
|
|
||||||
i1.GeneratedColumn<DateTime> _column_9(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<DateTime>('created_at', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.dateTime,
|
|
||||||
defaultValue: const CustomExpression('CURRENT_TIMESTAMP'));
|
|
||||||
i1.GeneratedColumn<int> _column_10(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<int>('width', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.int);
|
|
||||||
i1.GeneratedColumn<int> _column_11(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<int>('height', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.int);
|
|
||||||
i1.GeneratedColumn<int> _column_12(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<int>('duration_in_seconds', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.int);
|
|
||||||
i1.GeneratedColumn<String> _column_13(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('checksum', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<bool> _column_14(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<bool>('is_favorite', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.bool,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'CHECK ("is_favorite" IN (0, 1))'),
|
|
||||||
defaultValue: const CustomExpression('0'));
|
|
||||||
i1.GeneratedColumn<String> _column_15(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('owner_id', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
|
||||||
i1.GeneratedColumn<DateTime> _column_16(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<DateTime>('local_date_time', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.dateTime);
|
|
||||||
i1.GeneratedColumn<String> _column_17(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('thumb_hash', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<DateTime> _column_18(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<DateTime>('deleted_at', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.dateTime);
|
|
||||||
i1.GeneratedColumn<String> _column_19(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('live_photo_video_id', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<int> _column_20(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<int>('visibility', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.int);
|
|
||||||
|
|
||||||
class Shape2 extends i0.VersionedTable {
|
|
||||||
Shape2({required super.source, required super.alias}) : super.aliased();
|
|
||||||
i1.GeneratedColumn<String> get name =>
|
|
||||||
columnsByName['name']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<int> get type =>
|
|
||||||
columnsByName['type']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<DateTime> get createdAt =>
|
|
||||||
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<DateTime> get updatedAt =>
|
|
||||||
columnsByName['updated_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<int> get width =>
|
|
||||||
columnsByName['width']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<int> get height =>
|
|
||||||
columnsByName['height']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<int> get durationInSeconds =>
|
|
||||||
columnsByName['duration_in_seconds']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<String> get id =>
|
|
||||||
columnsByName['id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get checksum =>
|
|
||||||
columnsByName['checksum']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<bool> get isFavorite =>
|
|
||||||
columnsByName['is_favorite']! as i1.GeneratedColumn<bool>;
|
|
||||||
i1.GeneratedColumn<int> get orientation =>
|
|
||||||
columnsByName['orientation']! as i1.GeneratedColumn<int>;
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.GeneratedColumn<String> _column_21(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('checksum', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<int> _column_22(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<int>('orientation', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.int, defaultValue: const CustomExpression('0'));
|
|
||||||
|
|
||||||
class Shape3 extends i0.VersionedTable {
|
|
||||||
Shape3({required super.source, required super.alias}) : super.aliased();
|
|
||||||
i1.GeneratedColumn<String> get userId =>
|
|
||||||
columnsByName['user_id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<int> get key =>
|
|
||||||
columnsByName['key']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<i2.Uint8List> get value =>
|
|
||||||
columnsByName['value']! as i1.GeneratedColumn<i2.Uint8List>;
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.GeneratedColumn<String> _column_23(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('user_id', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
|
||||||
i1.GeneratedColumn<int> _column_24(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<int>('key', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.int);
|
|
||||||
i1.GeneratedColumn<i2.Uint8List> _column_25(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<i2.Uint8List>('value', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.blob);
|
|
||||||
|
|
||||||
class Shape4 extends i0.VersionedTable {
|
|
||||||
Shape4({required super.source, required super.alias}) : super.aliased();
|
|
||||||
i1.GeneratedColumn<String> get sharedById =>
|
|
||||||
columnsByName['shared_by_id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get sharedWithId =>
|
|
||||||
columnsByName['shared_with_id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<bool> get inTimeline =>
|
|
||||||
columnsByName['in_timeline']! as i1.GeneratedColumn<bool>;
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.GeneratedColumn<String> _column_26(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('shared_by_id', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
|
||||||
i1.GeneratedColumn<String> _column_27(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('shared_with_id', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES user_entity (id) ON DELETE CASCADE'));
|
|
||||||
i1.GeneratedColumn<bool> _column_28(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<bool>('in_timeline', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.bool,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'CHECK ("in_timeline" IN (0, 1))'),
|
|
||||||
defaultValue: const CustomExpression('0'));
|
|
||||||
|
|
||||||
class Shape5 extends i0.VersionedTable {
|
|
||||||
Shape5({required super.source, required super.alias}) : super.aliased();
|
|
||||||
i1.GeneratedColumn<String> get id =>
|
|
||||||
columnsByName['id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get name =>
|
|
||||||
columnsByName['name']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<DateTime> get updatedAt =>
|
|
||||||
columnsByName['updated_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<int> get backupSelection =>
|
|
||||||
columnsByName['backup_selection']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<bool> get isIosSharedAlbum =>
|
|
||||||
columnsByName['is_ios_shared_album']! as i1.GeneratedColumn<bool>;
|
|
||||||
i1.GeneratedColumn<bool> get marker_ =>
|
|
||||||
columnsByName['marker']! as i1.GeneratedColumn<bool>;
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.GeneratedColumn<int> _column_29(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<int>('backup_selection', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.int);
|
|
||||||
i1.GeneratedColumn<bool> _column_30(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<bool>('is_ios_shared_album', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.bool,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'CHECK ("is_ios_shared_album" IN (0, 1))'),
|
|
||||||
defaultValue: const CustomExpression('0'));
|
|
||||||
i1.GeneratedColumn<bool> _column_31(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<bool>('marker', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.bool,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'CHECK ("marker" IN (0, 1))'));
|
|
||||||
|
|
||||||
class Shape6 extends i0.VersionedTable {
|
|
||||||
Shape6({required super.source, required super.alias}) : super.aliased();
|
|
||||||
i1.GeneratedColumn<String> get assetId =>
|
|
||||||
columnsByName['asset_id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get albumId =>
|
|
||||||
columnsByName['album_id']! as i1.GeneratedColumn<String>;
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.GeneratedColumn<String> _column_32(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('asset_id', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES local_asset_entity (id) ON DELETE CASCADE'));
|
|
||||||
i1.GeneratedColumn<String> _column_33(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('album_id', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES local_album_entity (id) ON DELETE CASCADE'));
|
|
||||||
|
|
||||||
class Shape7 extends i0.VersionedTable {
|
|
||||||
Shape7({required super.source, required super.alias}) : super.aliased();
|
|
||||||
i1.GeneratedColumn<String> get assetId =>
|
|
||||||
columnsByName['asset_id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get city =>
|
|
||||||
columnsByName['city']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get state =>
|
|
||||||
columnsByName['state']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get country =>
|
|
||||||
columnsByName['country']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<DateTime> get dateTimeOriginal =>
|
|
||||||
columnsByName['date_time_original']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<String> get description =>
|
|
||||||
columnsByName['description']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<int> get height =>
|
|
||||||
columnsByName['height']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<int> get width =>
|
|
||||||
columnsByName['width']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<String> get exposureTime =>
|
|
||||||
columnsByName['exposure_time']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<double> get fNumber =>
|
|
||||||
columnsByName['f_number']! as i1.GeneratedColumn<double>;
|
|
||||||
i1.GeneratedColumn<int> get fileSize =>
|
|
||||||
columnsByName['file_size']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<double> get focalLength =>
|
|
||||||
columnsByName['focal_length']! as i1.GeneratedColumn<double>;
|
|
||||||
i1.GeneratedColumn<double> get latitude =>
|
|
||||||
columnsByName['latitude']! as i1.GeneratedColumn<double>;
|
|
||||||
i1.GeneratedColumn<double> get longitude =>
|
|
||||||
columnsByName['longitude']! as i1.GeneratedColumn<double>;
|
|
||||||
i1.GeneratedColumn<int> get iso =>
|
|
||||||
columnsByName['iso']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<String> get make =>
|
|
||||||
columnsByName['make']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get model =>
|
|
||||||
columnsByName['model']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get lens =>
|
|
||||||
columnsByName['lens']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get orientation =>
|
|
||||||
columnsByName['orientation']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get timeZone =>
|
|
||||||
columnsByName['time_zone']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<int> get rating =>
|
|
||||||
columnsByName['rating']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<String> get projectionType =>
|
|
||||||
columnsByName['projection_type']! as i1.GeneratedColumn<String>;
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.GeneratedColumn<String> _column_34(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('asset_id', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES remote_asset_entity (id) ON DELETE CASCADE'));
|
|
||||||
i1.GeneratedColumn<String> _column_35(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('city', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<String> _column_36(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('state', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<String> _column_37(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('country', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<DateTime> _column_38(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<DateTime>('date_time_original', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.dateTime);
|
|
||||||
i1.GeneratedColumn<String> _column_39(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('description', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<String> _column_40(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('exposure_time', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<double> _column_41(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<double>('f_number', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.double);
|
|
||||||
i1.GeneratedColumn<int> _column_42(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<int>('file_size', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.int);
|
|
||||||
i1.GeneratedColumn<double> _column_43(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<double>('focal_length', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.double);
|
|
||||||
i1.GeneratedColumn<double> _column_44(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<double>('latitude', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.double);
|
|
||||||
i1.GeneratedColumn<double> _column_45(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<double>('longitude', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.double);
|
|
||||||
i1.GeneratedColumn<int> _column_46(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<int>('iso', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.int);
|
|
||||||
i1.GeneratedColumn<String> _column_47(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('make', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<String> _column_48(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('model', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<String> _column_49(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('lens', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<String> _column_50(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('orientation', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<String> _column_51(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('time_zone', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<int> _column_52(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<int>('rating', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.int);
|
|
||||||
i1.GeneratedColumn<String> _column_53(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('projection_type', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
|
|
||||||
class Shape8 extends i0.VersionedTable {
|
|
||||||
Shape8({required super.source, required super.alias}) : super.aliased();
|
|
||||||
i1.GeneratedColumn<String> get id =>
|
|
||||||
columnsByName['id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get name =>
|
|
||||||
columnsByName['name']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get description =>
|
|
||||||
columnsByName['description']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<DateTime> get createdAt =>
|
|
||||||
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<DateTime> get updatedAt =>
|
|
||||||
columnsByName['updated_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<String> get ownerId =>
|
|
||||||
columnsByName['owner_id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get thumbnailAssetId =>
|
|
||||||
columnsByName['thumbnail_asset_id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<bool> get isActivityEnabled =>
|
|
||||||
columnsByName['is_activity_enabled']! as i1.GeneratedColumn<bool>;
|
|
||||||
i1.GeneratedColumn<int> get order =>
|
|
||||||
columnsByName['order']! as i1.GeneratedColumn<int>;
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.GeneratedColumn<String> _column_54(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('description', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string,
|
|
||||||
defaultValue: const CustomExpression('\'\''));
|
|
||||||
i1.GeneratedColumn<String> _column_55(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('thumbnail_asset_id', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.string,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES remote_asset_entity (id) ON DELETE SET NULL'));
|
|
||||||
i1.GeneratedColumn<bool> _column_56(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<bool>('is_activity_enabled', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.bool,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'CHECK ("is_activity_enabled" IN (0, 1))'),
|
|
||||||
defaultValue: const CustomExpression('1'));
|
|
||||||
i1.GeneratedColumn<int> _column_57(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<int>('order', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.int);
|
|
||||||
i1.GeneratedColumn<String> _column_58(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('album_id', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES remote_album_entity (id) ON DELETE CASCADE'));
|
|
||||||
|
|
||||||
class Shape9 extends i0.VersionedTable {
|
|
||||||
Shape9({required super.source, required super.alias}) : super.aliased();
|
|
||||||
i1.GeneratedColumn<String> get albumId =>
|
|
||||||
columnsByName['album_id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get userId =>
|
|
||||||
columnsByName['user_id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<int> get role =>
|
|
||||||
columnsByName['role']! as i1.GeneratedColumn<int>;
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.GeneratedColumn<int> _column_59(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<int>('role', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.int);
|
|
||||||
|
|
||||||
class Shape10 extends i0.VersionedTable {
|
|
||||||
Shape10({required super.source, required super.alias}) : super.aliased();
|
|
||||||
i1.GeneratedColumn<String> get id =>
|
|
||||||
columnsByName['id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<DateTime> get createdAt =>
|
|
||||||
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<DateTime> get updatedAt =>
|
|
||||||
columnsByName['updated_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<DateTime> get deletedAt =>
|
|
||||||
columnsByName['deleted_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<String> get ownerId =>
|
|
||||||
columnsByName['owner_id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<int> get type =>
|
|
||||||
columnsByName['type']! as i1.GeneratedColumn<int>;
|
|
||||||
i1.GeneratedColumn<String> get data =>
|
|
||||||
columnsByName['data']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<bool> get isSaved =>
|
|
||||||
columnsByName['is_saved']! as i1.GeneratedColumn<bool>;
|
|
||||||
i1.GeneratedColumn<DateTime> get memoryAt =>
|
|
||||||
columnsByName['memory_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<DateTime> get seenAt =>
|
|
||||||
columnsByName['seen_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<DateTime> get showAt =>
|
|
||||||
columnsByName['show_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<DateTime> get hideAt =>
|
|
||||||
columnsByName['hide_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.GeneratedColumn<String> _column_60(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('data', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string);
|
|
||||||
i1.GeneratedColumn<bool> _column_61(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<bool>('is_saved', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.bool,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'CHECK ("is_saved" IN (0, 1))'),
|
|
||||||
defaultValue: const CustomExpression('0'));
|
|
||||||
i1.GeneratedColumn<DateTime> _column_62(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<DateTime>('memory_at', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.dateTime);
|
|
||||||
i1.GeneratedColumn<DateTime> _column_63(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<DateTime>('seen_at', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.dateTime);
|
|
||||||
i1.GeneratedColumn<DateTime> _column_64(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<DateTime>('show_at', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.dateTime);
|
|
||||||
i1.GeneratedColumn<DateTime> _column_65(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<DateTime>('hide_at', aliasedName, true,
|
|
||||||
type: i1.DriftSqlType.dateTime);
|
|
||||||
|
|
||||||
class Shape11 extends i0.VersionedTable {
|
|
||||||
Shape11({required super.source, required super.alias}) : super.aliased();
|
|
||||||
i1.GeneratedColumn<String> get assetId =>
|
|
||||||
columnsByName['asset_id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get memoryId =>
|
|
||||||
columnsByName['memory_id']! as i1.GeneratedColumn<String>;
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.GeneratedColumn<String> _column_66(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('memory_id', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES memory_entity (id) ON DELETE CASCADE'));
|
|
||||||
|
|
||||||
class Shape12 extends i0.VersionedTable {
|
|
||||||
Shape12({required super.source, required super.alias}) : super.aliased();
|
|
||||||
i1.GeneratedColumn<String> get id =>
|
|
||||||
columnsByName['id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<DateTime> get createdAt =>
|
|
||||||
columnsByName['created_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<DateTime> get updatedAt =>
|
|
||||||
columnsByName['updated_at']! as i1.GeneratedColumn<DateTime>;
|
|
||||||
i1.GeneratedColumn<String> get ownerId =>
|
|
||||||
columnsByName['owner_id']! as i1.GeneratedColumn<String>;
|
|
||||||
i1.GeneratedColumn<String> get primaryAssetId =>
|
|
||||||
columnsByName['primary_asset_id']! as i1.GeneratedColumn<String>;
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.GeneratedColumn<String> _column_67(String aliasedName) =>
|
|
||||||
i1.GeneratedColumn<String>('primary_asset_id', aliasedName, false,
|
|
||||||
type: i1.DriftSqlType.string,
|
|
||||||
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
|
|
||||||
'REFERENCES remote_asset_entity (id)'));
|
|
||||||
i0.MigrationStepWithVersion migrationSteps({
|
|
||||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
|
||||||
}) {
|
|
||||||
return (currentVersion, database) async {
|
|
||||||
switch (currentVersion) {
|
|
||||||
case 1:
|
|
||||||
final schema = Schema2(database: database);
|
|
||||||
final migrator = i1.Migrator(database, schema);
|
|
||||||
await from1To2(migrator, schema);
|
|
||||||
return 2;
|
|
||||||
default:
|
|
||||||
throw ArgumentError.value('Unknown migration from $currentVersion');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
i1.OnUpgrade stepByStep({
|
|
||||||
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
|
|
||||||
}) =>
|
|
||||||
i0.VersionedSchema.stepByStepHelper(
|
|
||||||
step: migrationSteps(
|
|
||||||
from1To2: from1To2,
|
|
||||||
));
|
|
||||||
@@ -326,12 +326,16 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||||||
|
|
||||||
return transaction(() async {
|
return transaction(() async {
|
||||||
if (assetsToUnLink.isNotEmpty) {
|
if (assetsToUnLink.isNotEmpty) {
|
||||||
await _db.batch(
|
await _db.batch((batch) {
|
||||||
(batch) => batch.deleteWhere(
|
for (final assetToUnLink in assetsToUnLink) {
|
||||||
_db.localAlbumAssetEntity,
|
batch.deleteWhere(
|
||||||
(f) => f.assetId.isIn(assetsToUnLink) & f.albumId.equals(albumId),
|
_db.localAlbumAssetEntity,
|
||||||
),
|
(row) =>
|
||||||
);
|
row.assetId.equals(assetToUnLink) &
|
||||||
|
row.albumId.equals(albumId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await _deleteAssets(assetsToDelete);
|
await _deleteAssets(assetsToDelete);
|
||||||
@@ -359,7 +363,9 @@ class DriftLocalAlbumRepository extends DriftDatabaseRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return _db.batch((batch) {
|
return _db.batch((batch) {
|
||||||
batch.deleteWhere(_db.localAssetEntity, (f) => f.id.isIn(ids));
|
for (final id in ids) {
|
||||||
|
batch.deleteWhere(_db.localAssetEntity, (row) => row.id.equals(id));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import 'package:collection/collection.dart';
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart';
|
||||||
@@ -46,13 +45,9 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> delete(List<String> ids) {
|
Future<void> delete(List<String> ids) {
|
||||||
if (ids.isEmpty) {
|
|
||||||
return Future.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _db.batch((batch) {
|
return _db.batch((batch) {
|
||||||
for (final slice in ids.slices(32000)) {
|
for (final id in ids) {
|
||||||
batch.deleteWhere(_db.localAssetEntity, (e) => e.id.isIn(slice));
|
batch.deleteWhere(_db.localAssetEntity, (e) => e.id.equals(id));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,10 +121,15 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<int> removeAssets(String albumId, List<String> assetIds) {
|
Future<void> removeAssets(String albumId, List<String> assetIds) {
|
||||||
return _db.remoteAlbumAssetEntity.deleteWhere(
|
return _db.batch((batch) {
|
||||||
(tbl) => tbl.albumId.equals(albumId) & tbl.assetId.isIn(assetIds),
|
for (final assetId in assetIds) {
|
||||||
);
|
batch.deleteWhere(
|
||||||
|
_db.remoteAlbumAssetEntity,
|
||||||
|
(row) => row.albumId.equals(albumId) & row.assetId.equals(assetId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
FutureOr<(DateTime, DateTime)> getDateRange(String albumId) {
|
FutureOr<(DateTime, DateTime)> getDateRange(String albumId) {
|
||||||
@@ -160,6 +165,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||||||
|
|
||||||
final userIds = albumUserRows.map((row) => row.userId);
|
final userIds = albumUserRows.map((row) => row.userId);
|
||||||
|
|
||||||
|
// TODO: remove this isIn() after removing UserDto
|
||||||
return (_db.select(_db.userEntity)..where((row) => row.id.isIn(userIds)))
|
return (_db.select(_db.userEntity)..where((row) => row.id.isIn(userIds)))
|
||||||
.map(
|
.map(
|
||||||
(user) => UserDto(
|
(user) => UserDto(
|
||||||
|
|||||||
@@ -129,7 +129,14 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> delete(List<String> ids) {
|
Future<void> delete(List<String> ids) {
|
||||||
return _db.remoteAssetEntity.deleteWhere((row) => row.id.isIn(ids));
|
return _db.batch((batch) {
|
||||||
|
for (final id in ids) {
|
||||||
|
batch.deleteWhere(
|
||||||
|
_db.remoteAssetEntity,
|
||||||
|
(row) => row.id.equals(id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateLocation(List<String> ids, LatLng location) {
|
Future<void> updateLocation(List<String> ids, LatLng location) {
|
||||||
|
|||||||
@@ -29,8 +29,14 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
|
|
||||||
Future<void> deleteUsersV1(Iterable<SyncUserDeleteV1> data) async {
|
Future<void> deleteUsersV1(Iterable<SyncUserDeleteV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _db.userEntity
|
await _db.batch((batch) {
|
||||||
.deleteWhere((row) => row.id.isIn(data.map((e) => e.userId)));
|
for (final user in data) {
|
||||||
|
batch.deleteWhere(
|
||||||
|
_db.userEntity,
|
||||||
|
(row) => row.id.equals(user.userId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (error, stack) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: SyncUserDeleteV1', error, stack);
|
_logger.severe('Error: SyncUserDeleteV1', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
@@ -106,9 +112,14 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
String debugLabel = 'user',
|
String debugLabel = 'user',
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
await _db.remoteAssetEntity.deleteWhere(
|
await _db.batch((batch) {
|
||||||
(row) => row.id.isIn(data.map((e) => e.assetId)),
|
for (final asset in data) {
|
||||||
);
|
batch.deleteWhere(
|
||||||
|
_db.remoteAssetEntity,
|
||||||
|
(row) => row.id.equals(asset.assetId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (error, stack) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: deleteAssetsV1 - $debugLabel', error, stack);
|
_logger.severe('Error: deleteAssetsV1 - $debugLabel', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
@@ -202,9 +213,14 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
|
|
||||||
Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data) async {
|
Future<void> deleteAlbumsV1(Iterable<SyncAlbumDeleteV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _db.remoteAlbumEntity.deleteWhere(
|
await _db.batch((batch) {
|
||||||
(row) => row.id.isIn(data.map((e) => e.albumId)),
|
for (final album in data) {
|
||||||
);
|
batch.deleteWhere(
|
||||||
|
_db.remoteAlbumEntity,
|
||||||
|
(row) => row.id.equals(album.albumId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (error, stack) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: deleteAlbumsV1', error, stack);
|
_logger.severe('Error: deleteAlbumsV1', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
@@ -371,9 +387,14 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
|
|
||||||
Future<void> deleteMemoriesV1(Iterable<SyncMemoryDeleteV1> data) async {
|
Future<void> deleteMemoriesV1(Iterable<SyncMemoryDeleteV1> data) async {
|
||||||
try {
|
try {
|
||||||
await _db.memoryEntity.deleteWhere(
|
await _db.batch((batch) {
|
||||||
(row) => row.id.isIn(data.map((e) => e.memoryId)),
|
for (final memory in data) {
|
||||||
);
|
batch.deleteWhere(
|
||||||
|
_db.memoryEntity,
|
||||||
|
(row) => row.id.equals(memory.memoryId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (error, stack) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: deleteMemoriesV1', error, stack);
|
_logger.severe('Error: deleteMemoriesV1', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
@@ -455,9 +476,14 @@ class SyncStreamRepository extends DriftDatabaseRepository {
|
|||||||
String debugLabel = 'user',
|
String debugLabel = 'user',
|
||||||
}) async {
|
}) async {
|
||||||
try {
|
try {
|
||||||
await _db.stackEntity.deleteWhere(
|
await _db.batch((batch) {
|
||||||
(row) => row.id.isIn(data.map((e) => e.stackId)),
|
for (final stack in data) {
|
||||||
);
|
batch.deleteWhere(
|
||||||
|
_db.stackEntity,
|
||||||
|
(row) => row.id.equals(stack.stackId),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (error, stack) {
|
} catch (error, stack) {
|
||||||
_logger.severe('Error: deleteStacksV1 - $debugLabel', error, stack);
|
_logger.severe('Error: deleteStacksV1 - $debugLabel', error, stack);
|
||||||
rethrow;
|
rethrow;
|
||||||
|
|||||||
@@ -254,44 +254,42 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
required int offset,
|
required int offset,
|
||||||
required int count,
|
required int count,
|
||||||
}) async {
|
}) async {
|
||||||
return await transaction(() async {
|
final albumData = await (_db.remoteAlbumEntity.select()
|
||||||
final albumData = await (_db.remoteAlbumEntity.select()
|
..where((row) => row.id.equals(albumId)))
|
||||||
..where((row) => row.id.equals(albumId)))
|
.getSingleOrNull();
|
||||||
.getSingleOrNull();
|
|
||||||
|
|
||||||
// If album doesn't exist (was deleted), return empty list
|
// If album doesn't exist (was deleted), return empty list
|
||||||
if (albumData == null) {
|
if (albumData == null) {
|
||||||
return <BaseAsset>[];
|
return <BaseAsset>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
final isAscending = albumData.order == AlbumAssetOrder.asc;
|
final isAscending = albumData.order == AlbumAssetOrder.asc;
|
||||||
|
|
||||||
final query = _db.remoteAssetEntity.select().join(
|
final query = _db.remoteAssetEntity.select().join(
|
||||||
[
|
[
|
||||||
innerJoin(
|
innerJoin(
|
||||||
_db.remoteAlbumAssetEntity,
|
_db.remoteAlbumAssetEntity,
|
||||||
_db.remoteAlbumAssetEntity.assetId
|
_db.remoteAlbumAssetEntity.assetId
|
||||||
.equalsExp(_db.remoteAssetEntity.id),
|
.equalsExp(_db.remoteAssetEntity.id),
|
||||||
useColumns: false,
|
useColumns: false,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)..where(
|
)..where(
|
||||||
_db.remoteAssetEntity.deletedAt.isNull() &
|
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||||
_db.remoteAlbumAssetEntity.albumId.equals(albumId),
|
_db.remoteAlbumAssetEntity.albumId.equals(albumId),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isAscending) {
|
if (isAscending) {
|
||||||
query.orderBy([OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]);
|
query.orderBy([OrderingTerm.asc(_db.remoteAssetEntity.createdAt)]);
|
||||||
} else {
|
} else {
|
||||||
query.orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]);
|
query.orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
query.limit(count, offset: offset);
|
query.limit(count, offset: offset);
|
||||||
|
|
||||||
return query
|
return query
|
||||||
.map((row) => row.readTable(_db.remoteAssetEntity).toDto())
|
.map((row) => row.readTable(_db.remoteAssetEntity).toDto())
|
||||||
.get();
|
.get();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineQuery remote(String ownerId, GroupAssetsBy groupBy) =>
|
TimelineQuery remote(String ownerId, GroupAssetsBy groupBy) =>
|
||||||
@@ -464,15 +462,13 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
required Expression<bool> Function($RemoteAssetEntityTable row) filter,
|
required Expression<bool> Function($RemoteAssetEntityTable row) filter,
|
||||||
required int offset,
|
required int offset,
|
||||||
required int count,
|
required int count,
|
||||||
}) async {
|
}) {
|
||||||
return await transaction(() async {
|
final query = _db.remoteAssetEntity.select()
|
||||||
final query = _db.remoteAssetEntity.select()
|
..where(filter)
|
||||||
..where(filter)
|
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
|
||||||
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
|
..limit(count, offset: offset);
|
||||||
..limit(count, offset: offset);
|
|
||||||
|
|
||||||
return query.map((row) => row.toDto()).get();
|
return query.map((row) => row.toDto()).get();
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|
||||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/asset.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
|
||||||
import 'package:immich_mobile/utils/migration.dart';
|
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
|
||||||
|
|
||||||
@RoutePage()
|
|
||||||
class ChangeExperiencePage extends ConsumerStatefulWidget {
|
|
||||||
final bool switchingToBeta;
|
|
||||||
|
|
||||||
const ChangeExperiencePage({super.key, required this.switchingToBeta});
|
|
||||||
|
|
||||||
@override
|
|
||||||
ConsumerState createState() => _ChangeExperiencePageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _ChangeExperiencePageState extends ConsumerState<ChangeExperiencePage> {
|
|
||||||
bool hasMigrated = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => _handleMigration());
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _handleMigration() async {
|
|
||||||
if (widget.switchingToBeta) {
|
|
||||||
final assetNotifier = ref.read(assetProvider.notifier);
|
|
||||||
if (assetNotifier.mounted) {
|
|
||||||
assetNotifier.dispose();
|
|
||||||
}
|
|
||||||
final albumNotifier = ref.read(albumProvider.notifier);
|
|
||||||
if (albumNotifier.mounted) {
|
|
||||||
albumNotifier.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
final permission = await ref
|
|
||||||
.read(galleryPermissionNotifier.notifier)
|
|
||||||
.requestGalleryPermission();
|
|
||||||
|
|
||||||
if (permission.isGranted) {
|
|
||||||
await ref.read(backgroundSyncProvider).syncLocal(full: true);
|
|
||||||
await migrateDeviceAssetToSqlite(
|
|
||||||
ref.read(isarProvider),
|
|
||||||
ref.read(driftProvider),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
await ref.read(backgroundSyncProvider).cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future.delayed(const Duration(seconds: 3), () {
|
|
||||||
context.replaceRoute(
|
|
||||||
widget.switchingToBeta
|
|
||||||
? const TabShellRoute()
|
|
||||||
: const TabControllerRoute(),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (mounted) {
|
|
||||||
setState(() {
|
|
||||||
HapticFeedback.heavyImpact();
|
|
||||||
hasMigrated = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
body: Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
AnimatedSwitcher(
|
|
||||||
duration: Durations.long4,
|
|
||||||
child: hasMigrated
|
|
||||||
? const Icon(
|
|
||||||
Icons.check_circle_rounded,
|
|
||||||
color: Colors.green,
|
|
||||||
size: 48.0,
|
|
||||||
)
|
|
||||||
: const SizedBox(
|
|
||||||
width: 50.0,
|
|
||||||
height: 50.0,
|
|
||||||
child: CircularProgressIndicator(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16.0),
|
|
||||||
Center(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
SizedBox(
|
|
||||||
width: 300.0,
|
|
||||||
child: AnimatedSwitcher(
|
|
||||||
duration: Durations.long4,
|
|
||||||
child: hasMigrated
|
|
||||||
? Text(
|
|
||||||
"Migration success. Navigating to the new timeline...",
|
|
||||||
style: context.textTheme.titleMedium,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
)
|
|
||||||
: Text(
|
|
||||||
"Data migration in progress...\nPlease wait and don't close this page",
|
|
||||||
style: context.textTheme.titleMedium,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,6 @@ import 'package:immich_mobile/widgets/settings/advanced_settings.dart';
|
|||||||
import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_settings.dart';
|
import 'package:immich_mobile/widgets/settings/asset_list_settings/asset_list_settings.dart';
|
||||||
import 'package:immich_mobile/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart';
|
import 'package:immich_mobile/widgets/settings/asset_viewer_settings/asset_viewer_settings.dart';
|
||||||
import 'package:immich_mobile/widgets/settings/backup_settings/backup_settings.dart';
|
import 'package:immich_mobile/widgets/settings/backup_settings/backup_settings.dart';
|
||||||
import 'package:immich_mobile/widgets/settings/beta_timeline_list_tile.dart';
|
|
||||||
import 'package:immich_mobile/widgets/settings/language_settings.dart';
|
import 'package:immich_mobile/widgets/settings/language_settings.dart';
|
||||||
import 'package:immich_mobile/widgets/settings/networking_settings/networking_settings.dart';
|
import 'package:immich_mobile/widgets/settings/networking_settings/networking_settings.dart';
|
||||||
import 'package:immich_mobile/widgets/settings/notification_setting.dart';
|
import 'package:immich_mobile/widgets/settings/notification_setting.dart';
|
||||||
@@ -95,59 +94,55 @@ class _MobileLayout extends StatelessWidget {
|
|||||||
const _MobileLayout();
|
const _MobileLayout();
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<Widget> settings = SettingSection.values
|
|
||||||
.map(
|
|
||||||
(setting) => Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16.0,
|
|
||||||
),
|
|
||||||
child: Card(
|
|
||||||
elevation: 0,
|
|
||||||
clipBehavior: Clip.antiAlias,
|
|
||||||
color: context.colorScheme.surfaceContainer,
|
|
||||||
shape: const RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
|
||||||
),
|
|
||||||
margin: const EdgeInsets.symmetric(vertical: 4.0),
|
|
||||||
child: ListTile(
|
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 16.0,
|
|
||||||
),
|
|
||||||
leading: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
|
||||||
color: context.isDarkTheme
|
|
||||||
? Colors.black26
|
|
||||||
: Colors.white.withAlpha(100),
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: Icon(setting.icon, color: context.primaryColor),
|
|
||||||
),
|
|
||||||
title: Text(
|
|
||||||
setting.title,
|
|
||||||
style: context.textTheme.titleMedium!.copyWith(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: context.primaryColor,
|
|
||||||
),
|
|
||||||
).tr(),
|
|
||||||
subtitle: Text(
|
|
||||||
setting.subtitle,
|
|
||||||
style: context.textTheme.labelLarge,
|
|
||||||
).tr(),
|
|
||||||
onTap: () =>
|
|
||||||
context.pushRoute(SettingsSubRoute(section: setting)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.toList();
|
|
||||||
return ListView(
|
return ListView(
|
||||||
physics: const ClampingScrollPhysics(),
|
physics: const ClampingScrollPhysics(),
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
children: [
|
children: SettingSection.values
|
||||||
const BetaTimelineListTile(),
|
.map(
|
||||||
...settings,
|
(setting) => Padding(
|
||||||
],
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16.0,
|
||||||
|
),
|
||||||
|
child: Card(
|
||||||
|
elevation: 0,
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
color: context.colorScheme.surfaceContainer,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||||
|
),
|
||||||
|
margin: const EdgeInsets.symmetric(vertical: 4.0),
|
||||||
|
child: ListTile(
|
||||||
|
contentPadding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16.0,
|
||||||
|
),
|
||||||
|
leading: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(16)),
|
||||||
|
color: context.isDarkTheme
|
||||||
|
? Colors.black26
|
||||||
|
: Colors.white.withAlpha(100),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Icon(setting.icon, color: context.primaryColor),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
setting.title,
|
||||||
|
style: context.textTheme.titleMedium!.copyWith(
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: context.primaryColor,
|
||||||
|
),
|
||||||
|
).tr(),
|
||||||
|
subtitle: Text(
|
||||||
|
setting.subtitle,
|
||||||
|
style: context.textTheme.labelLarge,
|
||||||
|
).tr(),
|
||||||
|
onTap: () =>
|
||||||
|
context.pushRoute(SettingsSubRoute(section: setting)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,15 +73,7 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (context.router.current.name == SplashScreenRoute.name) {
|
if (context.router.current.name == SplashScreenRoute.name) {
|
||||||
context.replaceRoute(
|
context.replaceRoute(const TabControllerRoute());
|
||||||
Store.isBetaTimelineEnabled
|
|
||||||
? const TabShellRoute()
|
|
||||||
: const TabControllerRoute(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Store.isBetaTimelineEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final hasPermission =
|
final hasPermission =
|
||||||
|
|||||||
@@ -10,28 +10,13 @@ import 'package:immich_mobile/providers/search/search_input_focus.provider.dart'
|
|||||||
import 'package:immich_mobile/providers/tab.provider.dart';
|
import 'package:immich_mobile/providers/tab.provider.dart';
|
||||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/utils/migration.dart';
|
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class TabShellPage extends ConsumerStatefulWidget {
|
class TabShellPage extends ConsumerWidget {
|
||||||
const TabShellPage({super.key});
|
const TabShellPage({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ConsumerState<TabShellPage> createState() => _TabShellPageState();
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
}
|
|
||||||
|
|
||||||
class _TabShellPageState extends ConsumerState<TabShellPage> {
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
||||||
runNewSync(ref, full: true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final isScreenLandscape = context.orientation == Orientation.landscape;
|
final isScreenLandscape = context.orientation == Orientation.landscape;
|
||||||
|
|
||||||
Widget buildIcon({required Widget icon, required bool isProcessing}) {
|
Widget buildIcon({required Widget icon, required bool isProcessing}) {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
|
import 'package:immich_mobile/models/upload/share_intent_attachment.model.dart';
|
||||||
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
import 'package:immich_mobile/pages/common/large_leading_tile.dart';
|
||||||
@@ -76,9 +75,7 @@ class ShareIntentPage extends HookConsumerWidget {
|
|||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
context.navigateTo(
|
context.navigateTo(
|
||||||
Store.isBetaTimelineEnabled
|
const TabControllerRoute(),
|
||||||
? const TabShellRoute()
|
|
||||||
: const TabControllerRoute(),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.arrow_back),
|
icon: const Icon(Icons.arrow_back),
|
||||||
|
|||||||
@@ -3,12 +3,10 @@ import 'dart:async';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/services/log.service.dart';
|
import 'package:immich_mobile/domain/services/log.service.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
|
||||||
import 'package:immich_mobile/models/backup/backup_state.model.dart';
|
import 'package:immich_mobile/models/backup/backup_state.model.dart';
|
||||||
import 'package:immich_mobile/providers/album/album.provider.dart';
|
import 'package:immich_mobile/providers/album/album.provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset.provider.dart';
|
import 'package:immich_mobile/providers/asset.provider.dart';
|
||||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/ios_background_settings.provider.dart';
|
import 'package:immich_mobile/providers/backup/ios_background_settings.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/manual_upload.provider.dart';
|
import 'package:immich_mobile/providers/backup/manual_upload.provider.dart';
|
||||||
@@ -20,7 +18,6 @@ import 'package:immich_mobile/providers/tab.provider.dart';
|
|||||||
import 'package:immich_mobile/providers/websocket.provider.dart';
|
import 'package:immich_mobile/providers/websocket.provider.dart';
|
||||||
import 'package:immich_mobile/services/background.service.dart';
|
import 'package:immich_mobile/services/background.service.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
enum AppLifeCycleEnum {
|
enum AppLifeCycleEnum {
|
||||||
@@ -60,59 +57,29 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
|||||||
debugPrint("Using server URL: $endpoint");
|
debugPrint("Using server URL: $endpoint");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Store.isBetaTimelineEnabled) {
|
final permission = _ref.watch(galleryPermissionNotifier);
|
||||||
final permission = _ref.watch(galleryPermissionNotifier);
|
if (permission.isGranted || permission.isLimited) {
|
||||||
if (permission.isGranted || permission.isLimited) {
|
await _ref.read(backupProvider.notifier).resumeBackup();
|
||||||
await _ref.read(backupProvider.notifier).resumeBackup();
|
await _ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
|
||||||
await _ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await _ref.read(serverInfoProvider.notifier).getServerVersion();
|
await _ref.read(serverInfoProvider.notifier).getServerVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Store.isBetaTimelineEnabled) {
|
switch (_ref.read(tabProvider)) {
|
||||||
switch (_ref.read(tabProvider)) {
|
case TabEnum.home:
|
||||||
case TabEnum.home:
|
await _ref.read(assetProvider.notifier).getAllAsset();
|
||||||
await _ref.read(assetProvider.notifier).getAllAsset();
|
break;
|
||||||
break;
|
case TabEnum.search:
|
||||||
case TabEnum.search:
|
// nothing to do
|
||||||
// nothing to do
|
break;
|
||||||
break;
|
|
||||||
|
|
||||||
case TabEnum.albums:
|
case TabEnum.albums:
|
||||||
await _ref.read(albumProvider.notifier).refreshRemoteAlbums();
|
await _ref.read(albumProvider.notifier).refreshRemoteAlbums();
|
||||||
break;
|
break;
|
||||||
case TabEnum.library:
|
case TabEnum.library:
|
||||||
// nothing to do
|
// nothing to do
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_ref.read(backupProvider.notifier).cancelBackup();
|
|
||||||
|
|
||||||
final backgroundManager = _ref.read(backgroundSyncProvider);
|
|
||||||
// Ensure proper cleanup before starting new background tasks
|
|
||||||
try {
|
|
||||||
await Future.wait([
|
|
||||||
backgroundManager.syncLocal().then(
|
|
||||||
(_) {
|
|
||||||
Logger("AppLifeCycleNotifier")
|
|
||||||
.fine("Hashing assets after syncLocal");
|
|
||||||
// Check if app is still active before hashing
|
|
||||||
if (state == AppLifeCycleEnum.resumed) {
|
|
||||||
backgroundManager.hashAssets();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
backgroundManager.syncRemote(),
|
|
||||||
]);
|
|
||||||
} catch (e, stackTrace) {
|
|
||||||
Logger("AppLifeCycleNotifier").severe(
|
|
||||||
"Error during background sync",
|
|
||||||
e,
|
|
||||||
stackTrace,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ref.read(websocketProvider.notifier).connect();
|
_ref.read(websocketProvider.notifier).connect();
|
||||||
@@ -125,11 +92,9 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
|||||||
.read(galleryPermissionNotifier.notifier)
|
.read(galleryPermissionNotifier.notifier)
|
||||||
.getGalleryPermissionStatus();
|
.getGalleryPermissionStatus();
|
||||||
|
|
||||||
if (!Store.isBetaTimelineEnabled) {
|
await _ref.read(iOSBackgroundSettingsProvider.notifier).refresh();
|
||||||
await _ref.read(iOSBackgroundSettingsProvider.notifier).refresh();
|
|
||||||
|
|
||||||
_ref.invalidate(memoryFutureProvider);
|
_ref.invalidate(memoryFutureProvider);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleAppInactivity() {
|
void handleAppInactivity() {
|
||||||
@@ -141,8 +106,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
|||||||
state = AppLifeCycleEnum.paused;
|
state = AppLifeCycleEnum.paused;
|
||||||
_wasPaused = true;
|
_wasPaused = true;
|
||||||
|
|
||||||
if (!Store.isBetaTimelineEnabled &&
|
if (_ref.read(authProvider).isAuthenticated) {
|
||||||
_ref.read(authProvider).isAuthenticated) {
|
|
||||||
// Do not cancel backup if manual upload is in progress
|
// Do not cancel backup if manual upload is in progress
|
||||||
if (_ref.read(backupProvider.notifier).backupProgress !=
|
if (_ref.read(backupProvider.notifier).backupProgress !=
|
||||||
BackUpProgressEnum.manualInProgress) {
|
BackUpProgressEnum.manualInProgress) {
|
||||||
@@ -151,43 +115,15 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
|
|||||||
_ref.read(websocketProvider.notifier).disconnect();
|
_ref.read(websocketProvider.notifier).disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
LogService.I.flush();
|
||||||
LogService.I.flush();
|
|
||||||
} catch (e) {
|
|
||||||
// Ignore flush errors during pause
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> handleAppDetached() async {
|
Future<void> handleAppDetached() async {
|
||||||
state = AppLifeCycleEnum.detached;
|
state = AppLifeCycleEnum.detached;
|
||||||
|
LogService.I.flush();
|
||||||
// Flush logs before closing database
|
await Isar.getInstance()?.close();
|
||||||
try {
|
|
||||||
LogService.I.flush();
|
|
||||||
} catch (e) {
|
|
||||||
// Ignore flush errors during shutdown
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close Isar database safely
|
|
||||||
try {
|
|
||||||
final isar = Isar.getInstance();
|
|
||||||
if (isar != null && isar.isOpen) {
|
|
||||||
await isar.close();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Ignore close errors during shutdown
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Store.isBetaTimelineEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// no guarantee this is called at all
|
// no guarantee this is called at all
|
||||||
try {
|
_ref.read(manualUploadProvider.notifier).cancelBackup();
|
||||||
_ref.read(manualUploadProvider.notifier).cancelBackup();
|
|
||||||
} catch (e) {
|
|
||||||
// Ignore errors during shutdown
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleAppHidden() {
|
void handleAppHidden() {
|
||||||
|
|||||||
@@ -13,6 +13,5 @@ Isar isar(Ref ref) => throw UnimplementedError('isar');
|
|||||||
final driftProvider = Provider<Drift>((ref) {
|
final driftProvider = Provider<Drift>((ref) {
|
||||||
final drift = Drift();
|
final drift = Drift();
|
||||||
ref.onDispose(() => unawaited(drift.close()));
|
ref.onDispose(() => unawaited(drift.close()));
|
||||||
ref.keepAlive();
|
|
||||||
return drift;
|
return drift;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,25 +24,7 @@ class AuthRepository extends DatabaseRepository {
|
|||||||
|
|
||||||
const AuthRepository(super.db, this._drift);
|
const AuthRepository(super.db, this._drift);
|
||||||
|
|
||||||
Future<void> clearLocalData() async {
|
Future<void> clearLocalData() {
|
||||||
// Drift deletions - child entities first (those with foreign keys)
|
|
||||||
await Future.wait([
|
|
||||||
_drift.memoryAssetEntity.deleteAll(),
|
|
||||||
_drift.remoteAlbumAssetEntity.deleteAll(),
|
|
||||||
_drift.remoteAlbumUserEntity.deleteAll(),
|
|
||||||
_drift.remoteExifEntity.deleteAll(),
|
|
||||||
_drift.userMetadataEntity.deleteAll(),
|
|
||||||
_drift.partnerEntity.deleteAll(),
|
|
||||||
_drift.stackEntity.deleteAll(),
|
|
||||||
]);
|
|
||||||
// Drift deletions - parent entities
|
|
||||||
await Future.wait([
|
|
||||||
_drift.memoryEntity.deleteAll(),
|
|
||||||
_drift.remoteAlbumEntity.deleteAll(),
|
|
||||||
_drift.remoteAssetEntity.deleteAll(),
|
|
||||||
_drift.userEntity.deleteAll(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
return db.writeTxn(() {
|
return db.writeTxn(() {
|
||||||
return Future.wait([
|
return Future.wait([
|
||||||
db.assets.clear(),
|
db.assets.clear(),
|
||||||
@@ -50,6 +32,17 @@ class AuthRepository extends DatabaseRepository {
|
|||||||
db.albums.clear(),
|
db.albums.clear(),
|
||||||
db.eTags.clear(),
|
db.eTags.clear(),
|
||||||
db.users.clear(),
|
db.users.clear(),
|
||||||
|
_drift.remoteAssetEntity.deleteAll(),
|
||||||
|
_drift.remoteExifEntity.deleteAll(),
|
||||||
|
_drift.userEntity.deleteAll(),
|
||||||
|
_drift.userMetadataEntity.deleteAll(),
|
||||||
|
_drift.partnerEntity.deleteAll(),
|
||||||
|
_drift.remoteAlbumEntity.deleteAll(),
|
||||||
|
_drift.remoteAlbumAssetEntity.deleteAll(),
|
||||||
|
_drift.remoteAlbumUserEntity.deleteAll(),
|
||||||
|
_drift.memoryEntity.deleteAll(),
|
||||||
|
_drift.memoryAssetEntity.deleteAll(),
|
||||||
|
_drift.stackEntity.deleteAll(),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ import 'package:immich_mobile/pages/backup/failed_backup_status.page.dart';
|
|||||||
import 'package:immich_mobile/pages/common/activities.page.dart';
|
import 'package:immich_mobile/pages/common/activities.page.dart';
|
||||||
import 'package:immich_mobile/pages/common/app_log.page.dart';
|
import 'package:immich_mobile/pages/common/app_log.page.dart';
|
||||||
import 'package:immich_mobile/pages/common/app_log_detail.page.dart';
|
import 'package:immich_mobile/pages/common/app_log_detail.page.dart';
|
||||||
import 'package:immich_mobile/pages/common/change_experience.page.dart';
|
|
||||||
import 'package:immich_mobile/pages/common/create_album.page.dart';
|
import 'package:immich_mobile/pages/common/create_album.page.dart';
|
||||||
import 'package:immich_mobile/pages/common/gallery_viewer.page.dart';
|
import 'package:immich_mobile/pages/common/gallery_viewer.page.dart';
|
||||||
import 'package:immich_mobile/pages/common/headers_settings.page.dart';
|
import 'package:immich_mobile/pages/common/headers_settings.page.dart';
|
||||||
@@ -70,27 +69,27 @@ import 'package:immich_mobile/pages/search/person_result.page.dart';
|
|||||||
import 'package:immich_mobile/pages/search/recently_taken.page.dart';
|
import 'package:immich_mobile/pages/search/recently_taken.page.dart';
|
||||||
import 'package:immich_mobile/pages/search/search.page.dart';
|
import 'package:immich_mobile/pages/search/search.page.dart';
|
||||||
import 'package:immich_mobile/pages/share_intent/share_intent.page.dart';
|
import 'package:immich_mobile/pages/share_intent/share_intent.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/feat_in_development.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/dev/media_stat.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/drift_album.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/drift_archive.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/drift_asset_selection_timeline.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/drift_create_album.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/drift_favorite.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_favorite.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_library.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/drift_local_album.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/drift_locked_folder.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/drift_memory.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/drift_partner_detail.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_partner_detail.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_local_album.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_place.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_place.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_place_detail.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_place_detail.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_recently_taken.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_recently_taken.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_user_selection.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_user_selection.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_remote_album.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/drift_trash.page.dart';
|
|
||||||
import 'package:immich_mobile/presentation/pages/drift_video.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_video.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_trash.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_archive.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_locked_folder.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/dev/feat_in_development.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/local_timeline.page.dart';
|
import 'package:immich_mobile/presentation/pages/local_timeline.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/dev/main_timeline.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/dev/media_stat.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_remote_album.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_album.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_library.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_asset_selection_timeline.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_create_album.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_memory.page.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart';
|
||||||
import 'package:immich_mobile/providers/api.provider.dart';
|
import 'package:immich_mobile/providers/api.provider.dart';
|
||||||
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
||||||
@@ -104,6 +103,7 @@ import 'package:immich_mobile/services/api.service.dart';
|
|||||||
import 'package:immich_mobile/services/local_auth.service.dart';
|
import 'package:immich_mobile/services/local_auth.service.dart';
|
||||||
import 'package:immich_mobile/services/secure_storage.service.dart';
|
import 'package:immich_mobile/services/secure_storage.service.dart';
|
||||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||||
|
|
||||||
import 'package:maplibre_gl/maplibre_gl.dart';
|
import 'package:maplibre_gl/maplibre_gl.dart';
|
||||||
|
|
||||||
part 'router.gr.dart';
|
part 'router.gr.dart';
|
||||||
@@ -469,10 +469,6 @@ class AppRouter extends RootStackRouter {
|
|||||||
guards: [_authGuard, _duplicateGuard],
|
guards: [_authGuard, _duplicateGuard],
|
||||||
),
|
),
|
||||||
|
|
||||||
AutoRoute(
|
|
||||||
page: ChangeExperienceRoute.page,
|
|
||||||
guards: [_authGuard, _duplicateGuard],
|
|
||||||
),
|
|
||||||
// required to handle all deeplinks in deep_link.service.dart
|
// required to handle all deeplinks in deep_link.service.dart
|
||||||
// auto_route_library#1722
|
// auto_route_library#1722
|
||||||
RedirectRoute(path: '*', redirectTo: '/'),
|
RedirectRoute(path: '*', redirectTo: '/'),
|
||||||
|
|||||||
@@ -503,49 +503,6 @@ class BackupOptionsRoute extends PageRouteInfo<void> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// generated route for
|
|
||||||
/// [ChangeExperiencePage]
|
|
||||||
class ChangeExperienceRoute extends PageRouteInfo<ChangeExperienceRouteArgs> {
|
|
||||||
ChangeExperienceRoute({
|
|
||||||
Key? key,
|
|
||||||
required bool switchingToBeta,
|
|
||||||
List<PageRouteInfo>? children,
|
|
||||||
}) : super(
|
|
||||||
ChangeExperienceRoute.name,
|
|
||||||
args: ChangeExperienceRouteArgs(
|
|
||||||
key: key,
|
|
||||||
switchingToBeta: switchingToBeta,
|
|
||||||
),
|
|
||||||
initialChildren: children,
|
|
||||||
);
|
|
||||||
|
|
||||||
static const String name = 'ChangeExperienceRoute';
|
|
||||||
|
|
||||||
static PageInfo page = PageInfo(
|
|
||||||
name,
|
|
||||||
builder: (data) {
|
|
||||||
final args = data.argsAs<ChangeExperienceRouteArgs>();
|
|
||||||
return ChangeExperiencePage(
|
|
||||||
key: args.key,
|
|
||||||
switchingToBeta: args.switchingToBeta,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class ChangeExperienceRouteArgs {
|
|
||||||
const ChangeExperienceRouteArgs({this.key, required this.switchingToBeta});
|
|
||||||
|
|
||||||
final Key? key;
|
|
||||||
|
|
||||||
final bool switchingToBeta;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return 'ChangeExperienceRouteArgs{key: $key, switchingToBeta: $switchingToBeta}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [ChangePasswordPage]
|
/// [ChangePasswordPage]
|
||||||
class ChangePasswordRoute extends PageRouteInfo<void> {
|
class ChangePasswordRoute extends PageRouteInfo<void> {
|
||||||
|
|||||||
@@ -155,14 +155,12 @@ class ActionService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<int> removeFromAlbum(List<String> remoteIds, String albumId) async {
|
Future<int> removeFromAlbum(List<String> remoteIds, String albumId) async {
|
||||||
int removedCount = 0;
|
|
||||||
final result = await _albumApiRepository.removeAssets(albumId, remoteIds);
|
final result = await _albumApiRepository.removeAssets(albumId, remoteIds);
|
||||||
|
|
||||||
if (result.removed.isNotEmpty) {
|
if (result.removed.isNotEmpty) {
|
||||||
removedCount =
|
await _remoteAlbumRepository.removeAssets(albumId, result.removed);
|
||||||
await _remoteAlbumRepository.removeAssets(albumId, result.removed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return removedCount;
|
return result.removed.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,6 @@ enum AppSettingsEnum<T> {
|
|||||||
null,
|
null,
|
||||||
true,
|
true,
|
||||||
),
|
),
|
||||||
betaTimeline<bool>(StoreKey.betaTimeline, null, false),
|
|
||||||
;
|
;
|
||||||
|
|
||||||
const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue);
|
const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/services/log.service.dart';
|
import 'package:immich_mobile/domain/services/log.service.dart';
|
||||||
@@ -60,18 +59,9 @@ Cancelable<T?> runInIsolateGentle<T>({
|
|||||||
stack,
|
stack,
|
||||||
);
|
);
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
await LogService.I.flushBuffer();
|
||||||
await LogService.I.flushBuffer();
|
ref.read(driftProvider).close();
|
||||||
await ref.read(driftProvider).close();
|
ref.read(isarProvider).close();
|
||||||
await ref.read(isarProvider).close();
|
|
||||||
ref.dispose();
|
|
||||||
} catch (error) {
|
|
||||||
debugPrint("Error closing resources in isolate: $error");
|
|
||||||
} finally {
|
|
||||||
ref.dispose();
|
|
||||||
// Delay to ensure all resources are released
|
|
||||||
await Future.delayed(const Duration(seconds: 2));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:immich_mobile/domain/models/store.model.dart';
|
import 'package:immich_mobile/domain/models/store.model.dart';
|
||||||
import 'package:immich_mobile/domain/utils/background_sync.dart';
|
import 'package:immich_mobile/domain/utils/background_sync.dart';
|
||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
@@ -19,15 +18,12 @@ import 'package:immich_mobile/infrastructure/entities/local_asset.entity.drift.d
|
|||||||
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
import 'package:immich_mobile/infrastructure/entities/user.entity.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
||||||
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
|
||||||
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
|
||||||
import 'package:immich_mobile/utils/diff.dart';
|
import 'package:immich_mobile/utils/diff.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:logging/logging.dart';
|
|
||||||
// ignore: import_rule_photo_manager
|
// ignore: import_rule_photo_manager
|
||||||
import 'package:photo_manager/photo_manager.dart';
|
import 'package:photo_manager/photo_manager.dart';
|
||||||
|
|
||||||
const int targetVersion = 14;
|
const int targetVersion = 13;
|
||||||
|
|
||||||
Future<void> migrateDatabaseIfNeeded(Isar db) async {
|
Future<void> migrateDatabaseIfNeeded(Isar db) async {
|
||||||
final int version = Store.get(StoreKey.version, targetVersion);
|
final int version = Store.get(StoreKey.version, targetVersion);
|
||||||
@@ -52,22 +48,18 @@ Future<void> migrateDatabaseIfNeeded(Isar db) async {
|
|||||||
await _migrateDeviceAsset(db);
|
await _migrateDeviceAsset(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version < 13) {
|
if (version < 12 && (!kReleaseMode)) {
|
||||||
await Store.put(StoreKey.photoManagerCustomFilter, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (version < 14) {
|
|
||||||
if (!Store.isBetaTimelineEnabled) {
|
|
||||||
// Try again when beta timeline is enabled and the app is restarted
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final backgroundSync = BackgroundSyncManager();
|
final backgroundSync = BackgroundSyncManager();
|
||||||
await backgroundSync.syncLocal();
|
await backgroundSync.syncLocal();
|
||||||
final drift = Drift();
|
final drift = Drift();
|
||||||
await migrateDeviceAssetToSqlite(db, drift);
|
await _migrateDeviceAssetToSqlite(db, drift);
|
||||||
await drift.close();
|
await drift.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version < 13) {
|
||||||
|
await Store.put(StoreKey.photoManagerCustomFilter, true);
|
||||||
|
}
|
||||||
|
|
||||||
if (targetVersion >= 12) {
|
if (targetVersion >= 12) {
|
||||||
await Store.put(StoreKey.version, targetVersion);
|
await Store.put(StoreKey.version, targetVersion);
|
||||||
return;
|
return;
|
||||||
@@ -183,24 +175,33 @@ Future<void> _migrateDeviceAsset(Isar db) async {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> migrateDeviceAssetToSqlite(Isar db, Drift drift) async {
|
Future<void> _migrateDeviceAssetToSqlite(Isar db, Drift drift) async {
|
||||||
try {
|
try {
|
||||||
final isarDeviceAssets = await db.deviceAssetEntitys.where().findAll();
|
final isarDeviceAssets =
|
||||||
|
await db.deviceAssetEntitys.where().sortByAssetId().findAll();
|
||||||
await drift.batch((batch) {
|
await drift.batch((batch) {
|
||||||
for (final deviceAsset in isarDeviceAssets) {
|
for (final deviceAsset in isarDeviceAssets) {
|
||||||
batch.update(
|
final companion = LocalAssetEntityCompanion(
|
||||||
|
updatedAt: Value(deviceAsset.modifiedTime),
|
||||||
|
id: Value(deviceAsset.assetId),
|
||||||
|
checksum: Value(base64.encode(deviceAsset.hash)),
|
||||||
|
);
|
||||||
|
batch.insert<$LocalAssetEntityTable, LocalAssetEntityData>(
|
||||||
drift.localAssetEntity,
|
drift.localAssetEntity,
|
||||||
LocalAssetEntityCompanion(
|
companion,
|
||||||
checksum: Value(base64.encode(deviceAsset.hash)),
|
onConflict: DoUpdate(
|
||||||
|
(_) => companion,
|
||||||
|
where: (old) => old.updatedAt.equals(deviceAsset.modifiedTime),
|
||||||
),
|
),
|
||||||
where: (t) => t.id.equals(deviceAsset.assetId),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
debugPrint(
|
if (kDebugMode) {
|
||||||
"[MIGRATION] Error while migrating device assets to SQLite: $error",
|
debugPrint(
|
||||||
);
|
"[MIGRATION] Error while migrating device assets to SQLite: $error",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,18 +212,3 @@ class _DeviceAsset {
|
|||||||
|
|
||||||
const _DeviceAsset({required this.assetId, this.hash, this.dateTime});
|
const _DeviceAsset({required this.assetId, this.hash, this.dateTime});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> runNewSync(WidgetRef ref, {bool full = false}) async {
|
|
||||||
ref.read(backupProvider.notifier).cancelBackup();
|
|
||||||
|
|
||||||
final backgroundManager = ref.read(backgroundSyncProvider);
|
|
||||||
Future.wait([
|
|
||||||
backgroundManager.syncLocal(full: full).then(
|
|
||||||
(_) {
|
|
||||||
Logger("runNewSync").fine("Hashing assets after syncLocal");
|
|
||||||
backgroundManager.hashAssets();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
backgroundManager.syncRemote(),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/models/backup/backup_state.model.dart';
|
import 'package:immich_mobile/models/backup/backup_state.model.dart';
|
||||||
import 'package:immich_mobile/models/server_info/server_info.model.dart';
|
import 'package:immich_mobile/models/server_info/server_info.model.dart';
|
||||||
|
import 'package:immich_mobile/providers/background_sync.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
||||||
import 'package:immich_mobile/providers/cast.provider.dart';
|
import 'package:immich_mobile/providers/cast.provider.dart';
|
||||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
@@ -50,6 +51,7 @@ class ImmichSliverAppBar extends ConsumerWidget {
|
|||||||
pinned: pinned,
|
pinned: pinned,
|
||||||
snap: snap,
|
snap: snap,
|
||||||
expandedHeight: expandedHeight,
|
expandedHeight: expandedHeight,
|
||||||
|
backgroundColor: context.colorScheme.surfaceContainer,
|
||||||
shape: const RoundedRectangleBorder(
|
shape: const RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: BorderRadius.all(
|
||||||
Radius.circular(5),
|
Radius.circular(5),
|
||||||
@@ -66,6 +68,24 @@ class ImmichSliverAppBar extends ConsumerWidget {
|
|||||||
child: action,
|
child: action,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.swipe_left_alt_rounded),
|
||||||
|
onPressed: () => context.pop(),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
ref.read(backgroundSyncProvider).syncLocal(full: true);
|
||||||
|
ref.read(backgroundSyncProvider).syncRemote();
|
||||||
|
|
||||||
|
Future.delayed(
|
||||||
|
const Duration(seconds: 10),
|
||||||
|
() => ref.read(backgroundSyncProvider).hashAssets(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.sync,
|
||||||
|
),
|
||||||
|
),
|
||||||
if (isCasting)
|
if (isCasting)
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(right: 12),
|
padding: const EdgeInsets.only(right: 12),
|
||||||
@@ -107,30 +127,13 @@ class _ImmichLogoWithText extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
Builder(
|
Builder(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return Badge(
|
return Padding(
|
||||||
padding:
|
padding: const EdgeInsets.only(top: 3.0),
|
||||||
const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
|
child: SvgPicture.asset(
|
||||||
backgroundColor: context.primaryColor,
|
context.isDarkTheme
|
||||||
alignment: Alignment.centerRight,
|
? 'assets/immich-logo-inline-dark.svg'
|
||||||
offset: const Offset(16, -8),
|
: 'assets/immich-logo-inline-light.svg',
|
||||||
label: Text(
|
height: 40,
|
||||||
'β',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 11,
|
|
||||||
color: context.colorScheme.onPrimary,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontFamily: 'OverpassMono',
|
|
||||||
height: 1.2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 3.0),
|
|
||||||
child: SvgPicture.asset(
|
|
||||||
context.isDarkTheme
|
|
||||||
? 'assets/immich-logo-inline-dark.svg'
|
|
||||||
: 'assets/immich-logo-inline-light.svg',
|
|
||||||
height: 40,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||||
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
import 'package:immich_mobile/providers/backup/backup.provider.dart';
|
||||||
@@ -18,7 +17,6 @@ import 'package:immich_mobile/providers/gallery_permission.provider.dart';
|
|||||||
import 'package:immich_mobile/providers/oauth.provider.dart';
|
import 'package:immich_mobile/providers/oauth.provider.dart';
|
||||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
import 'package:immich_mobile/providers/server_info.provider.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/utils/migration.dart';
|
|
||||||
import 'package:immich_mobile/utils/provider_utils.dart';
|
import 'package:immich_mobile/utils/provider_utils.dart';
|
||||||
import 'package:immich_mobile/utils/url_helper.dart';
|
import 'package:immich_mobile/utils/url_helper.dart';
|
||||||
import 'package:immich_mobile/utils/version_compatibility.dart';
|
import 'package:immich_mobile/utils/version_compatibility.dart';
|
||||||
@@ -194,15 +192,6 @@ class LoginForm extends HookConsumerWidget {
|
|||||||
if (result.shouldChangePassword && !result.isAdmin) {
|
if (result.shouldChangePassword && !result.isAdmin) {
|
||||||
context.pushRoute(const ChangePasswordRoute());
|
context.pushRoute(const ChangePasswordRoute());
|
||||||
} else {
|
} else {
|
||||||
final isBeta = Store.isBetaTimelineEnabled;
|
|
||||||
if (isBeta) {
|
|
||||||
await ref
|
|
||||||
.read(galleryPermissionNotifier.notifier)
|
|
||||||
.requestGalleryPermission();
|
|
||||||
await runNewSync(ref);
|
|
||||||
context.replaceRoute(const TabShellRoute());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
context.replaceRoute(const TabControllerRoute());
|
context.replaceRoute(const TabControllerRoute());
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -303,18 +292,9 @@ class LoginForm extends HookConsumerWidget {
|
|||||||
if (isSuccess) {
|
if (isSuccess) {
|
||||||
isLoading.value = false;
|
isLoading.value = false;
|
||||||
final permission = ref.watch(galleryPermissionNotifier);
|
final permission = ref.watch(galleryPermissionNotifier);
|
||||||
final isBeta = Store.isBetaTimelineEnabled;
|
if (permission.isGranted || permission.isLimited) {
|
||||||
if (!isBeta && (permission.isGranted || permission.isLimited)) {
|
|
||||||
ref.watch(backupProvider.notifier).resumeBackup();
|
ref.watch(backupProvider.notifier).resumeBackup();
|
||||||
}
|
}
|
||||||
if (isBeta) {
|
|
||||||
await ref
|
|
||||||
.read(galleryPermissionNotifier.notifier)
|
|
||||||
.requestGalleryPermission();
|
|
||||||
await runNewSync(ref);
|
|
||||||
context.replaceRoute(const TabShellRoute());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
context.replaceRoute(const TabControllerRoute());
|
context.replaceRoute(const TabControllerRoute());
|
||||||
}
|
}
|
||||||
} catch (error, stack) {
|
} catch (error, stack) {
|
||||||
|
|||||||
@@ -1,269 +0,0 @@
|
|||||||
import 'dart:math' as math;
|
|
||||||
|
|
||||||
import 'package:auto_route/auto_route.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
|
||||||
|
|
||||||
class BetaTimelineListTile extends ConsumerStatefulWidget {
|
|
||||||
const BetaTimelineListTile({
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
ConsumerState<BetaTimelineListTile> createState() =>
|
|
||||||
_BetaTimelineListTileState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BetaTimelineListTileState extends ConsumerState<BetaTimelineListTile>
|
|
||||||
with SingleTickerProviderStateMixin {
|
|
||||||
late AnimationController _animationController;
|
|
||||||
late Animation<double> _rotationAnimation;
|
|
||||||
late Animation<double> _pulseAnimation;
|
|
||||||
late Animation<double> _gradientAnimation;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_animationController = AnimationController(
|
|
||||||
duration: const Duration(seconds: 3),
|
|
||||||
vsync: this,
|
|
||||||
);
|
|
||||||
|
|
||||||
_rotationAnimation = Tween<double>(begin: 0, end: 2 * math.pi).animate(
|
|
||||||
CurvedAnimation(
|
|
||||||
parent: _animationController,
|
|
||||||
curve: Curves.linear,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
_pulseAnimation = Tween<double>(begin: 1, end: 1.1).animate(
|
|
||||||
CurvedAnimation(
|
|
||||||
parent: _animationController,
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
_gradientAnimation = Tween<double>(begin: 0, end: 1).animate(
|
|
||||||
CurvedAnimation(
|
|
||||||
parent: _animationController,
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
_animationController.repeat(reverse: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_animationController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final betaTimelineValue = ref
|
|
||||||
.watch(appSettingsServiceProvider)
|
|
||||||
.getSetting<bool>(AppSettingsEnum.betaTimeline);
|
|
||||||
|
|
||||||
return AnimatedBuilder(
|
|
||||||
animation: _animationController,
|
|
||||||
builder: (context, child) {
|
|
||||||
void onSwitchChanged(bool value) {
|
|
||||||
showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return AlertDialog(
|
|
||||||
title: value
|
|
||||||
? const Text("Enable Beta Timeline")
|
|
||||||
: const Text("Disable Beta Timeline"),
|
|
||||||
content: value
|
|
||||||
? const Text(
|
|
||||||
"Are you sure you want to enable the beta timeline?",
|
|
||||||
)
|
|
||||||
: const Text(
|
|
||||||
"Are you sure you want to disable the beta timeline?",
|
|
||||||
),
|
|
||||||
actions: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
await ref.read(appSettingsServiceProvider).setSetting(
|
|
||||||
AppSettingsEnum.betaTimeline,
|
|
||||||
value,
|
|
||||||
);
|
|
||||||
context.router.replaceAll(
|
|
||||||
[ChangeExperienceRoute(switchingToBeta: value)],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: const Text("Yes"),
|
|
||||||
),
|
|
||||||
TextButton(
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
},
|
|
||||||
child: const Text("No"),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final gradientColors = [
|
|
||||||
Color.lerp(
|
|
||||||
context.primaryColor.withValues(alpha: 0.3),
|
|
||||||
context.primaryColor.withValues(alpha: 0.1),
|
|
||||||
_gradientAnimation.value,
|
|
||||||
)!,
|
|
||||||
Color.lerp(
|
|
||||||
context.primaryColor.withValues(alpha: 0.2),
|
|
||||||
context.primaryColor.withValues(alpha: 0.4),
|
|
||||||
_gradientAnimation.value,
|
|
||||||
)!,
|
|
||||||
Color.lerp(
|
|
||||||
context.primaryColor.withValues(alpha: 0.1),
|
|
||||||
context.primaryColor.withValues(alpha: 0.3),
|
|
||||||
_gradientAnimation.value,
|
|
||||||
)!,
|
|
||||||
];
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(12)),
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: gradientColors,
|
|
||||||
stops: const [0.0, 0.5, 1.0],
|
|
||||||
begin: Alignment.topLeft,
|
|
||||||
end: Alignment.bottomRight,
|
|
||||||
transform: GradientRotation(_rotationAnimation.value * 0.1),
|
|
||||||
),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: context.primaryColor.withValues(alpha: 0.1),
|
|
||||||
blurRadius: 8,
|
|
||||||
offset: const Offset(0, 2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Container(
|
|
||||||
margin: const EdgeInsets.all(1.5),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(10.5)),
|
|
||||||
color: context.scaffoldBackgroundColor,
|
|
||||||
),
|
|
||||||
child: Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(10.5)),
|
|
||||||
child: InkWell(
|
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(10.5)),
|
|
||||||
onTap: () => onSwitchChanged(!betaTimelineValue),
|
|
||||||
child: Padding(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.symmetric(horizontal: 20, vertical: 16),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Transform.scale(
|
|
||||||
scale: _pulseAnimation.value,
|
|
||||||
child: Transform.rotate(
|
|
||||||
angle: _rotationAnimation.value * 0.02,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: [
|
|
||||||
context.primaryColor.withValues(alpha: 0.2),
|
|
||||||
context.primaryColor.withValues(alpha: 0.1),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
Icons.auto_awesome,
|
|
||||||
color: context.primaryColor,
|
|
||||||
size: 20,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 16),
|
|
||||||
Expanded(
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"advanced_settings_beta_timeline_title"
|
|
||||||
.t(context: context),
|
|
||||||
style:
|
|
||||||
context.textTheme.titleMedium?.copyWith(
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
horizontal: 6,
|
|
||||||
vertical: 2,
|
|
||||||
),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: const BorderRadius.all(
|
|
||||||
Radius.circular(8),
|
|
||||||
),
|
|
||||||
gradient: LinearGradient(
|
|
||||||
colors: [
|
|
||||||
context.primaryColor
|
|
||||||
.withValues(alpha: 0.8),
|
|
||||||
context.primaryColor
|
|
||||||
.withValues(alpha: 0.6),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Text(
|
|
||||||
'NEW',
|
|
||||||
style:
|
|
||||||
context.textTheme.labelSmall?.copyWith(
|
|
||||||
color: Colors.white,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
fontSize: 10,
|
|
||||||
height: 1.2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(
|
|
||||||
"advanced_settings_beta_timeline_subtitle"
|
|
||||||
.t(context: context),
|
|
||||||
style: context.textTheme.labelLarge?.copyWith(
|
|
||||||
color: context.textTheme.labelLarge?.color
|
|
||||||
?.withValues(alpha: 0.7),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Switch.adaptive(
|
|
||||||
value: betaTimelineValue,
|
|
||||||
onChanged: onSwitchChanged,
|
|
||||||
activeColor: context.primaryColor,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -139,6 +139,7 @@ flutter:
|
|||||||
- family: OverpassMono
|
- family: OverpassMono
|
||||||
fonts:
|
fonts:
|
||||||
- asset: fonts/overpass/OverpassMono.ttf
|
- asset: fonts/overpass/OverpassMono.ttf
|
||||||
|
|
||||||
flutter_launcher_icons:
|
flutter_launcher_icons:
|
||||||
image_path_android: 'assets/immich-logo.png'
|
image_path_android: 'assets/immich-logo.png'
|
||||||
adaptive_icon_background: '#ffffff'
|
adaptive_icon_background: '#ffffff'
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
// dart format width=80
|
|
||||||
// GENERATED CODE, DO NOT EDIT BY HAND.
|
|
||||||
// ignore_for_file: type=lint
|
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
import 'package:drift/internal/migrations.dart';
|
|
||||||
import 'schema_v1.dart' as v1;
|
|
||||||
import 'schema_v2.dart' as v2;
|
|
||||||
|
|
||||||
class GeneratedHelper implements SchemaInstantiationHelper {
|
|
||||||
@override
|
|
||||||
GeneratedDatabase databaseForVersion(QueryExecutor db, int version) {
|
|
||||||
switch (version) {
|
|
||||||
case 1:
|
|
||||||
return v1.DatabaseAtV1(db);
|
|
||||||
case 2:
|
|
||||||
return v2.DatabaseAtV2(db);
|
|
||||||
default:
|
|
||||||
throw MissingSchemaException(version, versions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const versions = const [1, 2];
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,38 +0,0 @@
|
|||||||
// dart format width=80
|
|
||||||
// ignore_for_file: unused_local_variable, unused_import
|
|
||||||
import 'package:drift/drift.dart';
|
|
||||||
import 'package:drift_dev/api/migrations_native.dart';
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
|
||||||
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
|
|
||||||
|
|
||||||
import 'generated/schema.dart';
|
|
||||||
import 'generated/schema_v1.dart' as v1;
|
|
||||||
import 'generated/schema_v2.dart' as v2;
|
|
||||||
|
|
||||||
void main() {
|
|
||||||
driftRuntimeOptions.dontWarnAboutMultipleDatabases = true;
|
|
||||||
late SchemaVerifier verifier;
|
|
||||||
|
|
||||||
setUpAll(() {
|
|
||||||
verifier = SchemaVerifier(GeneratedHelper());
|
|
||||||
});
|
|
||||||
|
|
||||||
group('simple database migrations', () {
|
|
||||||
// These simple tests verify all possible schema updates with a simple (no
|
|
||||||
// data) migration. This is a quick way to ensure that written database
|
|
||||||
// migrations properly alter the schema.
|
|
||||||
const versions = GeneratedHelper.versions;
|
|
||||||
for (final (i, fromVersion) in versions.indexed) {
|
|
||||||
group('from $fromVersion', () {
|
|
||||||
for (final toVersion in versions.skip(i + 1)) {
|
|
||||||
test('to $toVersion', () async {
|
|
||||||
final schema = await verifier.schemaAt(fromVersion);
|
|
||||||
final db = Drift(schema.newConnection());
|
|
||||||
await verifier.migrateAndValidate(db, toVersion);
|
|
||||||
await db.close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user