fix: user profile images not working in beta timeline (#20203)

* fix user icons in album view

* revert updateUsersV1 change

* fix: UserDto merge issues

* fix: update user entity

* revert what I thought were merge issues

turns out drift cant figure out when it needs to gen a file...

* fix removed line

* handle defaults for older servers

* feat: checkpoint migrations

* fix: use parenthesis instead of brackets

* Update 1753800911775-ProfileImageCheckpointRemoval.ts

* fix: sync stream updateUsersV1
This commit is contained in:
Brandon Wees 2025-07-30 11:09:28 -05:00 committed by GitHub
parent da5deffd03
commit 097e132fba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
29 changed files with 7069 additions and 282 deletions

File diff suppressed because one or more lines are too long

View File

@ -11,7 +11,6 @@ class UserDto {
final bool isAdmin; final bool isAdmin;
final DateTime updatedAt; final DateTime updatedAt;
final String? profileImagePath;
final AvatarColor avatarColor; final AvatarColor avatarColor;
final bool memoryEnabled; final bool memoryEnabled;
@ -25,18 +24,22 @@ class UserDto {
bool get hasQuota => quotaSizeInBytes > 0; bool get hasQuota => quotaSizeInBytes > 0;
final bool hasProfileImage;
final DateTime profileChangedAt;
const UserDto({ const UserDto({
required this.id, required this.id,
required this.email, required this.email,
required this.name, required this.name,
required this.isAdmin, required this.isAdmin,
required this.updatedAt, required this.updatedAt,
this.profileImagePath, required this.profileChangedAt,
this.avatarColor = AvatarColor.primary, this.avatarColor = AvatarColor.primary,
this.memoryEnabled = true, this.memoryEnabled = true,
this.inTimeline = false, this.inTimeline = false,
this.isPartnerSharedBy = false, this.isPartnerSharedBy = false,
this.isPartnerSharedWith = false, this.isPartnerSharedWith = false,
this.hasProfileImage = false,
this.quotaUsageInBytes = 0, this.quotaUsageInBytes = 0,
this.quotaSizeInBytes = 0, this.quotaSizeInBytes = 0,
}); });
@ -49,14 +52,13 @@ email: $email,
name: $name, name: $name,
isAdmin: $isAdmin, isAdmin: $isAdmin,
updatedAt: $updatedAt, updatedAt: $updatedAt,
profileImagePath: ${profileImagePath ?? '<NA>'},
avatarColor: $avatarColor, avatarColor: $avatarColor,
memoryEnabled: $memoryEnabled, memoryEnabled: $memoryEnabled,
inTimeline: $inTimeline, inTimeline: $inTimeline,
isPartnerSharedBy: $isPartnerSharedBy, isPartnerSharedBy: $isPartnerSharedBy,
isPartnerSharedWith: $isPartnerSharedWith, isPartnerSharedWith: $isPartnerSharedWith,
quotaUsageInBytes: $quotaUsageInBytes, hasProfileImage: $hasProfileImage
quotaSizeInBytes: $quotaSizeInBytes, profileChangedAt: $profileChangedAt
}'''; }''';
} }
@ -66,28 +68,26 @@ quotaSizeInBytes: $quotaSizeInBytes,
String? name, String? name,
bool? isAdmin, bool? isAdmin,
DateTime? updatedAt, DateTime? updatedAt,
String? profileImagePath,
AvatarColor? avatarColor, AvatarColor? avatarColor,
bool? memoryEnabled, bool? memoryEnabled,
bool? inTimeline, bool? inTimeline,
bool? isPartnerSharedBy, bool? isPartnerSharedBy,
bool? isPartnerSharedWith, bool? isPartnerSharedWith,
int? quotaUsageInBytes, bool? hasProfileImage,
int? quotaSizeInBytes, DateTime? profileChangedAt,
}) => UserDto( }) => UserDto(
id: id ?? this.id, id: id ?? this.id,
email: email ?? this.email, email: email ?? this.email,
name: name ?? this.name, name: name ?? this.name,
isAdmin: isAdmin ?? this.isAdmin, isAdmin: isAdmin ?? this.isAdmin,
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
profileImagePath: profileImagePath ?? this.profileImagePath,
avatarColor: avatarColor ?? this.avatarColor, avatarColor: avatarColor ?? this.avatarColor,
memoryEnabled: memoryEnabled ?? this.memoryEnabled, memoryEnabled: memoryEnabled ?? this.memoryEnabled,
inTimeline: inTimeline ?? this.inTimeline, inTimeline: inTimeline ?? this.inTimeline,
isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy, isPartnerSharedBy: isPartnerSharedBy ?? this.isPartnerSharedBy,
isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith, isPartnerSharedWith: isPartnerSharedWith ?? this.isPartnerSharedWith,
quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes, hasProfileImage: hasProfileImage ?? this.hasProfileImage,
quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes, profileChangedAt: profileChangedAt ?? this.profileChangedAt,
); );
@override @override
@ -101,12 +101,11 @@ quotaSizeInBytes: $quotaSizeInBytes,
other.name == name && other.name == name &&
other.isPartnerSharedBy == isPartnerSharedBy && other.isPartnerSharedBy == isPartnerSharedBy &&
other.isPartnerSharedWith == isPartnerSharedWith && other.isPartnerSharedWith == isPartnerSharedWith &&
other.profileImagePath == profileImagePath &&
other.isAdmin == isAdmin && other.isAdmin == isAdmin &&
other.memoryEnabled == memoryEnabled && other.memoryEnabled == memoryEnabled &&
other.inTimeline == inTimeline && other.inTimeline == inTimeline &&
other.quotaUsageInBytes == quotaUsageInBytes && other.hasProfileImage == hasProfileImage &&
other.quotaSizeInBytes == quotaSizeInBytes; other.profileChangedAt.isAtSameMomentAs(profileChangedAt);
} }
@override @override
@ -116,14 +115,13 @@ quotaSizeInBytes: $quotaSizeInBytes,
email.hashCode ^ email.hashCode ^
updatedAt.hashCode ^ updatedAt.hashCode ^
isAdmin.hashCode ^ isAdmin.hashCode ^
profileImagePath.hashCode ^
avatarColor.hashCode ^ avatarColor.hashCode ^
memoryEnabled.hashCode ^ memoryEnabled.hashCode ^
inTimeline.hashCode ^ inTimeline.hashCode ^
isPartnerSharedBy.hashCode ^ isPartnerSharedBy.hashCode ^
isPartnerSharedWith.hashCode ^ isPartnerSharedWith.hashCode ^
quotaUsageInBytes.hashCode ^ hasProfileImage.hashCode ^
quotaSizeInBytes.hashCode; profileChangedAt.hashCode;
} }
class PartnerUserDto { class PartnerUserDto {

View File

@ -45,7 +45,7 @@ class UserService {
Future<String?> createProfileImage(String name, Uint8List image) async { Future<String?> createProfileImage(String name, Uint8List image) async {
try { try {
final path = await _userApiRepository.createProfileImage(name: name, data: image); final path = await _userApiRepository.createProfileImage(name: name, data: image);
final updatedUser = getMyUser().copyWith(profileImagePath: path); final updatedUser = getMyUser();
await _storeService.put(StoreKey.currentUser, updatedUser); await _storeService.put(StoreKey.currentUser, updatedUser);
await _isarUserRepository.update(updatedUser); await _isarUserRepository.update(updatedUser);
return path; return path;

View File

@ -50,12 +50,10 @@ class User {
isAdmin: dto.isAdmin, isAdmin: dto.isAdmin,
isPartnerSharedBy: dto.isPartnerSharedBy, isPartnerSharedBy: dto.isPartnerSharedBy,
isPartnerSharedWith: dto.isPartnerSharedWith, isPartnerSharedWith: dto.isPartnerSharedWith,
profileImagePath: dto.profileImagePath ?? "", profileImagePath: dto.hasProfileImage ? "HAS_PROFILE_IMAGE" : "",
avatarColor: dto.avatarColor, avatarColor: dto.avatarColor,
memoryEnabled: dto.memoryEnabled, memoryEnabled: dto.memoryEnabled,
inTimeline: dto.inTimeline, inTimeline: dto.inTimeline,
quotaUsageInBytes: dto.quotaUsageInBytes,
quotaSizeInBytes: dto.quotaSizeInBytes,
); );
UserDto toDto() => UserDto( UserDto toDto() => UserDto(
@ -64,12 +62,13 @@ class User {
name: name, name: name,
isAdmin: isAdmin, isAdmin: isAdmin,
updatedAt: updatedAt, updatedAt: updatedAt,
profileImagePath: profileImagePath.isEmpty ? null : profileImagePath,
avatarColor: avatarColor, avatarColor: avatarColor,
memoryEnabled: memoryEnabled, memoryEnabled: memoryEnabled,
inTimeline: inTimeline, inTimeline: inTimeline,
isPartnerSharedBy: isPartnerSharedBy, isPartnerSharedBy: isPartnerSharedBy,
isPartnerSharedWith: isPartnerSharedWith, isPartnerSharedWith: isPartnerSharedWith,
hasProfileImage: profileImagePath.isNotEmpty,
profileChangedAt: updatedAt,
quotaUsageInBytes: quotaUsageInBytes, quotaUsageInBytes: quotaUsageInBytes,
quotaSizeInBytes: quotaSizeInBytes, quotaSizeInBytes: quotaSizeInBytes,
); );
@ -82,11 +81,11 @@ class UserEntity extends Table with DriftDefaultsMixin {
TextColumn get name => text()(); TextColumn get name => text()();
BoolColumn get isAdmin => boolean().withDefault(const Constant(false))(); BoolColumn get isAdmin => boolean().withDefault(const Constant(false))();
TextColumn get email => text()(); TextColumn get email => text()();
TextColumn get profileImagePath => text().nullable()();
BoolColumn get hasProfileImage => boolean().withDefault(const Constant(false))();
DateTimeColumn get profileChangedAt => dateTime().withDefault(currentDateAndTime)();
DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)(); DateTimeColumn get updatedAt => dateTime().withDefault(currentDateAndTime)();
// Quota
IntColumn get quotaSizeInBytes => integer().nullable()();
IntColumn get quotaUsageInBytes => integer().withDefault(const Constant(0))();
@override @override
Set<Column> get primaryKey => {id}; Set<Column> get primaryKey => {id};

View File

@ -12,10 +12,9 @@ typedef $$UserEntityTableCreateCompanionBuilder =
required String name, required String name,
i0.Value<bool> isAdmin, i0.Value<bool> isAdmin,
required String email, required String email,
i0.Value<String?> profileImagePath, i0.Value<bool> hasProfileImage,
i0.Value<DateTime> profileChangedAt,
i0.Value<DateTime> updatedAt, i0.Value<DateTime> updatedAt,
i0.Value<int?> quotaSizeInBytes,
i0.Value<int> quotaUsageInBytes,
}); });
typedef $$UserEntityTableUpdateCompanionBuilder = typedef $$UserEntityTableUpdateCompanionBuilder =
i1.UserEntityCompanion Function({ i1.UserEntityCompanion Function({
@ -23,10 +22,9 @@ typedef $$UserEntityTableUpdateCompanionBuilder =
i0.Value<String> name, i0.Value<String> name,
i0.Value<bool> isAdmin, i0.Value<bool> isAdmin,
i0.Value<String> email, i0.Value<String> email,
i0.Value<String?> profileImagePath, i0.Value<bool> hasProfileImage,
i0.Value<DateTime> profileChangedAt,
i0.Value<DateTime> updatedAt, i0.Value<DateTime> updatedAt,
i0.Value<int?> quotaSizeInBytes,
i0.Value<int> quotaUsageInBytes,
}); });
class $$UserEntityTableFilterComposer class $$UserEntityTableFilterComposer
@ -58,8 +56,13 @@ class $$UserEntityTableFilterComposer
builder: (column) => i0.ColumnFilters(column), builder: (column) => i0.ColumnFilters(column),
); );
i0.ColumnFilters<String> get profileImagePath => $composableBuilder( i0.ColumnFilters<bool> get hasProfileImage => $composableBuilder(
column: $table.profileImagePath, column: $table.hasProfileImage,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<DateTime> get profileChangedAt => $composableBuilder(
column: $table.profileChangedAt,
builder: (column) => i0.ColumnFilters(column), builder: (column) => i0.ColumnFilters(column),
); );
@ -67,16 +70,6 @@ class $$UserEntityTableFilterComposer
column: $table.updatedAt, column: $table.updatedAt,
builder: (column) => i0.ColumnFilters(column), builder: (column) => i0.ColumnFilters(column),
); );
i0.ColumnFilters<int> get quotaSizeInBytes => $composableBuilder(
column: $table.quotaSizeInBytes,
builder: (column) => i0.ColumnFilters(column),
);
i0.ColumnFilters<int> get quotaUsageInBytes => $composableBuilder(
column: $table.quotaUsageInBytes,
builder: (column) => i0.ColumnFilters(column),
);
} }
class $$UserEntityTableOrderingComposer class $$UserEntityTableOrderingComposer
@ -108,8 +101,13 @@ class $$UserEntityTableOrderingComposer
builder: (column) => i0.ColumnOrderings(column), builder: (column) => i0.ColumnOrderings(column),
); );
i0.ColumnOrderings<String> get profileImagePath => $composableBuilder( i0.ColumnOrderings<bool> get hasProfileImage => $composableBuilder(
column: $table.profileImagePath, column: $table.hasProfileImage,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<DateTime> get profileChangedAt => $composableBuilder(
column: $table.profileChangedAt,
builder: (column) => i0.ColumnOrderings(column), builder: (column) => i0.ColumnOrderings(column),
); );
@ -117,16 +115,6 @@ class $$UserEntityTableOrderingComposer
column: $table.updatedAt, column: $table.updatedAt,
builder: (column) => i0.ColumnOrderings(column), builder: (column) => i0.ColumnOrderings(column),
); );
i0.ColumnOrderings<int> get quotaSizeInBytes => $composableBuilder(
column: $table.quotaSizeInBytes,
builder: (column) => i0.ColumnOrderings(column),
);
i0.ColumnOrderings<int> get quotaUsageInBytes => $composableBuilder(
column: $table.quotaUsageInBytes,
builder: (column) => i0.ColumnOrderings(column),
);
} }
class $$UserEntityTableAnnotationComposer class $$UserEntityTableAnnotationComposer
@ -150,23 +138,18 @@ class $$UserEntityTableAnnotationComposer
i0.GeneratedColumn<String> get email => i0.GeneratedColumn<String> get email =>
$composableBuilder(column: $table.email, builder: (column) => column); $composableBuilder(column: $table.email, builder: (column) => column);
i0.GeneratedColumn<String> get profileImagePath => $composableBuilder( i0.GeneratedColumn<bool> get hasProfileImage => $composableBuilder(
column: $table.profileImagePath, column: $table.hasProfileImage,
builder: (column) => column,
);
i0.GeneratedColumn<DateTime> get profileChangedAt => $composableBuilder(
column: $table.profileChangedAt,
builder: (column) => column, builder: (column) => column,
); );
i0.GeneratedColumn<DateTime> get updatedAt => i0.GeneratedColumn<DateTime> get updatedAt =>
$composableBuilder(column: $table.updatedAt, builder: (column) => column); $composableBuilder(column: $table.updatedAt, builder: (column) => column);
i0.GeneratedColumn<int> get quotaSizeInBytes => $composableBuilder(
column: $table.quotaSizeInBytes,
builder: (column) => column,
);
i0.GeneratedColumn<int> get quotaUsageInBytes => $composableBuilder(
column: $table.quotaUsageInBytes,
builder: (column) => column,
);
} }
class $$UserEntityTableTableManager class $$UserEntityTableTableManager
@ -210,19 +193,17 @@ class $$UserEntityTableTableManager
i0.Value<String> name = const i0.Value.absent(), i0.Value<String> name = const i0.Value.absent(),
i0.Value<bool> isAdmin = const i0.Value.absent(), i0.Value<bool> isAdmin = const i0.Value.absent(),
i0.Value<String> email = const i0.Value.absent(), i0.Value<String> email = const i0.Value.absent(),
i0.Value<String?> profileImagePath = const i0.Value.absent(), i0.Value<bool> hasProfileImage = const i0.Value.absent(),
i0.Value<DateTime> profileChangedAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(), i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> quotaSizeInBytes = const i0.Value.absent(),
i0.Value<int> quotaUsageInBytes = const i0.Value.absent(),
}) => i1.UserEntityCompanion( }) => i1.UserEntityCompanion(
id: id, id: id,
name: name, name: name,
isAdmin: isAdmin, isAdmin: isAdmin,
email: email, email: email,
profileImagePath: profileImagePath, hasProfileImage: hasProfileImage,
profileChangedAt: profileChangedAt,
updatedAt: updatedAt, updatedAt: updatedAt,
quotaSizeInBytes: quotaSizeInBytes,
quotaUsageInBytes: quotaUsageInBytes,
), ),
createCompanionCallback: createCompanionCallback:
({ ({
@ -230,19 +211,17 @@ class $$UserEntityTableTableManager
required String name, required String name,
i0.Value<bool> isAdmin = const i0.Value.absent(), i0.Value<bool> isAdmin = const i0.Value.absent(),
required String email, required String email,
i0.Value<String?> profileImagePath = const i0.Value.absent(), i0.Value<bool> hasProfileImage = const i0.Value.absent(),
i0.Value<DateTime> profileChangedAt = const i0.Value.absent(),
i0.Value<DateTime> updatedAt = const i0.Value.absent(), i0.Value<DateTime> updatedAt = const i0.Value.absent(),
i0.Value<int?> quotaSizeInBytes = const i0.Value.absent(),
i0.Value<int> quotaUsageInBytes = const i0.Value.absent(),
}) => i1.UserEntityCompanion.insert( }) => i1.UserEntityCompanion.insert(
id: id, id: id,
name: name, name: name,
isAdmin: isAdmin, isAdmin: isAdmin,
email: email, email: email,
profileImagePath: profileImagePath, hasProfileImage: hasProfileImage,
profileChangedAt: profileChangedAt,
updatedAt: updatedAt, updatedAt: updatedAt,
quotaSizeInBytes: quotaSizeInBytes,
quotaUsageInBytes: quotaUsageInBytes,
), ),
withReferenceMapper: (p0) => p0 withReferenceMapper: (p0) => p0
.map((e) => (e.readTable(table), i0.BaseReferences(db, table, e))) .map((e) => (e.readTable(table), i0.BaseReferences(db, table, e)))
@ -326,16 +305,32 @@ class $UserEntityTable extends i2.UserEntity
type: i0.DriftSqlType.string, type: i0.DriftSqlType.string,
requiredDuringInsert: true, requiredDuringInsert: true,
); );
static const i0.VerificationMeta _profileImagePathMeta = static const i0.VerificationMeta _hasProfileImageMeta =
const i0.VerificationMeta('profileImagePath'); const i0.VerificationMeta('hasProfileImage');
@override @override
late final i0.GeneratedColumn<String> profileImagePath = late final i0.GeneratedColumn<bool> hasProfileImage =
i0.GeneratedColumn<String>( i0.GeneratedColumn<bool>(
'profile_image_path', 'has_profile_image',
aliasedName, aliasedName,
true, false,
type: i0.DriftSqlType.string, type: i0.DriftSqlType.bool,
requiredDuringInsert: false, requiredDuringInsert: false,
defaultConstraints: i0.GeneratedColumn.constraintIsAlways(
'CHECK ("has_profile_image" IN (0, 1))',
),
defaultValue: const i3.Constant(false),
);
static const i0.VerificationMeta _profileChangedAtMeta =
const i0.VerificationMeta('profileChangedAt');
@override
late final i0.GeneratedColumn<DateTime> profileChangedAt =
i0.GeneratedColumn<DateTime>(
'profile_changed_at',
aliasedName,
false,
type: i0.DriftSqlType.dateTime,
requiredDuringInsert: false,
defaultValue: i3.currentDateAndTime,
); );
static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta( static const i0.VerificationMeta _updatedAtMeta = const i0.VerificationMeta(
'updatedAt', 'updatedAt',
@ -350,38 +345,15 @@ class $UserEntityTable extends i2.UserEntity
requiredDuringInsert: false, requiredDuringInsert: false,
defaultValue: i3.currentDateAndTime, defaultValue: i3.currentDateAndTime,
); );
static const i0.VerificationMeta _quotaSizeInBytesMeta =
const i0.VerificationMeta('quotaSizeInBytes');
@override
late final i0.GeneratedColumn<int> quotaSizeInBytes = i0.GeneratedColumn<int>(
'quota_size_in_bytes',
aliasedName,
true,
type: i0.DriftSqlType.int,
requiredDuringInsert: false,
);
static const i0.VerificationMeta _quotaUsageInBytesMeta =
const i0.VerificationMeta('quotaUsageInBytes');
@override
late final i0.GeneratedColumn<int> quotaUsageInBytes =
i0.GeneratedColumn<int>(
'quota_usage_in_bytes',
aliasedName,
false,
type: i0.DriftSqlType.int,
requiredDuringInsert: false,
defaultValue: const i3.Constant(0),
);
@override @override
List<i0.GeneratedColumn> get $columns => [ List<i0.GeneratedColumn> get $columns => [
id, id,
name, name,
isAdmin, isAdmin,
email, email,
profileImagePath, hasProfileImage,
profileChangedAt,
updatedAt, updatedAt,
quotaSizeInBytes,
quotaUsageInBytes,
]; ];
@override @override
String get aliasedName => _alias ?? actualTableName; String get aliasedName => _alias ?? actualTableName;
@ -422,12 +394,21 @@ class $UserEntityTable extends i2.UserEntity
} else if (isInserting) { } else if (isInserting) {
context.missing(_emailMeta); context.missing(_emailMeta);
} }
if (data.containsKey('profile_image_path')) { if (data.containsKey('has_profile_image')) {
context.handle( context.handle(
_profileImagePathMeta, _hasProfileImageMeta,
profileImagePath.isAcceptableOrUnknown( hasProfileImage.isAcceptableOrUnknown(
data['profile_image_path']!, data['has_profile_image']!,
_profileImagePathMeta, _hasProfileImageMeta,
),
);
}
if (data.containsKey('profile_changed_at')) {
context.handle(
_profileChangedAtMeta,
profileChangedAt.isAcceptableOrUnknown(
data['profile_changed_at']!,
_profileChangedAtMeta,
), ),
); );
} }
@ -437,24 +418,6 @@ class $UserEntityTable extends i2.UserEntity
updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta), updatedAt.isAcceptableOrUnknown(data['updated_at']!, _updatedAtMeta),
); );
} }
if (data.containsKey('quota_size_in_bytes')) {
context.handle(
_quotaSizeInBytesMeta,
quotaSizeInBytes.isAcceptableOrUnknown(
data['quota_size_in_bytes']!,
_quotaSizeInBytesMeta,
),
);
}
if (data.containsKey('quota_usage_in_bytes')) {
context.handle(
_quotaUsageInBytesMeta,
quotaUsageInBytes.isAcceptableOrUnknown(
data['quota_usage_in_bytes']!,
_quotaUsageInBytesMeta,
),
);
}
return context; return context;
} }
@ -480,22 +443,18 @@ class $UserEntityTable extends i2.UserEntity
i0.DriftSqlType.string, i0.DriftSqlType.string,
data['${effectivePrefix}email'], data['${effectivePrefix}email'],
)!, )!,
profileImagePath: attachedDatabase.typeMapping.read( hasProfileImage: attachedDatabase.typeMapping.read(
i0.DriftSqlType.string, i0.DriftSqlType.bool,
data['${effectivePrefix}profile_image_path'], data['${effectivePrefix}has_profile_image'],
), )!,
profileChangedAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime,
data['${effectivePrefix}profile_changed_at'],
)!,
updatedAt: attachedDatabase.typeMapping.read( updatedAt: attachedDatabase.typeMapping.read(
i0.DriftSqlType.dateTime, i0.DriftSqlType.dateTime,
data['${effectivePrefix}updated_at'], data['${effectivePrefix}updated_at'],
)!, )!,
quotaSizeInBytes: attachedDatabase.typeMapping.read(
i0.DriftSqlType.int,
data['${effectivePrefix}quota_size_in_bytes'],
),
quotaUsageInBytes: attachedDatabase.typeMapping.read(
i0.DriftSqlType.int,
data['${effectivePrefix}quota_usage_in_bytes'],
)!,
); );
} }
@ -516,19 +475,17 @@ class UserEntityData extends i0.DataClass
final String name; final String name;
final bool isAdmin; final bool isAdmin;
final String email; final String email;
final String? profileImagePath; final bool hasProfileImage;
final DateTime profileChangedAt;
final DateTime updatedAt; final DateTime updatedAt;
final int? quotaSizeInBytes;
final int quotaUsageInBytes;
const UserEntityData({ const UserEntityData({
required this.id, required this.id,
required this.name, required this.name,
required this.isAdmin, required this.isAdmin,
required this.email, required this.email,
this.profileImagePath, required this.hasProfileImage,
required this.profileChangedAt,
required this.updatedAt, required this.updatedAt,
this.quotaSizeInBytes,
required this.quotaUsageInBytes,
}); });
@override @override
Map<String, i0.Expression> toColumns(bool nullToAbsent) { Map<String, i0.Expression> toColumns(bool nullToAbsent) {
@ -537,14 +494,9 @@ class UserEntityData extends i0.DataClass
map['name'] = i0.Variable<String>(name); map['name'] = i0.Variable<String>(name);
map['is_admin'] = i0.Variable<bool>(isAdmin); map['is_admin'] = i0.Variable<bool>(isAdmin);
map['email'] = i0.Variable<String>(email); map['email'] = i0.Variable<String>(email);
if (!nullToAbsent || profileImagePath != null) { map['has_profile_image'] = i0.Variable<bool>(hasProfileImage);
map['profile_image_path'] = i0.Variable<String>(profileImagePath); map['profile_changed_at'] = i0.Variable<DateTime>(profileChangedAt);
}
map['updated_at'] = i0.Variable<DateTime>(updatedAt); map['updated_at'] = i0.Variable<DateTime>(updatedAt);
if (!nullToAbsent || quotaSizeInBytes != null) {
map['quota_size_in_bytes'] = i0.Variable<int>(quotaSizeInBytes);
}
map['quota_usage_in_bytes'] = i0.Variable<int>(quotaUsageInBytes);
return map; return map;
} }
@ -558,10 +510,9 @@ class UserEntityData extends i0.DataClass
name: serializer.fromJson<String>(json['name']), name: serializer.fromJson<String>(json['name']),
isAdmin: serializer.fromJson<bool>(json['isAdmin']), isAdmin: serializer.fromJson<bool>(json['isAdmin']),
email: serializer.fromJson<String>(json['email']), email: serializer.fromJson<String>(json['email']),
profileImagePath: serializer.fromJson<String?>(json['profileImagePath']), hasProfileImage: serializer.fromJson<bool>(json['hasProfileImage']),
profileChangedAt: serializer.fromJson<DateTime>(json['profileChangedAt']),
updatedAt: serializer.fromJson<DateTime>(json['updatedAt']), updatedAt: serializer.fromJson<DateTime>(json['updatedAt']),
quotaSizeInBytes: serializer.fromJson<int?>(json['quotaSizeInBytes']),
quotaUsageInBytes: serializer.fromJson<int>(json['quotaUsageInBytes']),
); );
} }
@override @override
@ -572,10 +523,9 @@ class UserEntityData extends i0.DataClass
'name': serializer.toJson<String>(name), 'name': serializer.toJson<String>(name),
'isAdmin': serializer.toJson<bool>(isAdmin), 'isAdmin': serializer.toJson<bool>(isAdmin),
'email': serializer.toJson<String>(email), 'email': serializer.toJson<String>(email),
'profileImagePath': serializer.toJson<String?>(profileImagePath), 'hasProfileImage': serializer.toJson<bool>(hasProfileImage),
'profileChangedAt': serializer.toJson<DateTime>(profileChangedAt),
'updatedAt': serializer.toJson<DateTime>(updatedAt), 'updatedAt': serializer.toJson<DateTime>(updatedAt),
'quotaSizeInBytes': serializer.toJson<int?>(quotaSizeInBytes),
'quotaUsageInBytes': serializer.toJson<int>(quotaUsageInBytes),
}; };
} }
@ -584,23 +534,17 @@ class UserEntityData extends i0.DataClass
String? name, String? name,
bool? isAdmin, bool? isAdmin,
String? email, String? email,
i0.Value<String?> profileImagePath = const i0.Value.absent(), bool? hasProfileImage,
DateTime? profileChangedAt,
DateTime? updatedAt, DateTime? updatedAt,
i0.Value<int?> quotaSizeInBytes = const i0.Value.absent(),
int? quotaUsageInBytes,
}) => i1.UserEntityData( }) => i1.UserEntityData(
id: id ?? this.id, id: id ?? this.id,
name: name ?? this.name, name: name ?? this.name,
isAdmin: isAdmin ?? this.isAdmin, isAdmin: isAdmin ?? this.isAdmin,
email: email ?? this.email, email: email ?? this.email,
profileImagePath: profileImagePath.present hasProfileImage: hasProfileImage ?? this.hasProfileImage,
? profileImagePath.value profileChangedAt: profileChangedAt ?? this.profileChangedAt,
: this.profileImagePath,
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
quotaSizeInBytes: quotaSizeInBytes.present
? quotaSizeInBytes.value
: this.quotaSizeInBytes,
quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes,
); );
UserEntityData copyWithCompanion(i1.UserEntityCompanion data) { UserEntityData copyWithCompanion(i1.UserEntityCompanion data) {
return UserEntityData( return UserEntityData(
@ -608,16 +552,13 @@ class UserEntityData extends i0.DataClass
name: data.name.present ? data.name.value : this.name, name: data.name.present ? data.name.value : this.name,
isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin, isAdmin: data.isAdmin.present ? data.isAdmin.value : this.isAdmin,
email: data.email.present ? data.email.value : this.email, email: data.email.present ? data.email.value : this.email,
profileImagePath: data.profileImagePath.present hasProfileImage: data.hasProfileImage.present
? data.profileImagePath.value ? data.hasProfileImage.value
: this.profileImagePath, : this.hasProfileImage,
profileChangedAt: data.profileChangedAt.present
? data.profileChangedAt.value
: this.profileChangedAt,
updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt, updatedAt: data.updatedAt.present ? data.updatedAt.value : this.updatedAt,
quotaSizeInBytes: data.quotaSizeInBytes.present
? data.quotaSizeInBytes.value
: this.quotaSizeInBytes,
quotaUsageInBytes: data.quotaUsageInBytes.present
? data.quotaUsageInBytes.value
: this.quotaUsageInBytes,
); );
} }
@ -628,10 +569,9 @@ class UserEntityData extends i0.DataClass
..write('name: $name, ') ..write('name: $name, ')
..write('isAdmin: $isAdmin, ') ..write('isAdmin: $isAdmin, ')
..write('email: $email, ') ..write('email: $email, ')
..write('profileImagePath: $profileImagePath, ') ..write('hasProfileImage: $hasProfileImage, ')
..write('updatedAt: $updatedAt, ') ..write('profileChangedAt: $profileChangedAt, ')
..write('quotaSizeInBytes: $quotaSizeInBytes, ') ..write('updatedAt: $updatedAt')
..write('quotaUsageInBytes: $quotaUsageInBytes')
..write(')')) ..write(')'))
.toString(); .toString();
} }
@ -642,10 +582,9 @@ class UserEntityData extends i0.DataClass
name, name,
isAdmin, isAdmin,
email, email,
profileImagePath, hasProfileImage,
profileChangedAt,
updatedAt, updatedAt,
quotaSizeInBytes,
quotaUsageInBytes,
); );
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
@ -655,10 +594,9 @@ class UserEntityData extends i0.DataClass
other.name == this.name && other.name == this.name &&
other.isAdmin == this.isAdmin && other.isAdmin == this.isAdmin &&
other.email == this.email && other.email == this.email &&
other.profileImagePath == this.profileImagePath && other.hasProfileImage == this.hasProfileImage &&
other.updatedAt == this.updatedAt && other.profileChangedAt == this.profileChangedAt &&
other.quotaSizeInBytes == this.quotaSizeInBytes && other.updatedAt == this.updatedAt);
other.quotaUsageInBytes == this.quotaUsageInBytes);
} }
class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> { class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
@ -666,29 +604,26 @@ class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
final i0.Value<String> name; final i0.Value<String> name;
final i0.Value<bool> isAdmin; final i0.Value<bool> isAdmin;
final i0.Value<String> email; final i0.Value<String> email;
final i0.Value<String?> profileImagePath; final i0.Value<bool> hasProfileImage;
final i0.Value<DateTime> profileChangedAt;
final i0.Value<DateTime> updatedAt; final i0.Value<DateTime> updatedAt;
final i0.Value<int?> quotaSizeInBytes;
final i0.Value<int> quotaUsageInBytes;
const UserEntityCompanion({ const UserEntityCompanion({
this.id = const i0.Value.absent(), this.id = const i0.Value.absent(),
this.name = const i0.Value.absent(), this.name = const i0.Value.absent(),
this.isAdmin = const i0.Value.absent(), this.isAdmin = const i0.Value.absent(),
this.email = const i0.Value.absent(), this.email = const i0.Value.absent(),
this.profileImagePath = const i0.Value.absent(), this.hasProfileImage = const i0.Value.absent(),
this.profileChangedAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(),
this.quotaSizeInBytes = const i0.Value.absent(),
this.quotaUsageInBytes = const i0.Value.absent(),
}); });
UserEntityCompanion.insert({ UserEntityCompanion.insert({
required String id, required String id,
required String name, required String name,
this.isAdmin = const i0.Value.absent(), this.isAdmin = const i0.Value.absent(),
required String email, required String email,
this.profileImagePath = const i0.Value.absent(), this.hasProfileImage = const i0.Value.absent(),
this.profileChangedAt = const i0.Value.absent(),
this.updatedAt = const i0.Value.absent(), this.updatedAt = const i0.Value.absent(),
this.quotaSizeInBytes = const i0.Value.absent(),
this.quotaUsageInBytes = const i0.Value.absent(),
}) : id = i0.Value(id), }) : id = i0.Value(id),
name = i0.Value(name), name = i0.Value(name),
email = i0.Value(email); email = i0.Value(email);
@ -697,20 +632,18 @@ class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
i0.Expression<String>? name, i0.Expression<String>? name,
i0.Expression<bool>? isAdmin, i0.Expression<bool>? isAdmin,
i0.Expression<String>? email, i0.Expression<String>? email,
i0.Expression<String>? profileImagePath, i0.Expression<bool>? hasProfileImage,
i0.Expression<DateTime>? profileChangedAt,
i0.Expression<DateTime>? updatedAt, i0.Expression<DateTime>? updatedAt,
i0.Expression<int>? quotaSizeInBytes,
i0.Expression<int>? quotaUsageInBytes,
}) { }) {
return i0.RawValuesInsertable({ return i0.RawValuesInsertable({
if (id != null) 'id': id, if (id != null) 'id': id,
if (name != null) 'name': name, if (name != null) 'name': name,
if (isAdmin != null) 'is_admin': isAdmin, if (isAdmin != null) 'is_admin': isAdmin,
if (email != null) 'email': email, if (email != null) 'email': email,
if (profileImagePath != null) 'profile_image_path': profileImagePath, if (hasProfileImage != null) 'has_profile_image': hasProfileImage,
if (profileChangedAt != null) 'profile_changed_at': profileChangedAt,
if (updatedAt != null) 'updated_at': updatedAt, if (updatedAt != null) 'updated_at': updatedAt,
if (quotaSizeInBytes != null) 'quota_size_in_bytes': quotaSizeInBytes,
if (quotaUsageInBytes != null) 'quota_usage_in_bytes': quotaUsageInBytes,
}); });
} }
@ -719,20 +652,18 @@ class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
i0.Value<String>? name, i0.Value<String>? name,
i0.Value<bool>? isAdmin, i0.Value<bool>? isAdmin,
i0.Value<String>? email, i0.Value<String>? email,
i0.Value<String?>? profileImagePath, i0.Value<bool>? hasProfileImage,
i0.Value<DateTime>? profileChangedAt,
i0.Value<DateTime>? updatedAt, i0.Value<DateTime>? updatedAt,
i0.Value<int?>? quotaSizeInBytes,
i0.Value<int>? quotaUsageInBytes,
}) { }) {
return i1.UserEntityCompanion( return i1.UserEntityCompanion(
id: id ?? this.id, id: id ?? this.id,
name: name ?? this.name, name: name ?? this.name,
isAdmin: isAdmin ?? this.isAdmin, isAdmin: isAdmin ?? this.isAdmin,
email: email ?? this.email, email: email ?? this.email,
profileImagePath: profileImagePath ?? this.profileImagePath, hasProfileImage: hasProfileImage ?? this.hasProfileImage,
profileChangedAt: profileChangedAt ?? this.profileChangedAt,
updatedAt: updatedAt ?? this.updatedAt, updatedAt: updatedAt ?? this.updatedAt,
quotaSizeInBytes: quotaSizeInBytes ?? this.quotaSizeInBytes,
quotaUsageInBytes: quotaUsageInBytes ?? this.quotaUsageInBytes,
); );
} }
@ -751,18 +682,15 @@ class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
if (email.present) { if (email.present) {
map['email'] = i0.Variable<String>(email.value); map['email'] = i0.Variable<String>(email.value);
} }
if (profileImagePath.present) { if (hasProfileImage.present) {
map['profile_image_path'] = i0.Variable<String>(profileImagePath.value); map['has_profile_image'] = i0.Variable<bool>(hasProfileImage.value);
}
if (profileChangedAt.present) {
map['profile_changed_at'] = i0.Variable<DateTime>(profileChangedAt.value);
} }
if (updatedAt.present) { if (updatedAt.present) {
map['updated_at'] = i0.Variable<DateTime>(updatedAt.value); map['updated_at'] = i0.Variable<DateTime>(updatedAt.value);
} }
if (quotaSizeInBytes.present) {
map['quota_size_in_bytes'] = i0.Variable<int>(quotaSizeInBytes.value);
}
if (quotaUsageInBytes.present) {
map['quota_usage_in_bytes'] = i0.Variable<int>(quotaUsageInBytes.value);
}
return map; return map;
} }
@ -773,10 +701,9 @@ class UserEntityCompanion extends i0.UpdateCompanion<i1.UserEntityData> {
..write('name: $name, ') ..write('name: $name, ')
..write('isAdmin: $isAdmin, ') ..write('isAdmin: $isAdmin, ')
..write('email: $email, ') ..write('email: $email, ')
..write('profileImagePath: $profileImagePath, ') ..write('hasProfileImage: $hasProfileImage, ')
..write('updatedAt: $updatedAt, ') ..write('profileChangedAt: $profileChangedAt, ')
..write('quotaSizeInBytes: $quotaSizeInBytes, ') ..write('updatedAt: $updatedAt')
..write('quotaUsageInBytes: $quotaUsageInBytes')
..write(')')) ..write(')'))
.toString(); .toString();
} }

