Load the current avatar photo alongside user accounts

This commit is contained in:
Aaron Po
2026-09-30 22:16:04 -04:00
parent c5972b9f04
commit bce8fb0472
11 changed files with 390 additions and 29 deletions

View File

@@ -14,6 +14,7 @@ public static class UserDtoMapper
FirstName : user.FirstName,
LastName : user.LastName,
Biography : user.UserProfile?.Biography ?? string.Empty,
AvatarUrl : user.UserAvatar?.Photo?.Hyperlink ?? string.Empty,
CreatedAt : user.CreatedAt
);
}

View File

@@ -7,5 +7,6 @@ public record PublicUserProfileDto(
string FirstName,
string LastName,
string Biography,
string AvatarUrl,
DateTime CreatedAt
);

View File

@@ -13,61 +13,87 @@ public class UserListRepository(ISqlConnectionFactory connectionFactory)
IUserListRepository
{
/// <inheritdoc />
/// <remarks>
/// Left-joins <c>Social.UserProfile</c>, so <see cref="UserAccount.UserProfile" /> is
/// <c>null</c> for an account whose profile has yet to be created.
/// </remarks>
public async Task<UserAccount?> GetByIdAsync(Guid id)
{
await using DbConnection connection = await CreateConnection();
IEnumerable<UserAccount> results = await connection.QueryAsync<
UserAccount,
UserProfile,
Photo,
UserAvatar,
UserAccount
>(
"""
SELECT
ua.UserAccountID,
ua.Username,
ua.FirstName, ua.LastName, ua.Email, ua.CreatedAt, ua.UpdatedAt,
ua.DateOfBirth, ua.RowVersion,
up.UserProfileID, up.UserAccountID, up.Biography, up.RowVersion
ua.UserAccountID, ua.Username, ua.FirstName, ua.LastName,
ua.Email, ua.CreatedAt, ua.UpdatedAt, ua.DateOfBirth,
ua.RowVersion, up.UserProfileID, up.UserAccountID,
up.Biography, up.RowVersion, p.PhotoID, p.UploadedByID,
p.Hyperlink, p.UploadedAt, p.RowVersion, av.UserAvatarID,
av.UserProfileID, av.PhotoID, av.ValidFrom, av.ValidTo,
av.RowVersion
FROM Auth.UserAccount ua
LEFT JOIN Social.UserProfile up ON ua.UserAccountID = up.UserAccountID
LEFT JOIN Social.UserProfile up
ON ua.UserAccountID = up.UserAccountID
LEFT JOIN Social.UserAvatar av
ON up.UserProfileID = av.UserProfileID
AND av.ValidTo IS NULL
LEFT JOIN Media.Photo p ON av.PhotoID = p.PhotoID
WHERE ua.UserAccountID = @UserAccountId
""",
MapUserRow,
new { UserAccountId = id },
splitOn: "UserProfileID"
splitOn: AvatarSplitOn
);
return results.SingleOrDefault();
}
private static UserAccount MapUserRow(UserAccount user, UserProfile? profile)
{
user.UserProfile = profile;
return user;
}
/// <inheritdoc />
/// <remarks>
/// Always applies pagination via <c>OFFSET</c>/<c>FETCH</c>, ordered by creation date descending.
/// A <c>null</c> <paramref name="limit" />/<paramref name="offset" /> is treated as "no limit"/"no offset".
/// Does not select <c>Email</c> or <c>DateOfBirth</c>: this listing is consumed only as public
/// profiles, so those fields are left at their default rather than read from the database.
/// </remarks>
public async Task<IEnumerable<UserAccount>> GetAllAsync(int? limit, int? offset)
{
await using DbConnection connection = await CreateConnection();
return await connection.QueryAsync<UserAccount>(
return await connection.QueryAsync<
UserAccount,
UserProfile,
Photo,
UserAvatar,
UserAccount
>(
"""
SELECT UserAccountID, Username, FirstName, LastName, CreatedAt, UpdatedAt, RowVersion
FROM Auth.UserAccount
ORDER BY CreatedAt DESC
SELECT
ua.UserAccountID, ua.Username, ua.FirstName, ua.LastName,
ua.CreatedAt, ua.UpdatedAt, ua.RowVersion,
up.UserProfileID, up.UserAccountID, up.Biography, up.RowVersion,
p.PhotoID, p.UploadedByID, p.Hyperlink, p.UploadedAt, p.RowVersion,
av.UserAvatarID, av.UserProfileID, av.PhotoID, av.ValidFrom, av.ValidTo,
av.RowVersion
FROM Auth.UserAccount ua
LEFT JOIN Social.UserProfile up ON ua.UserAccountID = up.UserAccountID
LEFT JOIN Social.UserAvatar av
ON up.UserProfileID = av.UserProfileID AND av.ValidTo IS NULL
LEFT JOIN Media.Photo p ON av.PhotoID = p.PhotoID
ORDER BY ua.CreatedAt DESC
OFFSET @Offset ROWS FETCH NEXT @Limit ROWS ONLY
""",
new { Offset = offset ?? 0, Limit = limit ?? int.MaxValue }
MapUserRow,
new { Offset = offset ?? 0, Limit = limit ?? int.MaxValue },
splitOn: AvatarSplitOn
);
}
private const string AvatarSplitOn = "UserProfileID,PhotoID,UserAvatarID";
private static UserAccount MapUserRow(
UserAccount user,
UserProfile? profile,
Photo? photo,
UserAvatar? avatar
)
{
user.UserProfile = profile;
avatar?.Photo = photo;
user.UserAvatar = avatar;
return user;
}
}