First Commit

This commit is contained in:
2025-12-18 16:28:50 +07:00
commit 8c3e4f491f
9974 changed files with 396488 additions and 0 deletions
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
import extension.setupDependencyInjection
plugins {
id("io.element.android-compose-library")
id("kotlin-parcelize")
}
android {
namespace = "io.element.android.libraries.recentemojis.impl"
}
setupDependencyInjection()
dependencies {
api(projects.libraries.recentemojis.api)
implementation(projects.libraries.matrix.api)
implementation(libs.kotlinx.collections.immutable)
implementation(libs.matrix.emojibase.bindings)
testImplementation(projects.libraries.recentemojis.test)
testImplementation(libs.test.junit)
testImplementation(libs.coroutines.test)
testImplementation(libs.molecule.runtime)
testImplementation(libs.test.truth)
testImplementation(libs.test.turbine)
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.tests.testutils)
}
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.recentemojis.impl
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.recentemojis.api.AddRecentEmoji
import kotlinx.coroutines.withContext
@ContributesBinding(SessionScope::class)
class DefaultAddRecentEmoji(
private val client: MatrixClient,
private val dispatchers: CoroutineDispatchers,
) : AddRecentEmoji {
override suspend operator fun invoke(emoji: String): Result<Unit> = withContext(dispatchers.io) {
client.addRecentEmoji(emoji)
}
}
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.recentemojis.impl
import android.content.Context
import io.element.android.emojibasebindings.EmojibaseDatasource
import io.element.android.emojibasebindings.EmojibaseStore
import io.element.android.libraries.recentemojis.api.EmojibaseProvider
class DefaultEmojibaseProvider(val context: Context) : EmojibaseProvider {
override val emojibaseStore: EmojibaseStore by lazy {
EmojibaseDatasource().load(context)
}
}
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.recentemojis.impl
import dev.zacsweers.metro.ContributesBinding
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.recentemojis.api.EmojibaseProvider
import io.element.android.libraries.recentemojis.api.GetRecentEmojis
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.withContext
@ContributesBinding(SessionScope::class)
class DefaultGetRecentEmojis(
private val client: MatrixClient,
private val dispatchers: CoroutineDispatchers,
private val emojibaseProvider: EmojibaseProvider,
) : GetRecentEmojis {
override suspend operator fun invoke(): Result<ImmutableList<String>> = withContext(dispatchers.io) {
val allEmojis = emojibaseProvider.emojibaseStore.allEmojis
client.getRecentEmojis()
.map { emojis ->
// Remove any possible duplicates
emojis.distinct()
// Return only those emojis that are valid
.filter { recent -> allEmojis.any { recent == it.unicode } }
.toImmutableList()
}
}
}
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.recentemojis.impl
import com.google.common.truth.Truth.assertThat
import io.element.android.emojibasebindings.Emoji
import io.element.android.emojibasebindings.EmojibaseCategory
import io.element.android.emojibasebindings.EmojibaseCategory.People
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.recentemojis.test.FakeEmojibaseProvider
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
class DefaultGetRecentEmojisTest {
@Test
fun `invoke - deduplicates results`() = runTest {
val recentEmojiResult = persistentListOf(":)", ":D", ":)")
val getRecentEmojis = createDefaultGetRecentEmojis(
recentEmojis = { Result.success(recentEmojiResult) },
emojibaseContents = persistentMapOf(People to recentEmojiResult.map { emoji(it) }.toImmutableList())
)
assertThat(getRecentEmojis()).isEqualTo(Result.success(persistentListOf(":)", ":D")))
}
@Test
fun `invoke - removes non-standard emojis`() = runTest {
val recentEmojiResult = persistentListOf(":)", ":D", "Custom reaction")
val getRecentEmojis = createDefaultGetRecentEmojis(
recentEmojis = { Result.success(recentEmojiResult) },
emojibaseContents = persistentMapOf(
People to persistentListOf(emoji(":)"), emoji(":D"))
)
)
assertThat(getRecentEmojis()).isEqualTo(Result.success(persistentListOf(":)", ":D")))
}
private fun emoji(unicode: String) = Emoji(
hexcode = "",
label = "",
tags = null,
shortcodes = persistentListOf(),
unicode = unicode,
skins = null,
)
private fun TestScope.createDefaultGetRecentEmojis(
recentEmojis: () -> Result<List<String>> = { Result.success(emptyList()) },
emojibaseContents: ImmutableMap<EmojibaseCategory, ImmutableList<Emoji>> = persistentMapOf(People to persistentListOf(emoji(":)"))),
) = DefaultGetRecentEmojis(
client = FakeMatrixClient(getRecentEmojisLambda = recentEmojis),
dispatchers = testCoroutineDispatchers(),
emojibaseProvider = FakeEmojibaseProvider(emojibaseContents),
)
}