View File

@ -66,7 +66,7 @@ class Drift extends $Drift implements IDatabaseRepository {
: super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true))); : super(executor ?? driftDatabase(name: 'immich', native: const DriftNativeOptions(shareAcrossIsolates: true)));
@override @override
int get schemaVersion => 4; int get schemaVersion => 5;
@override @override
MigrationStrategy get migration => MigrationStrategy( MigrationStrategy get migration => MigrationStrategy(
@ -94,6 +94,15 @@ class Drift extends $Drift implements IDatabaseRepository {
// asset_face_entity is added // asset_face_entity is added
await m.create(v4.assetFaceEntity); await m.create(v4.assetFaceEntity);
}, },
from4To5: (m, v5) async {
await m.alterTable(
TableMigration(
v5.userEntity,
newColumns: [v5.userEntity.hasProfileImage, v5.userEntity.profileChangedAt],
columnTransformer: {v5.userEntity.profileChangedAt: currentDateAndTime},
),
);
},
), ),
); );

View File

@ -1954,10 +1954,376 @@ i1.GeneratedColumn<String> _column_83(String aliasedName) =>
false, false,
type: i1.DriftSqlType.string, type: i1.DriftSqlType.string,
); );
final class Schema5 extends i0.VersionedSchema {
Schema5({required super.database}) : super(version: 5);
@override
late final List<i1.DatabaseSchemaEntity> entities = [
userEntity,
remoteAssetEntity,
stackEntity,
localAssetEntity,
localAlbumEntity,
localAlbumAssetEntity,
idxLocalAssetChecksum,
uQRemoteAssetOwnerChecksum,
idxRemoteAssetChecksum,
userMetadataEntity,
partnerEntity,
remoteExifEntity,
remoteAlbumEntity,
remoteAlbumAssetEntity,
remoteAlbumUserEntity,
memoryEntity,
memoryAssetEntity,
personEntity,
assetFaceEntity,
];
late final Shape16 userEntity = Shape16(
source: i0.VersionedTable(
entityName: 'user_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_0,
_column_1,
_column_2,
_column_3,
_column_84,
_column_85,
_column_5,
],
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,
_column_21,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape3 stackEntity = Shape3(
source: i0.VersionedTable(
entityName: 'stack_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [_column_0, _column_9, _column_5, _column_15, _column_75],
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_22,
_column_14,
_column_23,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape6 localAlbumEntity = Shape6(
source: i0.VersionedTable(
entityName: 'local_album_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_0,
_column_1,
_column_5,
_column_31,
_column_32,
_column_33,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape7 localAlbumAssetEntity = Shape7(
source: i0.VersionedTable(
entityName: 'local_album_asset_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(asset_id, album_id)'],
columns: [_column_34, _column_35],
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 Shape4 userMetadataEntity = Shape4(
source: i0.VersionedTable(
entityName: 'user_metadata_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(user_id, "key")'],
columns: [_column_25, _column_26, _column_27],
attachedDatabase: database,
),
alias: null,
);
late final Shape5 partnerEntity = Shape5(
source: i0.VersionedTable(
entityName: 'partner_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(shared_by_id, shared_with_id)'],
columns: [_column_28, _column_29, _column_30],
attachedDatabase: database,
),
alias: null,
);
late final Shape8 remoteExifEntity = Shape8(
source: i0.VersionedTable(
entityName: 'remote_exif_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(asset_id)'],
columns: [
_column_36,
_column_37,
_column_38,
_column_39,
_column_40,
_column_41,
_column_11,
_column_10,
_column_42,
_column_43,
_column_44,
_column_45,
_column_46,
_column_47,
_column_48,
_column_49,
_column_50,
_column_51,
_column_52,
_column_53,
_column_54,
_column_55,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape9 remoteAlbumEntity = Shape9(
source: i0.VersionedTable(
entityName: 'remote_album_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_0,
_column_1,
_column_56,
_column_9,
_column_5,
_column_15,
_column_57,
_column_58,
_column_59,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape7 remoteAlbumAssetEntity = Shape7(
source: i0.VersionedTable(
entityName: 'remote_album_asset_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(asset_id, album_id)'],
columns: [_column_36, _column_60],
attachedDatabase: database,
),
alias: null,
);
late final Shape10 remoteAlbumUserEntity = Shape10(
source: i0.VersionedTable(
entityName: 'remote_album_user_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(album_id, user_id)'],
columns: [_column_60, _column_25, _column_61],
attachedDatabase: database,
),
alias: null,
);
late final Shape11 memoryEntity = Shape11(
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_62,
_column_63,
_column_64,
_column_65,
_column_66,
_column_67,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape12 memoryAssetEntity = Shape12(
source: i0.VersionedTable(
entityName: 'memory_asset_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(asset_id, memory_id)'],
columns: [_column_36, _column_68],
attachedDatabase: database,
),
alias: null,
);
late final Shape14 personEntity = Shape14(
source: i0.VersionedTable(
entityName: 'person_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_0,
_column_9,
_column_5,
_column_15,
_column_1,
_column_69,
_column_71,
_column_72,
_column_73,
_column_74,
],
attachedDatabase: database,
),
alias: null,
);
late final Shape15 assetFaceEntity = Shape15(
source: i0.VersionedTable(
entityName: 'asset_face_entity',
withoutRowId: true,
isStrict: true,
tableConstraints: ['PRIMARY KEY(id)'],
columns: [
_column_0,
_column_36,
_column_76,
_column_77,
_column_78,
_column_79,
_column_80,
_column_81,
_column_82,
_column_83,
],
attachedDatabase: database,
),
alias: null,
);
}
class Shape16 extends i0.VersionedTable {
Shape16({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<bool> get hasProfileImage =>
columnsByName['has_profile_image']! as i1.GeneratedColumn<bool>;
i1.GeneratedColumn<DateTime> get profileChangedAt =>
columnsByName['profile_changed_at']! as i1.GeneratedColumn<DateTime>;
i1.GeneratedColumn<DateTime> get updatedAt =>
columnsByName['updated_at']! as i1.GeneratedColumn<DateTime>;
}
i1.GeneratedColumn<bool> _column_84(String aliasedName) =>
i1.GeneratedColumn<bool>(
'has_profile_image',
aliasedName,
false,
type: i1.DriftSqlType.bool,
defaultConstraints: i1.GeneratedColumn.constraintIsAlways(
'CHECK ("has_profile_image" IN (0, 1))',
),
defaultValue: const CustomExpression('0'),
);
i1.GeneratedColumn<DateTime> _column_85(String aliasedName) =>
i1.GeneratedColumn<DateTime>(
'profile_changed_at',
aliasedName,
false,
type: i1.DriftSqlType.dateTime,
defaultValue: const CustomExpression('CURRENT_TIMESTAMP'),
);
i0.MigrationStepWithVersion migrationSteps({ i0.MigrationStepWithVersion migrationSteps({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2, required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3, required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4, required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5,
}) { }) {
return (currentVersion, database) async { return (currentVersion, database) async {
switch (currentVersion) { switch (currentVersion) {
@ -1976,6 +2342,11 @@ i0.MigrationStepWithVersion migrationSteps({
final migrator = i1.Migrator(database, schema); final migrator = i1.Migrator(database, schema);
await from3To4(migrator, schema); await from3To4(migrator, schema);
return 4; return 4;
case 4:
final schema = Schema5(database: database);
final migrator = i1.Migrator(database, schema);
await from4To5(migrator, schema);
return 5;
default: default:
throw ArgumentError.value('Unknown migration from $currentVersion'); throw ArgumentError.value('Unknown migration from $currentVersion');
} }
@ -1986,10 +2357,12 @@ i1.OnUpgrade stepByStep({
required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2, required Future<void> Function(i1.Migrator m, Schema2 schema) from1To2,
required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3, required Future<void> Function(i1.Migrator m, Schema3 schema) from2To3,
required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4, required Future<void> Function(i1.Migrator m, Schema4 schema) from3To4,
required Future<void> Function(i1.Migrator m, Schema5 schema) from4To5,
}) => i0.VersionedSchema.stepByStepHelper( }) => i0.VersionedSchema.stepByStepHelper(
step: migrationSteps( step: migrationSteps(
from1To2: from1To2, from1To2: from1To2,
from2To3: from2To3, from2To3: from2To3,
from3To4: from3To4, from3To4: from3To4,
from4To5: from4To5,
), ),
); );

View File

@ -173,15 +173,14 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
id: user.id, id: user.id,
email: user.email, email: user.email,
name: user.name, name: user.name,
profileImagePath: user.profileImagePath?.isEmpty == true ? null : user.profileImagePath,
isAdmin: user.isAdmin, isAdmin: user.isAdmin,
updatedAt: user.updatedAt, updatedAt: user.updatedAt,
quotaSizeInBytes: user.quotaSizeInBytes ?? 0,
quotaUsageInBytes: user.quotaUsageInBytes,
memoryEnabled: true, memoryEnabled: true,
inTimeline: false, inTimeline: false,
isPartnerSharedBy: false, isPartnerSharedBy: false,
isPartnerSharedWith: false, isPartnerSharedWith: false,
profileChangedAt: user.profileChangedAt,
hasProfileImage: user.hasProfileImage,
), ),
) )
.get(); .get();

View File

@ -42,7 +42,12 @@ class SyncStreamRepository extends DriftDatabaseRepository {
try { try {
await _db.batch((batch) { await _db.batch((batch) {
for (final user in data) { for (final user in data) {
final companion = UserEntityCompanion(name: Value(user.name), email: Value(user.email)); final companion = UserEntityCompanion(
name: Value(user.name),
email: Value(user.email),
hasProfileImage: Value(user.hasProfileImage),
profileChangedAt: Value(user.profileChangedAt),
);
batch.insert(_db.userEntity, companion.copyWith(id: Value(user.id)), onConflict: DoUpdate((_) => companion)); batch.insert(_db.userEntity, companion.copyWith(id: Value(user.id)), onConflict: DoUpdate((_) => companion));
} }

View File

@ -11,7 +11,8 @@ abstract final class UserConverter {
name: dto.name, name: dto.name,
isAdmin: false, isAdmin: false,
updatedAt: DateTime.now(), updatedAt: DateTime.now(),
profileImagePath: dto.profileImagePath, hasProfileImage: dto.profileImagePath.isNotEmpty,
profileChangedAt: dto.profileChangedAt,
avatarColor: dto.avatarColor.toAvatarColor(), avatarColor: dto.avatarColor.toAvatarColor(),
); );
@ -21,14 +22,13 @@ abstract final class UserConverter {
name: adminDto.name, name: adminDto.name,
isAdmin: adminDto.isAdmin, isAdmin: adminDto.isAdmin,
updatedAt: adminDto.updatedAt, updatedAt: adminDto.updatedAt,
profileImagePath: adminDto.profileImagePath,
avatarColor: adminDto.avatarColor.toAvatarColor(), avatarColor: adminDto.avatarColor.toAvatarColor(),
memoryEnabled: preferenceDto?.memories.enabled ?? true, memoryEnabled: preferenceDto?.memories.enabled ?? true,
inTimeline: false, inTimeline: false,
isPartnerSharedBy: false, isPartnerSharedBy: false,
isPartnerSharedWith: false, isPartnerSharedWith: false,
quotaUsageInBytes: adminDto.quotaUsageInBytes ?? 0, profileChangedAt: adminDto.profileChangedAt,
quotaSizeInBytes: adminDto.quotaSizeInBytes ?? 0, hasProfileImage: adminDto.profileImagePath.isNotEmpty,
); );
static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto( static UserDto fromPartnerDto(PartnerResponseDto dto) => UserDto(
@ -37,14 +37,13 @@ abstract final class UserConverter {
name: dto.name, name: dto.name,
isAdmin: false, isAdmin: false,
updatedAt: DateTime.now(), updatedAt: DateTime.now(),
profileImagePath: dto.profileImagePath,
avatarColor: dto.avatarColor.toAvatarColor(), avatarColor: dto.avatarColor.toAvatarColor(),
memoryEnabled: false, memoryEnabled: false,
inTimeline: dto.inTimeline ?? false, inTimeline: dto.inTimeline ?? false,
isPartnerSharedBy: false, isPartnerSharedBy: false,
isPartnerSharedWith: false, isPartnerSharedWith: false,
quotaUsageInBytes: 0, profileChangedAt: dto.profileChangedAt,
quotaSizeInBytes: 0, hasProfileImage: dto.profileImagePath.isNotEmpty,
); );
} }

View File

@ -27,15 +27,14 @@ final driftUsersProvider = FutureProvider.autoDispose<List<UserDto>>((ref) async
name: entity.name, name: entity.name,
email: entity.email, email: entity.email,
isAdmin: entity.isAdmin, isAdmin: entity.isAdmin,
profileImagePath: entity.profileImagePath,
updatedAt: entity.updatedAt, updatedAt: entity.updatedAt,
quotaSizeInBytes: entity.quotaSizeInBytes ?? 0,
quotaUsageInBytes: entity.quotaUsageInBytes,
isPartnerSharedBy: false, isPartnerSharedBy: false,
isPartnerSharedWith: false, isPartnerSharedWith: false,
avatarColor: AvatarColor.primary, avatarColor: AvatarColor.primary,
memoryEnabled: true, memoryEnabled: true,
inTimeline: true, inTimeline: true,
profileChangedAt: entity.profileChangedAt,
hasProfileImage: entity.hasProfileImage,
), ),
) )
.toList(); .toList();

View File

@ -167,7 +167,6 @@ class AuthNotifier extends StateNotifier<AuthState> {
isAuthenticated: true, isAuthenticated: true,
name: user.name, name: user.name,
isAdmin: user.isAdmin, isAdmin: user.isAdmin,
profileImagePath: user.profileImagePath,
); );
return true; return true;

View File

@ -40,6 +40,11 @@ dynamic upgradeDto(dynamic value, String targetType) {
addDefault(value, 'isOnboarded', false); addDefault(value, 'isOnboarded', false);
} }
break; break;
case 'SyncUserV1':
if (value is Map) {
addDefault(value, 'profileChangedAt', DateTime.now().toIso8601String());
addDefault(value, 'hasProfileImage', false);
}
} }
} }

View File

@ -32,6 +32,7 @@ class UserCircleAvatar extends ConsumerWidget {
), ),
child: Text(user.name[0].toUpperCase()), child: Text(user.name[0].toUpperCase()),
); );
return Tooltip( return Tooltip(
message: user.name, message: user.name,
child: Container( child: Container(
@ -42,13 +43,12 @@ class UserCircleAvatar extends ConsumerWidget {
child: CircleAvatar( child: CircleAvatar(
backgroundColor: userAvatarColor, backgroundColor: userAvatarColor,
radius: radius, radius: radius,
child: user.profileImagePath == null child: user.hasProfileImage
? textIcon ? ClipRRect(
: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(50)), borderRadius: const BorderRadius.all(Radius.circular(50)),
child: CachedNetworkImage( child: CachedNetworkImage(
fit: BoxFit.cover, fit: BoxFit.cover,
cacheKey: user.profileImagePath, cacheKey: user.profileChangedAt.toIso8601String(),
width: size, width: size,
height: size, height: size,
placeholder: (_, __) => Image.memory(kTransparentImage), placeholder: (_, __) => Image.memory(kTransparentImage),
@ -57,7 +57,8 @@ class UserCircleAvatar extends ConsumerWidget {
fadeInDuration: const Duration(milliseconds: 300), fadeInDuration: const Duration(milliseconds: 300),
errorWidget: (context, error, stackTrace) => textIcon, errorWidget: (context, error, stackTrace) => textIcon,
), ),
), )
: textIcon,
), ),
), ),
); );

View File

@ -16,8 +16,10 @@ class SyncUserV1 {
required this.avatarColor, required this.avatarColor,
required this.deletedAt, required this.deletedAt,
required this.email, required this.email,
required this.hasProfileImage,
required this.id, required this.id,
required this.name, required this.name,
required this.profileChangedAt,
}); });
UserAvatarColor? avatarColor; UserAvatarColor? avatarColor;
@ -26,17 +28,23 @@ class SyncUserV1 {
String email; String email;
bool hasProfileImage;
String id; String id;
String name; String name;
DateTime profileChangedAt;
@override @override
bool operator ==(Object other) => identical(this, other) || other is SyncUserV1 && bool operator ==(Object other) => identical(this, other) || other is SyncUserV1 &&
other.avatarColor == avatarColor && other.avatarColor == avatarColor &&
other.deletedAt == deletedAt && other.deletedAt == deletedAt &&
other.email == email && other.email == email &&
other.hasProfileImage == hasProfileImage &&
other.id == id && other.id == id &&
other.name == name; other.name == name &&
other.profileChangedAt == profileChangedAt;
@override @override
int get hashCode => int get hashCode =>
@ -44,11 +52,13 @@ class SyncUserV1 {
(avatarColor == null ? 0 : avatarColor!.hashCode) + (avatarColor == null ? 0 : avatarColor!.hashCode) +
(deletedAt == null ? 0 : deletedAt!.hashCode) + (deletedAt == null ? 0 : deletedAt!.hashCode) +
(email.hashCode) + (email.hashCode) +
(hasProfileImage.hashCode) +
(id.hashCode) + (id.hashCode) +
(name.hashCode); (name.hashCode) +
(profileChangedAt.hashCode);
@override @override
String toString() => 'SyncUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, id=$id, name=$name]'; String toString() => 'SyncUserV1[avatarColor=$avatarColor, deletedAt=$deletedAt, email=$email, hasProfileImage=$hasProfileImage, id=$id, name=$name, profileChangedAt=$profileChangedAt]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@ -63,8 +73,10 @@ class SyncUserV1 {
// json[r'deletedAt'] = null; // json[r'deletedAt'] = null;
} }
json[r'email'] = this.email; json[r'email'] = this.email;
json[r'hasProfileImage'] = this.hasProfileImage;
json[r'id'] = this.id; json[r'id'] = this.id;
json[r'name'] = this.name; json[r'name'] = this.name;
json[r'profileChangedAt'] = this.profileChangedAt.toUtc().toIso8601String();
return json; return json;
} }
@ -80,8 +92,10 @@ class SyncUserV1 {
avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']),
deletedAt: mapDateTime(json, r'deletedAt', r''), deletedAt: mapDateTime(json, r'deletedAt', r''),
email: mapValueOfType<String>(json, r'email')!, email: mapValueOfType<String>(json, r'email')!,
hasProfileImage: mapValueOfType<bool>(json, r'hasProfileImage')!,
id: mapValueOfType<String>(json, r'id')!, id: mapValueOfType<String>(json, r'id')!,
name: mapValueOfType<String>(json, r'name')!, name: mapValueOfType<String>(json, r'name')!,
profileChangedAt: mapDateTime(json, r'profileChangedAt', r'')!,
); );
} }
return null; return null;
@ -132,8 +146,10 @@ class SyncUserV1 {
'avatarColor', 'avatarColor',
'deletedAt', 'deletedAt',
'email', 'email',
'hasProfileImage',
'id', 'id',
'name', 'name',
'profileChangedAt',
}; };
} }

View File

@ -98,7 +98,7 @@ void main() {
group('createProfileImage', () { group('createProfileImage', () {
test('should return profile image path', () async { test('should return profile image path', () async {
const profileImagePath = 'profile.jpg'; const profileImagePath = 'profile.jpg';
final updatedUser = UserStub.admin.copyWith(profileImagePath: profileImagePath); final updatedUser = UserStub.admin;
when( when(
() => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)),
@ -115,7 +115,7 @@ void main() {
test('should return null if profile image creation fails', () async { test('should return null if profile image creation fails', () async {
const profileImagePath = 'profile.jpg'; const profileImagePath = 'profile.jpg';
final updatedUser = UserStub.admin.copyWith(profileImagePath: profileImagePath); final updatedUser = UserStub.admin;
when( when(
() => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)), () => mockUserApiRepo.createProfileImage(name: profileImagePath, data: Uint8List(0)),

View File

@ -7,6 +7,7 @@ import 'schema_v1.dart' as v1;
import 'schema_v2.dart' as v2; import 'schema_v2.dart' as v2;
import 'schema_v3.dart' as v3; import 'schema_v3.dart' as v3;
import 'schema_v4.dart' as v4; import 'schema_v4.dart' as v4;
import 'schema_v5.dart' as v5;
class GeneratedHelper implements SchemaInstantiationHelper { class GeneratedHelper implements SchemaInstantiationHelper {
@override @override
@ -20,10 +21,12 @@ class GeneratedHelper implements SchemaInstantiationHelper {
return v3.DatabaseAtV3(db); return v3.DatabaseAtV3(db);
case 4: case 4:
return v4.DatabaseAtV4(db); return v4.DatabaseAtV4(db);
case 5:
return v5.DatabaseAtV5(db);
default: default:
throw MissingSchemaException(version, versions); throw MissingSchemaException(version, versions);
} }
} }
static const versions = const [1, 2, 3, 4]; static const versions = const [1, 2, 3, 4, 5];
} }

File diff suppressed because it is too large Load Diff

View File

@ -4,12 +4,28 @@ import 'package:openapi/api.dart';
abstract final class SyncStreamStub { abstract final class SyncStreamStub {
static final userV1Admin = SyncEvent( static final userV1Admin = SyncEvent(
type: SyncEntityType.userV1, type: SyncEntityType.userV1,
data: SyncUserV1(deletedAt: DateTime(2020), email: "admin@admin", id: "1", name: "Admin", avatarColor: null), data: SyncUserV1(
deletedAt: DateTime(2020),
email: "admin@admin",
id: "1",
name: "Admin",
avatarColor: null,
hasProfileImage: false,
profileChangedAt: DateTime(2025),
),
ack: "1", ack: "1",
); );
static final userV1User = SyncEvent( static final userV1User = SyncEvent(
type: SyncEntityType.userV1, type: SyncEntityType.userV1,
data: SyncUserV1(deletedAt: DateTime(2021), email: "user@user", id: "5", name: "User", avatarColor: null), data: SyncUserV1(
deletedAt: DateTime(2021),
email: "user@user",
id: "5",
name: "User",
avatarColor: null,
hasProfileImage: false,
profileChangedAt: DateTime(2025),
),
ack: "5", ack: "5",
); );
static final userDeleteV1 = SyncEvent( static final userDeleteV1 = SyncEvent(

View File

@ -10,7 +10,7 @@ abstract final class UserStub {
name: "admin", name: "admin",
isAdmin: true, isAdmin: true,
updatedAt: DateTime(2021), updatedAt: DateTime(2021),
profileImagePath: null, profileChangedAt: DateTime(2021),
avatarColor: AvatarColor.green, avatarColor: AvatarColor.green,
); );
@ -20,7 +20,7 @@ abstract final class UserStub {
name: "user1", name: "user1",
isAdmin: false, isAdmin: false,
updatedAt: DateTime(2022), updatedAt: DateTime(2022),
profileImagePath: null, profileChangedAt: DateTime(2022),
avatarColor: AvatarColor.red, avatarColor: AvatarColor.red,
); );
@ -30,7 +30,7 @@ abstract final class UserStub {
name: "user2", name: "user2",
isAdmin: false, isAdmin: false,
updatedAt: DateTime(2023), updatedAt: DateTime(2023),
profileImagePath: null, profileChangedAt: DateTime(2023),
avatarColor: AvatarColor.primary, avatarColor: AvatarColor.primary,
); );
} }

View File

@ -66,7 +66,14 @@ void main() {
final MockPartnerRepository partnerRepository = MockPartnerRepository(); final MockPartnerRepository partnerRepository = MockPartnerRepository();
final MockUserService userService = MockUserService(); final MockUserService userService = MockUserService();
final owner = UserDto(id: "1", updatedAt: DateTime.now(), email: "a@b.c", name: "first last", isAdmin: false); final owner = UserDto(
id: "1",
updatedAt: DateTime.now(),
email: "a@b.c",
name: "first last",
isAdmin: false,
profileChangedAt: DateTime(2021),
);
late SyncService s; late SyncService s;
setUpAll(() async { setUpAll(() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();

View File

@ -15124,19 +15124,28 @@
"email": { "email": {
"type": "string" "type": "string"
}, },
"hasProfileImage": {
"type": "boolean"
},
"id": { "id": {
"type": "string" "type": "string"
}, },
"name": { "name": {
"type": "string" "type": "string"
},
"profileChangedAt": {
"format": "date-time",
"type": "string"
} }
}, },
"required": [ "required": [
"avatarColor", "avatarColor",
"deletedAt", "deletedAt",
"email", "email",
"hasProfileImage",
"id", "id",
"name" "name",
"profileChangedAt"
], ],
"type": "object" "type": "object"
}, },

View File

@ -357,7 +357,7 @@ export const columns = {
], ],
syncAlbumUser: ['album_user.albumsId as albumId', 'album_user.usersId as userId', 'album_user.role'], syncAlbumUser: ['album_user.albumsId as albumId', 'album_user.usersId as userId', 'album_user.role'],
syncStack: ['stack.id', 'stack.createdAt', 'stack.updatedAt', 'stack.primaryAssetId', 'stack.ownerId'], syncStack: ['stack.id', 'stack.createdAt', 'stack.updatedAt', 'stack.primaryAssetId', 'stack.ownerId'],
syncUser: ['id', 'name', 'email', 'avatarColor', 'deletedAt', 'updateId'], syncUser: ['id', 'name', 'email', 'avatarColor', 'deletedAt', 'updateId', 'profileImagePath', 'profileChangedAt'],
stack: ['stack.id', 'stack.primaryAssetId', 'ownerId'], stack: ['stack.id', 'stack.primaryAssetId', 'ownerId'],
syncAssetExif: [ syncAssetExif: [
'asset_exif.assetId', 'asset_exif.assetId',

View File

@ -62,6 +62,8 @@ export class SyncUserV1 {
@ValidateEnum({ enum: UserAvatarColor, name: 'UserAvatarColor', nullable: true }) @ValidateEnum({ enum: UserAvatarColor, name: 'UserAvatarColor', nullable: true })
avatarColor!: UserAvatarColor | null; avatarColor!: UserAvatarColor | null;
deletedAt!: Date | null; deletedAt!: Date | null;
hasProfileImage!: boolean;
profileChangedAt!: Date;
} }
@ExtraModel() @ExtraModel()
@ -74,8 +76,6 @@ export class SyncAuthUserV1 extends SyncUserV1 {
quotaSizeInBytes!: number | null; quotaSizeInBytes!: number | null;
@ApiProperty({ type: 'integer' }) @ApiProperty({ type: 'integer' })
quotaUsageInBytes!: number; quotaUsageInBytes!: number;
hasProfileImage!: boolean;
profileChangedAt!: Date;
} }
@ExtraModel() @ExtraModel()

View File

@ -452,14 +452,14 @@ select
"avatarColor", "avatarColor",
"deletedAt", "deletedAt",
"updateId", "updateId",
"profileImagePath",
"profileChangedAt",
"isAdmin", "isAdmin",
"pinCode", "pinCode",
"oauthId", "oauthId",
"storageLabel", "storageLabel",
"quotaSizeInBytes", "quotaSizeInBytes",
"quotaUsageInBytes", "quotaUsageInBytes"
"profileImagePath",
"profileChangedAt"
from from
"user" "user"
where where
@ -896,7 +896,9 @@ select
"email", "email",
"avatarColor", "avatarColor",
"deletedAt", "deletedAt",
"updateId" "updateId",
"profileImagePath",
"profileChangedAt"
from from
"user" "user"
where where

View File

@ -375,16 +375,7 @@ class AuthUserSync extends BaseSync {
return this.db return this.db
.selectFrom('user') .selectFrom('user')
.select(columns.syncUser) .select(columns.syncUser)
.select([ .select(['isAdmin', 'pinCode', 'oauthId', 'storageLabel', 'quotaSizeInBytes', 'quotaUsageInBytes'])
'isAdmin',
'pinCode',
'oauthId',
'storageLabel',
'quotaSizeInBytes',
'quotaUsageInBytes',
'profileImagePath',
'profileChangedAt',
])
.$call(this.upsertTableFilters(ack)) .$call(this.upsertTableFilters(ack))
.stream(); .stream();
} }

View File

@ -0,0 +1,25 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await sql`DELETE FROM session_sync_checkpoint
WHERE type IN (
'UserV1',
'AssetV1',
'PartnerAssetV1',
'PartnerAssetBackfillV1',
'AlbumAssetV1',
'AlbumAssetBackfillV1'
)`.execute(db);
}
export async function down(db: Kysely<any>): Promise<void> {
await sql`DELETE FROM session_sync_checkpoint
WHERE type IN (
'UserV1',
'AssetV1',
'PartnerAssetV1',
'PartnerAssetBackfillV1',
'AlbumAssetV1',
'AlbumAssetBackfillV1'
)`.execute(db);
}

View File

@ -188,8 +188,8 @@ export class SyncService extends BaseService {
const upsertType = SyncEntityType.UserV1; const upsertType = SyncEntityType.UserV1;
const upserts = this.syncRepository.user.getUpserts(checkpointMap[upsertType]); const upserts = this.syncRepository.user.getUpserts(checkpointMap[upsertType]);
for await (const { updateId, ...data } of upserts) { for await (const { updateId, profileImagePath, ...data } of upserts) {
send(response, { type: upsertType, ids: [updateId], data }); send(response, { type: upsertType, ids: [updateId], data: { ...data, hasProfileImage: !!profileImagePath } });
} }
} }

View File

@ -35,9 +35,11 @@ describe(SyncEntityType.UserV1, () => {
data: { data: {
deletedAt: user.deletedAt, deletedAt: user.deletedAt,
email: user.email, email: user.email,
hasProfileImage: user.profileImagePath !== '',
id: user.id, id: user.id,
name: user.name, name: user.name,
avatarColor: user.avatarColor, avatarColor: user.avatarColor,
profileChangedAt: user.profileChangedAt.toISOString(),
}, },
type: 'UserV1', type: 'UserV1',
}, },