From b4a798c39fa8bce75f560663d618a9d568e01ff3 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Tue, 3 Jun 2025 21:31:50 +0530 Subject: [PATCH] feat(mobile): remote asset & exif sync (#18756) * feat(mobile): remote asset & exif sync * add visibility and update constraints * chore: generate drifts * update ids to be strings * clear remote entities on logout * reset sqlite button --------- Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- .../drift_schemas/main/drift_schema_v1.json | Bin 4108 -> 15819 bytes .../lib/domain/models/asset/asset.model.dart | 18 +- .../domain/services/sync_stream.service.dart | 1 - mobile/lib/extensions/string_extensions.dart | 9 - .../infrastructure/entities/exif.entity.dart | 53 ++++++ .../entities/exif.entity.drift.dart | Bin 0 -> 60139 bytes .../entities/local_asset.entity.dart | 2 +- .../entities/local_asset.entity.drift.dart | Bin 25613 -> 25624 bytes .../entities/partner.entity.dart | 8 +- .../entities/partner.entity.drift.dart | Bin 23757 -> 23376 bytes .../entities/remote_asset.entity.dart | 35 ++++ .../entities/remote_asset.entity.drift.dart | Bin 0 -> 43533 bytes .../infrastructure/entities/user.entity.dart | 2 +- .../entities/user.entity.drift.dart | Bin 25521 -> 25320 bytes .../entities/user_metadata.entity.dart | 4 +- .../entities/user_metadata.entity.drift.dart | Bin 17619 -> 17406 bytes .../repositories/db.repository.dart | 4 + .../repositories/db.repository.drift.dart | Bin 4413 -> 5740 bytes .../repositories/sync_api.repository.dart | 2 + .../repositories/sync_stream.repository.dart | 164 +++++++++++++++--- .../pages/dev/feat_in_development.page.dart | 27 +++ ...ia_stat.page.dart => media_stat.page.dart} | 98 ++++++++--- mobile/lib/repositories/auth.repository.dart | 12 +- mobile/lib/routing/router.dart | 6 +- mobile/lib/routing/router.gr.dart | 16 ++ mobile/makefile | 2 +- 26 files changed, 388 insertions(+), 75 deletions(-) create mode 100644 mobile/lib/infrastructure/entities/exif.entity.drift.dart create mode 100644 mobile/lib/infrastructure/entities/remote_asset.entity.dart create mode 100644 mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart rename mobile/lib/presentation/pages/dev/{local_media_stat.page.dart => media_stat.page.dart} (76%) diff --git a/mobile/drift_schemas/main/drift_schema_v1.json b/mobile/drift_schemas/main/drift_schema_v1.json index 1870ef477f91db0a749c2310c16918d9d00d2197..5cdec3d924591137863fe2b99fe148936f240e74 100644 GIT binary patch literal 15819 zcmeHOS#R4o5dJHIUYY_%kRIFJH5LV2$3=~8(`@7H0^1e@EzveNI$V*;(IEeQXDCXd z4%bI~$c5h$4o!}Rb9^%#-rK}ylG^Qi+hxLu_*C*Ru-o=v!i6>B9#T8@kxP7BPqLcSLV2WW<9p%nM@AQ%qzF#_*YbHw<732vWk*I0txSBN}@WzCVI* zEsi-J2X>1j;gJ!eGLDjOerRP3DyGUT>cPe^$>CsKp9fW0ijE#Pc^!tKw~9Cmg0{Qw zdfh**qc3*4`xo2lpIJvIE$i9Sr?9>Y8qFNpxdCN3O%9~60x0P>k4sddIctHU7EcsZ z3C`T=s`KyNPT}0pW{#emJW)IA<6rKm!PGd)rKpbSs42jmYUDHu(R&c?(=j7cDkl}5 z^^`u&I8P?bVISQA80hBhDW0NFatW0ct~IHH6q8;2!2o<146o_|X6j4#^3$i@*? z{u1Xb#(nDc7t^YlXSBRcL(l@Jbj>3fQ!fiYH(XA_Sdu8b!APfNYX%RDc4*s<4v-KZ zZ>Ju2xFpjn2wNB-zdm4ezy+R_LgmR~u@X@M#?aM+rV6<=krT)J7*I`>THRL_oVER_ z_qGS7((PSXM##60xck)lcxIjU-uDJQtJ}Hgc20Z1!xG+qxuq+?VA=3)$H7$)-UM)z zd7Ot3lA)fm(H3dz5pEHGEFP~CI7T4;0saHjr0^x690HmO|0p}>J=1Jr|nR}(NqWlZEe9v~1ZVpJX=KGG!9Kdl?-=RB+h?9W=R;^OxK8sG4x;-$}ObFdOT zSngL}uIN{VV1S;n#JWlWb1&SMDx{WGQxBSYxi3cC!802`D)ag%`}0SJn@uX1tP?@W zx4smmNqbw0`ibL|8%Gr56PBq213&Lpt8D3rD3=6=@zavG1q;KIqd60u(i zWCp>MWDR88o-hR&(d#h6D}=IhNL*yjCB`J7vG_xPRoM2+8R)jqSnb6bJs1( z*hrN(B;y5kSf`A^QZ{p;VVFgBdO4(iRkGRnHyjc3Lufg8q_G}A_3FI~^DYPgF~QAw zO=jFVfNJa=Uqyy+P_;tfH%meww!qnFdzdQM9$ZRZe1mNG4QN8@CkcT|zM# zuJH(4W7ma>|BN!3CM{Mb+Zw~yT<{@>&O)r`79DPZWD0n%DxZI6AE@h}`M}&NXg+n5 z{T|@#@+{)lQWo)QZr-o62zJXyt5@cY)TXEeim(m0Tayf}I~`qOi*@Fs`MnqP##PNs z51IzHOAjU_gn^1LNJpE(r4z=1jB13LjW>nUEa(y_zK(eSJ(guhn?~4=`2>^ulnlSa z%2U$RzqJ5izQ?FAZoJhfUfS+bgC~rSC)JMWY}k}-Ih1Wptk6|k(~F*tHiAWHk_-jB z@~DT5DOER{n;h@*@J9sN7=|^?1*5Zc++=lU=dWHRVEcQ;8z2Rn-ht2zPE|*h$AWQ< z!j6#mKBYB+Uk?f|GIpziH3-dMGz`WwI5jAZV3muoqNmANBiQrlRrR>_pvmAa9g`nI z0$34ZI@%O20i!^7k1AbOcrDVJ!ZG&wlJ8+nqc2kaO$be47P~Rj>&#-3l?|JG?kqIH zgSh{aw#MX9X&Lq9bnGfBg{keE*ygYM^6dP#Qg;3_&(1lvPJ%E1t*aNm_3Jn>8m*x3_cJDZ>L@_UIFqf$`E3tUfXUM~M4g-kdp axn?`StF9_X8W-0v1P`gxk}dG|_2GXScKP)H delta 74 zcmX?I-J`I"}, localId: ${localId ?? ""}, isFavorite: $isFavorite, + visibility: $visibility, }'''; } @@ -39,9 +49,13 @@ class Asset extends BaseAsset { bool operator ==(Object other) { if (other is! Asset) return false; if (identical(this, other)) return true; - return super == other && id == other.id && localId == other.localId; + return super == other && + id == other.id && + localId == other.localId && + visibility == other.visibility; } @override - int get hashCode => super.hashCode ^ id.hashCode ^ localId.hashCode; + int get hashCode => + super.hashCode ^ id.hashCode ^ localId.hashCode ^ visibility.hashCode; } diff --git a/mobile/lib/domain/services/sync_stream.service.dart b/mobile/lib/domain/services/sync_stream.service.dart index ac63734b0..00f97825b 100644 --- a/mobile/lib/domain/services/sync_stream.service.dart +++ b/mobile/lib/domain/services/sync_stream.service.dart @@ -63,7 +63,6 @@ class SyncStreamService { Iterable data, ) async { _logger.fine("Processing sync data for $type of length ${data.length}"); - // ignore: prefer-switch-expression switch (type) { case SyncEntityType.userV1: return _syncStreamRepository.updateUsersV1(data.cast()); diff --git a/mobile/lib/extensions/string_extensions.dart b/mobile/lib/extensions/string_extensions.dart index 73c8c2d34..67411013e 100644 --- a/mobile/lib/extensions/string_extensions.dart +++ b/mobile/lib/extensions/string_extensions.dart @@ -1,7 +1,3 @@ -import 'dart:typed_data'; - -import 'package:uuid/parsing.dart'; - extension StringExtension on String { String capitalize() { return split(" ") @@ -33,8 +29,3 @@ extension DurationExtension on String { return int.parse(this); } } - -extension UUIDExtension on String { - Uint8List toUuidByte({bool shouldValidate = false}) => - UuidParsing.parseAsByteList(this, validate: shouldValidate); -} diff --git a/mobile/lib/infrastructure/entities/exif.entity.dart b/mobile/lib/infrastructure/entities/exif.entity.dart index 5a93bc976..11730b776 100644 --- a/mobile/lib/infrastructure/entities/exif.entity.dart +++ b/mobile/lib/infrastructure/entities/exif.entity.dart @@ -1,4 +1,7 @@ +import 'package:drift/drift.dart' hide Query; import 'package:immich_mobile/domain/models/exif.model.dart' as domain; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/exif.converter.dart'; import 'package:isar/isar.dart'; @@ -90,3 +93,53 @@ class ExifInfo { exposureSeconds: exposureSeconds, ); } + +class RemoteExifEntity extends Table with DriftDefaultsMixin { + const RemoteExifEntity(); + + TextColumn get assetId => + text().references(RemoteAssetEntity, #id, onDelete: KeyAction.cascade)(); + + TextColumn get city => text().nullable()(); + + TextColumn get state => text().nullable()(); + + TextColumn get country => text().nullable()(); + + DateTimeColumn get dateTimeOriginal => dateTime().nullable()(); + + TextColumn get description => text().nullable()(); + + IntColumn get height => integer().nullable()(); + + IntColumn get width => integer().nullable()(); + + TextColumn get exposureTime => text().nullable()(); + + IntColumn get fNumber => integer().nullable()(); + + IntColumn get fileSize => integer().nullable()(); + + IntColumn get focalLength => integer().nullable()(); + + IntColumn get latitude => integer().nullable()(); + + IntColumn get longitude => integer().nullable()(); + + IntColumn get iso => integer().nullable()(); + + TextColumn get make => text().nullable()(); + + TextColumn get model => text().nullable()(); + + TextColumn get orientation => text().nullable()(); + + TextColumn get timeZone => text().nullable()(); + + IntColumn get rating => integer().nullable()(); + + TextColumn get projectionType => text().nullable()(); + + @override + Set get primaryKey => {assetId}; +} diff --git a/mobile/lib/infrastructure/entities/exif.entity.drift.dart b/mobile/lib/infrastructure/entities/exif.entity.drift.dart new file mode 100644 index 0000000000000000000000000000000000000000..10025d9cb876ce60d9866804303fa33b84d8dcfb GIT binary patch literal 60139 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 ANdN!< literal 0 HcmV?d00001 diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.dart b/mobile/lib/infrastructure/entities/local_asset.entity.dart index 724cf532c..ff5ee7481 100644 --- a/mobile/lib/infrastructure/entities/local_asset.entity.dart +++ b/mobile/lib/infrastructure/entities/local_asset.entity.dart @@ -2,7 +2,7 @@ import 'package:drift/drift.dart'; import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; -@TableIndex(name: 'local_asset_checksum', columns: {#checksum}) +@TableIndex(name: 'idx_local_asset_checksum', columns: {#checksum}) class LocalAssetEntity extends Table with DriftDefaultsMixin, AssetEntityMixin { const LocalAssetEntity(); diff --git a/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/local_asset.entity.drift.dart index 0a4896a4a3a1e015c2cdaf89bb8b516ad0ee38e5..68bc1b3c5ddb42d8acee3422a1dc75a4f69973bf 100644 GIT binary patch delta 41 scmeA@!8qdt07aS)8vp - blob().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get sharedById => + text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); - BlobColumn get sharedWithId => - blob().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get sharedWithId => + text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); BoolColumn get inTimeline => boolean().withDefault(const Constant(false))(); diff --git a/mobile/lib/infrastructure/entities/partner.entity.drift.dart b/mobile/lib/infrastructure/entities/partner.entity.drift.dart index 974a9e3c30367eff4c91ec7f02d9053d72776252..26a5dd2fe0eadc7fc6fe67b552b26b64b2b0d619 100644 GIT binary patch delta 1412 zcmbVMO-vI}5Y8*m)-3@fSla^aTeMiPv=mAZ>9&B80wN~Tv_wGD$VxYXlC;IBm!cjy zAk4*!Ap{R5Y7f4I#Kia$qc@_72QMBV@nB+NJQ(F7xPNaU0!?&Jv)?!Gn{Q@!-s)5I zeg)lmJwF~SiKjBh7%V>VG!~k`i~3OW{OAdIf-v%nuNL@^Ck;wgTt>w52)TT~YsJ<<5YG#^lCGFecIMDpH%hy$Fu8Y^5xvinbH!<$NTE^|T$U<1T9m!` z#aL=CXJZzsS7ZXiY=uuIU9jcX!;DRx0Lj@*l3_^&%I$KLg%{46|4}j^g3X^LxlL;g ze5y2wGb>l$>{XXwrWEfjyJ;QFXHHRbXi7K#RmFe+o2y~KSx)Xb7A1pO)(O>4Mm3ma zm-m*;vPa?Kk-U>rl%&aJ+1vY1F29muki2t63+adVHE?Q3ZZwP_gYrLPcxkDCGJheg zdJa&&%v)(fO~idNg<5xS1?Hrs2=hrk70>Wvr=}*RCzI-W1YPn%Vhn5T^{@~!8`JW> zxf$>TT<|f}4fg|Ysimu2Jq;gmJ$faTY;c!#sF{p(oY#Xl%&IU%!Yusg9-�@Gr!w zJ#24myIB5;kc>}Yg(BB_I}xq%rjM>aF`ntw;L-jNZTvyMUxUA#rP*+N;8vZY7utC{ zs5K}S6EX?$&LBSro)y5-X$u$!=ZB^;2|nf7Hi$V6XA(ksCsbRj4o$ktb{<}UE` JjAJ;5{s5ap$%Oy_ delta 1772 zcma)6Ur19?80YBN+O(ycxy?-PHUC_7ZvIVeZc`(jrh%HuLaS?=?85TTb|oZ2Ees;4 z^m{1~DvW&ZKoLP81wEMMQxMdHPxeyaLj)DsxpzzNnH#x>%g*onzTfY6zVDo~_7tr? zM|WOj`uLD|Oq?9^`_B1zkuPHSFe6mX4SMt>P^4qXPeQ8T0BVOP$T&Az2=jqtTyky2gMii5ot$4X)f4&f0ltfGgv zVRcdfE-4#8`<17Nfd^G)xMI^%c`#$o#%s1M9jd}>^&v#v1?ACaobsJxV7(zjujF-c zFzF}(uQN+G6dWCrE*8VUvV(&q=OdVH7^C%mHI&dmsnZivh?kr-NvIlsc6Y0xrZq2_ z(88J4EEsFKpprDA3l09+@&T18%_#1wgBTeWM))ALCA{frLbQRp_F4+S6YXs=DX&ku z=@e~8bxg|fPMQU#JFilGa!Uiqq(Z)ipO2KbgFJd@#(7iUEJfwj*I>#DEZ10L4sUVPx{s zToWkAA&WR5bm=>M|6G0PzDu&KT6o*@gL;h>1CBH!{v$qPH^T4J ztF+4dUY44OOX|N7r}QiZ6r~P8lh=aZ HcyFM;9tSP1 diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.dart new file mode 100644 index 000000000..96f4077a2 --- /dev/null +++ b/mobile/lib/infrastructure/entities/remote_asset.entity.dart @@ -0,0 +1,35 @@ +import 'package:drift/drift.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/utils/asset.mixin.dart'; +import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; + +@TableIndex( + name: 'UQ_remote_asset_owner_checksum', + columns: {#checksum, #ownerId}, + unique: true, +) +class RemoteAssetEntity extends Table + with DriftDefaultsMixin, AssetEntityMixin { + const RemoteAssetEntity(); + + TextColumn get id => text()(); + + TextColumn get checksum => text()(); + + BoolColumn get isFavorite => boolean().withDefault(const Constant(false))(); + + TextColumn get ownerId => + text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + + DateTimeColumn get localDateTime => dateTime().nullable()(); + + TextColumn get thumbHash => text().nullable()(); + + DateTimeColumn get deletedAt => dateTime().nullable()(); + + IntColumn get visibility => intEnum()(); + + @override + Set get primaryKey => {id}; +} diff --git a/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart b/mobile/lib/infrastructure/entities/remote_asset.entity.drift.dart new file mode 100644 index 0000000000000000000000000000000000000000..e3fe521700e6a0afce09422078e181b040ca698d GIT binary patch literal 43533 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 UJD>U`w^1r_%m3dE-uL(Z4|@Fj7ytkO literal 0 HcmV?d00001 diff --git a/mobile/lib/infrastructure/entities/user.entity.dart b/mobile/lib/infrastructure/entities/user.entity.dart index 955b2267d..b0c1e6e86 100644 --- a/mobile/lib/infrastructure/entities/user.entity.dart +++ b/mobile/lib/infrastructure/entities/user.entity.dart @@ -78,7 +78,7 @@ class User { class UserEntity extends Table with DriftDefaultsMixin { const UserEntity(); - BlobColumn get id => blob()(); + TextColumn get id => text()(); TextColumn get name => text()(); BoolColumn get isAdmin => boolean().withDefault(const Constant(false))(); TextColumn get email => text()(); diff --git a/mobile/lib/infrastructure/entities/user.entity.drift.dart b/mobile/lib/infrastructure/entities/user.entity.drift.dart index 474746a79210c40b5928a1cceac1e4b93d1bedc8..32be96951829bdb9dc0255b3575d3f6a41ea6476 100644 GIT binary patch delta 389 zcmdmZobkm`#t8=|rdv;zX5^fBRd}*FqXnb!RhOui7!#$pE&eHfxJd4iPq=68|-%rLXOWM$bHjW%Z}2QY#RU;-+;uc`*3IMr%4 z7pqQYVl>{&u5p+dNS)T+2jX?<$#YG%QRA4bU@5WrxWy?Jm|1ge!sOfVJqUGKtual(BThh&P5LSABSs*XZtv13YZW}c2hYHng?j*i0Q z3qFdIcLXe-Tpz^7o0+0uYpYO_ky)$x2+ hoFLcNM#ye9jgW==GcG!U8RRyQoP47A=DTrMSOCj+hK&FK delta 661 zcmaEHlyT#6#t8@bQxc0xtV${iQd8nn5=#;%{*#=TZ#?lgC!_IXRz@#IlgR;$Y5bW+ zdZC$lB^ExJ#U%=vDU&}kifq2jsKdyEEU}rDS(ix!S;S5uJr%5eA&2hfT-N!lIAtg2 zvMNm$;5FMkpF1CiszxClpeo79w}j<4HweCDN3}a&vYQ!2WRt8c8>8`NIpqLG0aP~u z-G5L^Mi50%LmepEs3AL^BP(REJq)N2+fq2Gx z@?2n5lkKdfHWyi(VnMNbvy*KY6ROBOdp=a>ZQkUx3r$Ut>s_<}4RxQzVQiqMl2VkJ zR^kM7m1|*XVoqjBrCvs2afU`_il&Z2USe*ljzVU!V@hsjo(`7+2uuo)oGc%(Q9l!_ z-c*nw#Txk~8L35jnJGF7B^jB;K!)b##{mZzQNslo+18UM21#u`5ws4?xiz6z&=lK) rlE#5>*~z=2xF#P9=i59XoE6R2v!f!IQKJLsi^+`f;+sX{uCM?AvoYXD diff --git a/mobile/lib/infrastructure/entities/user_metadata.entity.dart b/mobile/lib/infrastructure/entities/user_metadata.entity.dart index ebbfeebad..302a9ffce 100644 --- a/mobile/lib/infrastructure/entities/user_metadata.entity.dart +++ b/mobile/lib/infrastructure/entities/user_metadata.entity.dart @@ -6,8 +6,8 @@ import 'package:immich_mobile/infrastructure/utils/drift_default.mixin.dart'; class UserMetadataEntity extends Table with DriftDefaultsMixin { const UserMetadataEntity(); - BlobColumn get userId => - blob().references(UserEntity, #id, onDelete: KeyAction.cascade)(); + TextColumn get userId => + text().references(UserEntity, #id, onDelete: KeyAction.cascade)(); TextColumn get preferences => text().map(userPreferenceConverter)(); @override diff --git a/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart b/mobile/lib/infrastructure/entities/user_metadata.entity.drift.dart index 9829fd1acc066d44535b6c2cbdbaa8bd81c9f52a..95ab63ebf6993c5199b66689edfb5f53955711c3 100644 GIT binary patch delta 904 zcmccI$@s6Gal*-oFP$ddQ=C|FCi zVdnj1Qw0hrvwMJ*i%)K5SAuBSe4M?UN!my+6zHmeqSUn1qSUG7_i>3$ z&gWVMR4&C0vPX(radIhl71W>tUct$}Y!FvH1j{N+R_8T@%PNDd(cZkD_X`t{`9VN= z@^t|TFl#Pc5vma%1e7L!kQJD0&Z`Jl0W&6ExR#00X!2drexMP#VzyvXb@Bx+GHhpAz-+h$}=*;LT#hL83kb4A$cF{zh_>fnHc*PHCzQFi|hEyCsL> lbWo~CPxrfFZclgMV}xcBJ0w>k%-cNIF&XBg%{

83BFeDk%T} delta 1158 zcmZuwO-vI(6wZ{k)RrP3Eo}pJfL01@HsH4W2ozFd6oUZ|VB$ftu+!|C+QN2MgGOS! z7*Eo?5EBhv{USyYK#dN6B7?cJsG@lFdhuX*<~ra$ed>0``$O-`}=zjeY}sB z-UkIwi5uQ30TCJ`$R*OU)6&NqZ zQWrbL=TtH%aj+9@(xxgJ5dALi%%C24;q0dOQf)JgyM1ufl`?E`xZtmcMVGq&Dx;!$ ztou@x^*i|Bj#Klt`u+ zOJnFypMamyI>c!^QG^()*U~>+KKoN(bX^<4zdb0a^|ddev^99I`*?`9Uq5sN&XPQl zd4&k43pw>#e$a+bz*a{bmY9flCRdozhX!L{IukI_ISR|2D$U-pF diff --git a/mobile/lib/infrastructure/repositories/db.repository.dart b/mobile/lib/infrastructure/repositories/db.repository.dart index 17fcad76b..4ad60276a 100644 --- a/mobile/lib/infrastructure/repositories/db.repository.dart +++ b/mobile/lib/infrastructure/repositories/db.repository.dart @@ -3,10 +3,12 @@ import 'dart:async'; import 'package:drift/drift.dart'; import 'package:drift_flutter/drift_flutter.dart'; import 'package:immich_mobile/domain/interfaces/db.interface.dart'; +import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_album_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/local_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/partner.entity.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user_metadata.entity.dart'; import 'package:isar/isar.dart'; @@ -36,6 +38,8 @@ class IsarDatabaseRepository implements IDatabaseRepository { LocalAlbumEntity, LocalAssetEntity, LocalAlbumAssetEntity, + RemoteAssetEntity, + RemoteExifEntity, ], ) class Drift extends $Drift implements IDatabaseRepository { diff --git a/mobile/lib/infrastructure/repositories/db.repository.drift.dart b/mobile/lib/infrastructure/repositories/db.repository.drift.dart index 6611eb5c92957bdf09f80f9b9dc90e9949a14ff9..d1bda93653f51e1767a0ecb5d55aa0ed8d068c5c 100644 GIT binary patch delta 522 zcmdn1^hRgH9Hxo!W^6^Nx%nljlLeWS7|kcfTCk*6WTs87Ws+gE*gT8r64T^AEIbP4 zdMZI+Wsb$gsU@y?C7C6aA&E&jsR~ft2CSldsEQ_^Wma^sfa!Au8j7h6A*x`@r2qz* zC|Wj$vvxD)KS1Xkbm26HrvcaJ+)89n7yNN@XV33vj7oDA%+Gss@D$)KHLLCkyh)XhKvXypCip RhH7z;*`nw=fJWDH0RV(Ct!@AS delta 44 zcmaE(vsY=u9Hz}-%omt8zh*0A _parseLines(List lines) { diff --git a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart index 5ad9a369d..804f66c5b 100644 --- a/mobile/lib/infrastructure/repositories/sync_stream.repository.dart +++ b/mobile/lib/infrastructure/repositories/sync_stream.repository.dart @@ -1,12 +1,13 @@ import 'package:drift/drift.dart'; -import 'package:flutter/foundation.dart'; import 'package:immich_mobile/domain/interfaces/sync_stream.interface.dart'; -import 'package:immich_mobile/extensions/string_extensions.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/infrastructure/entities/exif.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/partner.entity.drift.dart'; +import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:logging/logging.dart'; -import 'package:openapi/api.dart'; +import 'package:openapi/api.dart' hide AssetVisibility; class DriftSyncStreamRepository extends DriftDatabaseRepository implements ISyncStreamRepository { @@ -22,7 +23,7 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository for (final user in data) { batch.delete( _db.userEntity, - UserEntityCompanion(id: Value(user.userId.toUuidByte())), + UserEntityCompanion(id: Value(user.userId)), ); } }); @@ -44,7 +45,7 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository batch.insert( _db.userEntity, - companion.copyWith(id: Value(user.id.toUuidByte())), + companion.copyWith(id: Value(user.id)), onConflict: DoUpdate((_) => companion), ); } @@ -63,8 +64,8 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository batch.delete( _db.partnerEntity, PartnerEntityCompanion( - sharedById: Value(partner.sharedById.toUuidByte()), - sharedWithId: Value(partner.sharedWithId.toUuidByte()), + sharedById: Value(partner.sharedById), + sharedWithId: Value(partner.sharedWithId), ), ); } @@ -86,8 +87,8 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository batch.insert( _db.partnerEntity, companion.copyWith( - sharedById: Value(partner.sharedById.toUuidByte()), - sharedWithId: Value(partner.sharedWithId.toUuidByte()), + sharedById: Value(partner.sharedById), + sharedWithId: Value(partner.sharedWithId), ), onConflict: DoUpdate((_) => companion), ); @@ -99,36 +100,153 @@ class DriftSyncStreamRepository extends DriftDatabaseRepository } } - // Assets - @override - Future updateAssetsV1(Iterable data) async { - debugPrint("updateAssetsV1 - ${data.length}"); - } - @override Future deleteAssetsV1(Iterable data) async { - debugPrint("deleteAssetsV1 - ${data.length}"); + try { + await _deleteAssetsV1(data); + } catch (e, s) { + _logger.severe('Error while processing deleteAssetsV1', e, s); + rethrow; + } } - // Partner Assets @override - Future updatePartnerAssetsV1(Iterable data) async { - debugPrint("updatePartnerAssetsV1 - ${data.length}"); + Future updateAssetsV1(Iterable data) async { + try { + await _updateAssetsV1(data); + } catch (e, s) { + _logger.severe('Error while processing updateAssetsV1', e, s); + rethrow; + } } @override Future deletePartnerAssetsV1(Iterable data) async { - debugPrint("deletePartnerAssetsV1 - ${data.length}"); + try { + await _deleteAssetsV1(data); + } catch (e, s) { + _logger.severe('Error while processing deletePartnerAssetsV1', e, s); + rethrow; + } + } + + @override + Future updatePartnerAssetsV1(Iterable data) async { + try { + await _updateAssetsV1(data); + } catch (e, s) { + _logger.severe('Error while processing updatePartnerAssetsV1', e, s); + rethrow; + } } - // EXIF @override Future updateAssetsExifV1(Iterable data) async { - debugPrint("updateAssetsExifV1 - ${data.length}"); + try { + await _updateAssetExifV1(data); + } catch (e, s) { + _logger.severe('Error while processing updateAssetsExifV1', e, s); + rethrow; + } } @override Future updatePartnerAssetsExifV1(Iterable data) async { - debugPrint("updatePartnerAssetsExifV1 - ${data.length}"); + try { + await _updateAssetExifV1(data); + } catch (e, s) { + _logger.severe('Error while processing updatePartnerAssetsExifV1', e, s); + rethrow; + } } + + Future _updateAssetsV1(Iterable data) => + _db.batch((batch) { + for (final asset in data) { + final companion = RemoteAssetEntityCompanion( + name: Value(asset.originalFileName), + type: Value(asset.type.toAssetType()), + createdAt: Value.absentIfNull(asset.fileCreatedAt), + updatedAt: Value.absentIfNull(asset.fileModifiedAt), + durationInSeconds: const Value(0), + checksum: Value(asset.checksum), + isFavorite: Value(asset.isFavorite), + ownerId: Value(asset.ownerId), + localDateTime: Value(asset.localDateTime), + thumbHash: Value(asset.thumbhash), + deletedAt: Value(asset.deletedAt), + visibility: Value(asset.visibility.toAssetVisibility()), + ); + + batch.insert( + _db.remoteAssetEntity, + companion.copyWith(id: Value(asset.id)), + onConflict: DoUpdate((_) => companion), + ); + } + }); + + Future _deleteAssetsV1(Iterable assets) => + _db.batch((batch) { + for (final asset in assets) { + batch.delete( + _db.remoteAssetEntity, + RemoteAssetEntityCompanion(id: Value(asset.assetId)), + ); + } + }); + + Future _updateAssetExifV1(Iterable data) => + _db.batch((batch) { + for (final exif in data) { + final companion = RemoteExifEntityCompanion( + city: Value(exif.city), + state: Value(exif.state), + country: Value(exif.country), + dateTimeOriginal: Value(exif.dateTimeOriginal), + description: Value(exif.description), + height: Value(exif.exifImageHeight), + width: Value(exif.exifImageWidth), + exposureTime: Value(exif.exposureTime), + fNumber: Value(exif.fNumber), + fileSize: Value(exif.fileSizeInByte), + focalLength: Value(exif.focalLength), + latitude: Value(exif.latitude), + longitude: Value(exif.longitude), + iso: Value(exif.iso), + make: Value(exif.make), + model: Value(exif.model), + orientation: Value(exif.orientation), + timeZone: Value(exif.timeZone), + rating: Value(exif.rating), + projectionType: Value(exif.projectionType), + ); + + batch.insert( + _db.remoteExifEntity, + companion.copyWith(assetId: Value(exif.assetId)), + onConflict: DoUpdate((_) => companion), + ); + } + }); +} + +extension on SyncAssetV1TypeEnum { + AssetType toAssetType() => switch (this) { + SyncAssetV1TypeEnum.IMAGE => AssetType.image, + SyncAssetV1TypeEnum.VIDEO => AssetType.video, + SyncAssetV1TypeEnum.AUDIO => AssetType.audio, + SyncAssetV1TypeEnum.OTHER => AssetType.other, + _ => throw Exception('Unknown SyncAssetV1TypeEnum value: $this'), + }; +} + +extension on SyncAssetV1VisibilityEnum { + AssetVisibility toAssetVisibility() => switch (this) { + SyncAssetV1VisibilityEnum.timeline => AssetVisibility.timeline, + SyncAssetV1VisibilityEnum.hidden => AssetVisibility.hidden, + SyncAssetV1VisibilityEnum.archive => AssetVisibility.archive, + SyncAssetV1VisibilityEnum.locked => AssetVisibility.locked, + _ => throw Exception('Unknown SyncAssetV1VisibilityEnum value: $this'), + }; } diff --git a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart index da0bea157..3ff0b12b9 100644 --- a/mobile/lib/presentation/pages/dev/feat_in_development.page.dart +++ b/mobile/lib/presentation/pages/dev/feat_in_development.page.dart @@ -53,11 +53,38 @@ final _features = [ await db.localAlbumAssetEntity.deleteAll(); }, ), + _Feature( + name: 'Clear Remote Data', + icon: Icons.delete_sweep_rounded, + onTap: (_, ref) async { + final db = ref.read(driftProvider); + await db.remoteAssetEntity.deleteAll(); + await db.remoteExifEntity.deleteAll(); + }, + ), _Feature( name: 'Local Media Summary', icon: Icons.table_chart_rounded, onTap: (ctx, _) => ctx.pushRoute(const LocalMediaSummaryRoute()), ), + _Feature( + name: 'Remote Media Summary', + icon: Icons.summarize_rounded, + onTap: (ctx, _) => ctx.pushRoute(const RemoteMediaSummaryRoute()), + ), + _Feature( + name: 'Reset Sqlite', + icon: Icons.table_view_rounded, + onTap: (_, ref) async { + final drift = ref.read(driftProvider); + // ignore: invalid_use_of_protected_member, invalid_use_of_visible_for_testing_member + final migrator = drift.createMigrator(); + for (final entity in drift.allSchemaEntities) { + await migrator.drop(entity); + await migrator.create(entity); + } + }, + ), ]; @RoutePage() diff --git a/mobile/lib/presentation/pages/dev/local_media_stat.page.dart b/mobile/lib/presentation/pages/dev/media_stat.page.dart similarity index 76% rename from mobile/lib/presentation/pages/dev/local_media_stat.page.dart rename to mobile/lib/presentation/pages/dev/media_stat.page.dart index b42cae84f..5debeff31 100644 --- a/mobile/lib/presentation/pages/dev/local_media_stat.page.dart +++ b/mobile/lib/presentation/pages/dev/media_stat.page.dart @@ -1,3 +1,5 @@ +// ignore_for_file: prefer-single-widget-per-file + import 'package:auto_route/auto_route.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; @@ -8,7 +10,40 @@ import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; -final _stats = [ +class _Stat { + const _Stat({required this.name, required this.load}); + + final String name; + final Future Function(Drift _) load; +} + +class _Summary extends StatelessWidget { + final String name; + final Future countFuture; + + const _Summary({required this.name, required this.countFuture}); + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: countFuture, + builder: (ctx, snapshot) { + final Widget subtitle; + + if (snapshot.connectionState == ConnectionState.waiting) { + subtitle = const CircularProgressIndicator(); + } else if (snapshot.hasError) { + subtitle = const Icon(Icons.error_rounded); + } else { + subtitle = Text('${snapshot.data ?? 0}'); + } + return ListTile(title: Text(name), trailing: subtitle); + }, + ); + } +} + +final _localStats = [ _Stat( name: 'Local Assets', load: (db) => db.managers.localAssetEntity.count(), @@ -36,11 +71,11 @@ class LocalMediaSummaryPage extends StatelessWidget { slivers: [ SliverList.builder( itemBuilder: (_, index) { - final stat = _stats[index]; + final stat = _localStats[index]; final countFuture = stat.load(db); return _Summary(name: stat.name, countFuture: countFuture); }, - itemCount: _stats.length, + itemCount: _localStats.length, ), SliverToBoxAdapter( child: Column( @@ -90,36 +125,43 @@ class LocalMediaSummaryPage extends StatelessWidget { } } -// ignore: prefer-single-widget-per-file -class _Summary extends StatelessWidget { - final String name; - final Future countFuture; +final _remoteStats = [ + _Stat( + name: 'Remote Assets', + load: (db) => db.managers.remoteAssetEntity.count(), + ), + _Stat( + name: 'Exif Entities', + load: (db) => db.managers.remoteExifEntity.count(), + ), +]; - const _Summary({required this.name, required this.countFuture}); +@RoutePage() +class RemoteMediaSummaryPage extends StatelessWidget { + const RemoteMediaSummaryPage({super.key}); @override Widget build(BuildContext context) { - return FutureBuilder( - future: countFuture, - builder: (ctx, snapshot) { - final Widget subtitle; + return Scaffold( + appBar: AppBar(title: const Text('Remote Media Summary')), + body: Consumer( + builder: (ctx, ref, __) { + final db = ref.watch(driftProvider); - if (snapshot.connectionState == ConnectionState.waiting) { - subtitle = const CircularProgressIndicator(); - } else if (snapshot.hasError) { - subtitle = const Icon(Icons.error_rounded); - } else { - subtitle = Text('${snapshot.data ?? 0}'); - } - return ListTile(title: Text(name), trailing: subtitle); - }, + return CustomScrollView( + slivers: [ + SliverList.builder( + itemBuilder: (_, index) { + final stat = _remoteStats[index]; + final countFuture = stat.load(db); + return _Summary(name: stat.name, countFuture: countFuture); + }, + itemCount: _remoteStats.length, + ), + ], + ); + }, + ), ); } } - -class _Stat { - const _Stat({required this.name, required this.load}); - - final String name; - final Future Function(Drift _) load; -} diff --git a/mobile/lib/repositories/auth.repository.dart b/mobile/lib/repositories/auth.repository.dart index f9e82e163..01d2684fa 100644 --- a/mobile/lib/repositories/auth.repository.dart +++ b/mobile/lib/repositories/auth.repository.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:drift/drift.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/album.entity.dart'; @@ -8,17 +9,22 @@ import 'package:immich_mobile/entities/etag.entity.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/exif.entity.dart'; import 'package:immich_mobile/infrastructure/entities/user.entity.dart'; +import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/interfaces/auth.interface.dart'; import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart'; import 'package:immich_mobile/providers/db.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/repositories/database.repository.dart'; final authRepositoryProvider = Provider( - (ref) => AuthRepository(ref.watch(dbProvider)), + (ref) => + AuthRepository(ref.watch(dbProvider), drift: ref.watch(driftProvider)), ); class AuthRepository extends DatabaseRepository implements IAuthRepository { - AuthRepository(super.db); + final Drift _drift; + + AuthRepository(super.db, {required Drift drift}) : _drift = drift; @override Future clearLocalData() { @@ -29,6 +35,8 @@ class AuthRepository extends DatabaseRepository implements IAuthRepository { db.albums.clear(), db.eTags.clear(), db.users.clear(), + _drift.remoteAssetEntity.deleteAll(), + _drift.remoteExifEntity.deleteAll(), ]); }); } diff --git a/mobile/lib/routing/router.dart b/mobile/lib/routing/router.dart index a6e1d89ff..1f14aaa5b 100644 --- a/mobile/lib/routing/router.dart +++ b/mobile/lib/routing/router.dart @@ -64,7 +64,7 @@ import 'package:immich_mobile/pages/search/recently_taken.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/presentation/pages/dev/feat_in_development.page.dart'; -import 'package:immich_mobile/presentation/pages/dev/local_media_stat.page.dart'; +import 'package:immich_mobile/presentation/pages/dev/media_stat.page.dart'; import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/gallery_permission.provider.dart'; import 'package:immich_mobile/routing/auth_guard.dart'; @@ -326,5 +326,9 @@ class AppRouter extends RootStackRouter { page: LocalMediaSummaryRoute.page, guards: [_authGuard, _duplicateGuard], ), + AutoRoute( + page: RemoteMediaSummaryRoute.page, + guards: [_authGuard, _duplicateGuard], + ), ]; } diff --git a/mobile/lib/routing/router.gr.dart b/mobile/lib/routing/router.gr.dart index 57fb8cef8..0c57949f0 100644 --- a/mobile/lib/routing/router.gr.dart +++ b/mobile/lib/routing/router.gr.dart @@ -1356,6 +1356,22 @@ class RecentlyTakenRoute extends PageRouteInfo { ); } +/// generated route for +/// [RemoteMediaSummaryPage] +class RemoteMediaSummaryRoute extends PageRouteInfo { + const RemoteMediaSummaryRoute({List? children}) + : super(RemoteMediaSummaryRoute.name, initialChildren: children); + + static const String name = 'RemoteMediaSummaryRoute'; + + static PageInfo page = PageInfo( + name, + builder: (data) { + return const RemoteMediaSummaryPage(); + }, + ); +} + /// generated route for /// [SearchPage] class SearchRoute extends PageRouteInfo { diff --git a/mobile/makefile b/mobile/makefile index b797a6592..ec0d08f08 100644 --- a/mobile/makefile +++ b/mobile/makefile @@ -21,7 +21,7 @@ create_splash: build_release_android: flutter build appbundle -migrations: +migration: dart run drift_dev make-migrations translation: