豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit 384eb63

Browse files
authored
upflit(beta): fix: avatar image uri (#10500) (#10522)
2 parents 2740362 + 814aba4 commit 384eb63

File tree

6 files changed

+41
-16
lines changed

6 files changed

+41
-16
lines changed

feature/account/api/build.gradle.kts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@ kotlin {
1111
commonMain.dependencies {
1212
api(projects.core.architecture.api)
1313
}
14-
15-
androidMain.dependencies {
16-
// ensure Android target can consume the module if it's platform-specific
17-
api(projects.core.architecture.api)
18-
}
1914
}
2015
}
2116

feature/account/avatar/api/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ kotlin {
88
}
99

1010
sourceSets {
11-
1211
commonMain.dependencies {
1312
api(projects.feature.account.api)
1413
api(libs.uri)

feature/account/avatar/impl/src/main/kotlin/net/thunderbird/feature/account/avatar/data/datasource/LocalAvatarImageDataSource.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package net.thunderbird.feature.account.avatar.data.datasource
22

33
import com.eygraber.uri.Uri
4+
import kotlin.time.Clock
5+
import kotlin.time.ExperimentalTime
46
import net.thunderbird.core.file.DirectoryProvider
57
import net.thunderbird.core.file.FileManager
68
import net.thunderbird.feature.account.AccountId
@@ -15,17 +17,22 @@ import net.thunderbird.feature.account.avatar.data.AvatarDataContract.DataSource
1517
* @param fileManager The [FileManager] for file operations.
1618
* @param directoryProvider The [DirectoryProvider] to get app directories.
1719
*/
20+
@OptIn(ExperimentalTime::class)
1821
internal class LocalAvatarImageDataSource(
1922
private val fileManager: FileManager,
2023
private val directoryProvider: DirectoryProvider,
24+
private val clock: Clock,
2125
) : LocalAvatarImage {
2226

2327
override suspend fun update(id: AccountId, imageUri: Uri): Uri {
2428
val avatarImageUri = getAvatarImageUri(id)
2529

2630
fileManager.copy(imageUri, avatarImageUri)
2731

28-
return avatarImageUri
32+
return avatarImageUri.buildUpon()
33+
.clearQuery()
34+
.appendQueryParameter("v", clock.now().toEpochMilliseconds().toString())
35+
.build()
2936
}
3037

3138
override suspend fun delete(id: AccountId) {

feature/account/avatar/impl/src/main/kotlin/net/thunderbird/feature/account/avatar/di/FeatureAccountAvatarModule.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
package net.thunderbird.feature.account.avatar.di
22

3+
import kotlin.time.ExperimentalTime
34
import net.thunderbird.feature.account.avatar.AvatarImageRepository
45
import net.thunderbird.feature.account.avatar.data.AvatarDataContract.DataSource
56
import net.thunderbird.feature.account.avatar.data.DefaultAvatarImageRepository
67
import net.thunderbird.feature.account.avatar.data.datasource.LocalAvatarImageDataSource
78
import org.koin.dsl.module
89

10+
@OptIn(ExperimentalTime::class)
911
val featureAccountAvatarModule = module {
1012
single<DataSource.LocalAvatarImage> {
1113
LocalAvatarImageDataSource(
1214
fileManager = get(),
1315
directoryProvider = get(),
16+
clock = get(),
1417
)
1518
}
1619

feature/account/avatar/impl/src/test/kotlin/net/thunderbird/feature/account/avatar/data/datasource/LocalAvatarImageDataSourceTest.kt

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,24 @@ package net.thunderbird.feature.account.avatar.data.datasource
22

33
import assertk.assertThat
44
import assertk.assertions.isEqualTo
5+
import assertk.assertions.isNotEqualTo
56
import assertk.assertions.isNotNull
67
import assertk.assertions.isNull
78
import com.eygraber.uri.toKmpUri
89
import kotlin.test.BeforeTest
910
import kotlin.test.Test
11+
import kotlin.time.Duration.Companion.milliseconds
12+
import kotlin.time.ExperimentalTime
13+
import kotlin.time.Instant
1014
import kotlinx.coroutines.test.runTest
1115
import net.thunderbird.core.file.DirectoryProvider
16+
import net.thunderbird.core.testing.TestClock
1217
import net.thunderbird.feature.account.AccountIdFactory
1318
import net.thunderbird.feature.account.avatar.data.AvatarDataContract
1419
import org.junit.Rule
1520
import org.junit.rules.TemporaryFolder
1621

22+
@OptIn(ExperimentalTime::class)
1723
class LocalAvatarImageDataSourceTest {
1824

1925
@JvmField
@@ -22,37 +28,57 @@ class LocalAvatarImageDataSourceTest {
2228

2329
private lateinit var directoryProvider: DirectoryProvider
2430
private lateinit var fileManager: CapturingFileManager
31+
private lateinit var clock: TestClock
2532
private lateinit var testSubject: LocalAvatarImageDataSource
2633

2734
@BeforeTest
2835
fun setUp() {
2936
val appDir = folder.newFolder("app")
3037
directoryProvider = FakeDirectoryProvider(appDir.absolutePath.toKmpUri())
3138
fileManager = CapturingFileManager()
32-
testSubject = LocalAvatarImageDataSource(fileManager, directoryProvider)
39+
clock = TestClock(Instant.fromEpochMilliseconds(1_000))
40+
testSubject = LocalAvatarImageDataSource(fileManager, directoryProvider, clock)
3341
}
3442

3543
@Test
36-
fun `update should copy image to expected path and return destination uri`() = runTest {
44+
fun `update should copy image to expected path and return versioned destination uri`() = runTest {
3745
// Arrange
3846
val accountId = AccountIdFactory.create()
3947
val source = "file:///external/picked/image.jpg".toKmpUri()
4048
val expectedDir = directoryProvider.getFilesDir().buildUpon()
4149
.appendPath(AvatarDataContract.DataSource.LocalAvatarImage.DIRECTORY_NAME)
4250
.build()
4351
val expectedDest = expectedDir.buildUpon().appendPath("$accountId.jpg").build()
52+
val expectedVersioned = expectedDest.buildUpon().appendQueryParameter("v", "1000").build()
4453

4554
// Act
4655
val returned = testSubject.update(accountId, source)
4756

4857
// Assert
49-
assertThat(returned).isEqualTo(expectedDest)
58+
assertThat(returned).isEqualTo(expectedVersioned)
5059
assertThat(fileManager.lastCreatedDir).isEqualTo(expectedDir)
5160
assertThat(fileManager.lastCopySource).isEqualTo(source)
5261
assertThat(fileManager.lastCopyDestination).isEqualTo(expectedDest)
5362
assertThat(fileManager.lastDeleted).isNull()
5463
}
5564

65+
@Test
66+
fun `successive updates should return different URIs`() = runTest {
67+
// Arrange
68+
val accountId = AccountIdFactory.create()
69+
val source = "file:///external/picked/image.jpg".toKmpUri()
70+
71+
// Act
72+
val uri1 = testSubject.update(accountId, source)
73+
clock.advanceTimeBy(1.milliseconds)
74+
val uri2 = testSubject.update(accountId, source)
75+
76+
// Assert
77+
assertThat(uri1).isNotEqualTo(uri2)
78+
// Base paths should be the same
79+
assertThat(uri1.buildUpon().clearQuery().build()).isEqualTo(uri2.buildUpon().clearQuery().build())
80+
}
81+
5682
@Test
5783
fun `delete should remove expected avatar path`() = runTest {
5884
// Arrange

feature/account/profile/api/build.gradle.kts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,7 @@ kotlin {
99
}
1010
sourceSets {
1111
commonMain.dependencies {
12-
api(projects.feature.account.api)
13-
api(projects.feature.account.avatar.api)
14-
}
15-
16-
androidMain.dependencies {
17-
// ensure Android target can consume the module if it's platform-specific
12+
api(projects.core.architecture.api)
1813
api(projects.feature.account.api)
1914
api(projects.feature.account.avatar.api)
2015
}

0 commit comments

Comments
 (0)