Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ interface UserProfileDao {
* Uses `INSERT OR REPLACE` with correlated sub-selects (rather than `ON CONFLICT DO
* UPDATE`) so it works on the minSdk-29 SQLite build, which predates UPSERT. The
* sub-selects read the current row before the replace, preserving every column the
* blocklist doesn't know about; the avatar keeps its existing value when [profilePicture]
* is null via COALESCE.
* blocklist doesn't know about. The name and avatar keep their existing values when
* [displayName] or [profilePicture] is null, via COALESCE: null means the caller has no
* profile to write (a failed fetch), whereas an empty name is one the user really has. A
* null name with nothing cached stores an empty one, since the column is NOT NULL.
*/
@Query(
"""
Expand All @@ -38,7 +40,7 @@ interface UserProfileDao {
profile_picture_json, username, pending_migration_json
) VALUES (
:userIdHex,
:displayName,
COALESCE(:displayName, (SELECT display_name FROM user_profiles WHERE user_id_hex = :userIdHex), ''),
(SELECT phone_value FROM user_profiles WHERE user_id_hex = :userIdHex),
(SELECT phone_verified FROM user_profiles WHERE user_id_hex = :userIdHex),
(SELECT email_value FROM user_profiles WHERE user_id_hex = :userIdHex),
Expand All @@ -50,7 +52,7 @@ interface UserProfileDao {
)
"""
)
suspend fun upsertNameAndAvatar(userIdHex: String, displayName: String, profilePicture: MediaItem?)
suspend fun upsertNameAndAvatar(userIdHex: String, displayName: String?, profilePicture: MediaItem?)

/** The cached profile for [userIdHex], or null if none is cached. */
@Query("SELECT * FROM user_profiles WHERE user_id_hex = :userIdHex LIMIT 1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,37 @@ class UserProfileDaoTest {
assertEquals("alice", dao.getByUserId("u1")?.username)
}

@Test
fun `partial write with no name keeps the cached name`() = runBlocking {
val dao = db.userProfileDao()
dao.upsertFull(listOf(fullProfile("u1")))

// The blocklist writes no name when the profile fetch failed; that is not a rename.
dao.upsertNameAndAvatar(userIdHex = "u1", displayName = null, profilePicture = null)

assertEquals("Alice", dao.getByUserId("u1")?.displayName)
}

@Test
fun `partial write with no name and nothing cached stores an empty name`() = runBlocking {
val dao = db.userProfileDao()

dao.upsertNameAndAvatar(userIdHex = "u1", displayName = null, profilePicture = null)

assertEquals("", dao.getByUserId("u1")?.displayName)
}

@Test
fun `partial write with an empty name replaces the cached name`() = runBlocking {
val dao = db.userProfileDao()
dao.upsertFull(listOf(fullProfile("u1")))

// A fetched profile with no name is the user's own choice, not a failed lookup.
dao.upsertNameAndAvatar(userIdHex = "u1", displayName = "", profilePicture = null)

assertEquals("", dao.getByUserId("u1")?.displayName)
}

@Test
fun `chat member relation joins the shared normalized profile`() = runBlocking {
db.userProfileDao().upsertFull(listOf(fullProfile("u1")))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,13 @@ class BlockedUserDataSource @Inject constructor(
/**
* Writes the blocked user's name + avatar into the shared `user_profiles` table without
* disturbing any richer profile already cached from a chat (the blocklist only resolves
* those two fields). See [com.flipcash.app.persistence.dao.UserProfileDao.upsertNameAndAvatar].
* those two fields). A null profile means the fetch failed, so the cached name stays rather
* than being blanked. See [com.flipcash.app.persistence.dao.UserProfileDao.upsertNameAndAvatar].
*/
private suspend fun FlipcashDatabase.writeProfile(resolved: ResolvedBlockedUser) {
userProfileDao().upsertNameAndAvatar(
userIdHex = resolved.blocked.userId.hexEncodedString(),
displayName = resolved.profile?.displayName.orEmpty(),
displayName = resolved.profile?.displayName,
profilePicture = resolved.profile?.profilePicture,
)
}
Expand Down
Loading