diff --git a/app-android/src/main/java/io/github/droidkaigi/confsched/KaigiApp.kt b/app-android/src/main/java/io/github/droidkaigi/confsched/KaigiApp.kt index 969c04864..92b790dae 100644 --- a/app-android/src/main/java/io/github/droidkaigi/confsched/KaigiApp.kt +++ b/app-android/src/main/java/io/github/droidkaigi/confsched/KaigiApp.kt @@ -226,18 +226,17 @@ private class ExternalNavController( /** * Navigate to Calendar Registration - * @param timeTableItem カレンダー登録に必要なタイムラインアイテムの情報 */ - fun navigateToCalendarRegistration(timeTableItem: TimetableItem) { + fun navigateToCalendarRegistration(timetableItem: TimetableItem) { val calendarIntent = Intent(Intent.ACTION_INSERT).apply { data = CalendarContract.Events.CONTENT_URI putExtras( bundleOf( - CalendarContract.EXTRA_EVENT_BEGIN_TIME to timeTableItem.startsAt.toEpochMilliseconds(), - CalendarContract.EXTRA_EVENT_END_TIME to timeTableItem.endsAt.toEpochMilliseconds(), - CalendarContract.Events.TITLE to "[${timeTableItem.room.name.currentLangTitle}] ${timeTableItem.title.currentLangTitle}", - CalendarContract.Events.DESCRIPTION to timeTableItem.url, - CalendarContract.Events.EVENT_LOCATION to timeTableItem.room.name.currentLangTitle, + CalendarContract.EXTRA_EVENT_BEGIN_TIME to timetableItem.startsAt.toEpochMilliseconds(), + CalendarContract.EXTRA_EVENT_END_TIME to timetableItem.endsAt.toEpochMilliseconds(), + CalendarContract.Events.TITLE to "[${timetableItem.room.name.currentLangTitle}] ${timetableItem.title.currentLangTitle}", + CalendarContract.Events.DESCRIPTION to timetableItem.url, + CalendarContract.Events.EVENT_LOCATION to timetableItem.room.name.currentLangTitle, ), ) } @@ -253,11 +252,11 @@ private class ExternalNavController( context.startActivity(Intent(context, OssLicensesMenuActivity::class.java)) } - fun onShareClick(timeTableItem: TimetableItem) { + fun onShareClick(timetableItem: TimetableItem) { shareNavigator.share( - "[${timeTableItem.room.name.currentLangTitle}] ${timeTableItem.startsTimeString} - ${timeTableItem.endsTimeString}\n" + - "${timeTableItem.title.currentLangTitle}\n" + - timeTableItem.url, + "[${timetableItem.room.name.currentLangTitle}] ${timetableItem.startsTimeString} - ${timetableItem.endsTimeString}\n" + + "${timetableItem.title.currentLangTitle}\n" + + timetableItem.url, ) } diff --git a/app-ios-shared/src/iosTest/kotlin/io/github/droidkaigi/confsched/shared/EntryPointTest.kt b/app-ios-shared/src/iosTest/kotlin/io/github/droidkaigi/confsched/shared/EntryPointTest.kt index 14ab1c14e..81aa0f621 100644 --- a/app-ios-shared/src/iosTest/kotlin/io/github/droidkaigi/confsched/shared/EntryPointTest.kt +++ b/app-ios-shared/src/iosTest/kotlin/io/github/droidkaigi/confsched/shared/EntryPointTest.kt @@ -1,11 +1,9 @@ package io.github.droidkaigi.confsched.shared import io.github.droidkaigi.confsched.data.Repositories -import io.github.droidkaigi.confsched.data.achievements.AchievementsDataStore import io.github.droidkaigi.confsched.data.auth.AuthApi import io.github.droidkaigi.confsched.data.auth.Authenticator import io.github.droidkaigi.confsched.data.auth.User -import io.github.droidkaigi.confsched.model.AchievementRepository import io.github.droidkaigi.confsched.data.remoteconfig.RemoteConfigApi import io.github.droidkaigi.confsched.data.sessions.SessionCacheDataStore import io.github.droidkaigi.confsched.data.sessions.SessionsApiClient @@ -50,10 +48,8 @@ class EntryPointTest { assertNotNull(kmpEntryPoint.get()) assertNotNull(kmpEntryPoint.get()) assertNotNull(kmpEntryPoint.get()) - assertNotNull(kmpEntryPoint.get()) assertNotNull(kmpEntryPoint.get()) - assertNotNull(kmpEntryPoint.get()) assertNotNull(kmpEntryPoint.get()) assertNotNull(kmpEntryPoint.get()) assertNotNull(kmpEntryPoint.get()) diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidRoborazziPlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidRoborazziPlugin.kt index c2ce7a862..8ff6f6f2d 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidRoborazziPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/AndroidRoborazziPlugin.kt @@ -7,6 +7,7 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED import org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED import org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED +import org.gradle.api.tasks.testing.logging.TestLogEvent.STARTED import org.gradle.kotlin.dsl.dependencies import org.gradle.kotlin.dsl.getByType @@ -22,14 +23,16 @@ class AndroidRoborazziPlugin : Plugin { testOptions { unitTests { all { test -> + test.systemProperties["robolectric.logging.enabled"] = "true" test.jvmArgs("-noverify") test.systemProperties["robolectric.graphicsMode"] = "NATIVE" test.systemProperties["robolectric.pixelCopyRenderMode"] = "hardware" test.maxParallelForks = Runtime.getRuntime().availableProcessors() test.testLogging { - events.addAll(listOf(PASSED, SKIPPED, FAILED)) + events.addAll(listOf(STARTED, PASSED, SKIPPED, FAILED)) showCauses = true showExceptions = true + showStandardStreams = true exceptionFormat = FULL } } diff --git a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpRoborazziPlugin.kt b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpRoborazziPlugin.kt index cd6379839..b60d7497f 100644 --- a/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpRoborazziPlugin.kt +++ b/build-logic/src/main/kotlin/io/github/droidkaigi/confsched/primitive/KmpRoborazziPlugin.kt @@ -7,6 +7,7 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED import org.gradle.api.tasks.testing.logging.TestLogEvent.PASSED import org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED +import org.gradle.api.tasks.testing.logging.TestLogEvent.STARTED import org.gradle.kotlin.dsl.getByType import kotlin.collections.listOf import kotlin.collections.set @@ -31,7 +32,7 @@ class KmpRoborazziPlugin : Plugin { test.maxParallelForks = Runtime.getRuntime().availableProcessors() test.testLogging { - events.addAll(listOf(PASSED, SKIPPED, FAILED)) + events.addAll(listOf(STARTED, PASSED, SKIPPED, FAILED)) showCauses = true showExceptions = true exceptionFormat = FULL diff --git a/ci.sh b/ci.sh index e24ec4273..8fbb7c5f4 100755 --- a/ci.sh +++ b/ci.sh @@ -12,6 +12,6 @@ PLATFORM_IOS="iOS Simulator,name=iPhone 15 Pro,OS=17.4" set -o pipefail && xcodebuild build \ -project $PROJECT \ --scheme "DroidKaigi2024AppWithKmpBuild" \ +-scheme "DroidKaigi2024App" \ -configuration Debug \ -destination platform="$PLATFORM_IOS" diff --git a/core/data/src/androidMain/kotlin/io/github/droidkaigi/confsched/data/achievements/AchievementsDataStoreModule.kt b/core/data/src/androidMain/kotlin/io/github/droidkaigi/confsched/data/achievements/AchievementsDataStoreModule.kt deleted file mode 100644 index 3c3ca462c..000000000 --- a/core/data/src/androidMain/kotlin/io/github/droidkaigi/confsched/data/achievements/AchievementsDataStoreModule.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.github.droidkaigi.confsched.data.achievements - -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import io.github.droidkaigi.confsched.data.user.AchievementsDataStoreQualifier -import javax.inject.Singleton - -@InstallIn(SingletonComponent::class) -@Module -public class AchievementsDataStoreModule { - - @Provides - @Singleton - public fun provideAchievementsDataStore( - @AchievementsDataStoreQualifier - dataStore: DataStore, - ): AchievementsDataStore { - return AchievementsDataStore(dataStore) - } -} diff --git a/core/data/src/androidMain/kotlin/io/github/droidkaigi/confsched/data/remoteconfig/AchievementRepositoryModule.kt b/core/data/src/androidMain/kotlin/io/github/droidkaigi/confsched/data/remoteconfig/AchievementRepositoryModule.kt deleted file mode 100644 index dbc0c75a1..000000000 --- a/core/data/src/androidMain/kotlin/io/github/droidkaigi/confsched/data/remoteconfig/AchievementRepositoryModule.kt +++ /dev/null @@ -1,40 +0,0 @@ -package io.github.droidkaigi.confsched.data.remoteconfig - -import dagger.Binds -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import dagger.multibindings.ClassKey -import dagger.multibindings.IntoMap -import io.github.droidkaigi.confsched.data.achievements.AchievementsDataStore -import io.github.droidkaigi.confsched.data.achievements.DefaultAchievementRepository -import io.github.droidkaigi.confsched.data.di.RepositoryQualifier -import io.github.droidkaigi.confsched.model.AchievementRepository -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -public abstract class AchievementRepositoryModule { - @Binds - @RepositoryQualifier - @IntoMap - @ClassKey(AchievementRepository::class) - public abstract fun bindAchievementRepository( - repository: AchievementRepository, - ): Any - - public companion object { - @Provides - @Singleton - public fun provideAchievementRepositorySingleton( - remoteConfigApi: RemoteConfigApi, - achievementsDataStore: AchievementsDataStore, - ): AchievementRepository { - return DefaultAchievementRepository( - remoteConfigApi = remoteConfigApi, - achievementsDataStore = achievementsDataStore, - ) - } - } -} diff --git a/core/data/src/androidMain/kotlin/io/github/droidkaigi/confsched/data/user/DataStoreModule.kt b/core/data/src/androidMain/kotlin/io/github/droidkaigi/confsched/data/user/DataStoreModule.kt index a68ee6678..3fd7a89cd 100644 --- a/core/data/src/androidMain/kotlin/io/github/droidkaigi/confsched/data/user/DataStoreModule.kt +++ b/core/data/src/androidMain/kotlin/io/github/droidkaigi/confsched/data/user/DataStoreModule.kt @@ -21,9 +21,6 @@ public annotation class UserDataStoreQualifier @Qualifier public annotation class SessionCacheDataStoreQualifier -@Qualifier -public annotation class AchievementsDataStoreQualifier - @InstallIn(SingletonComponent::class) @Module public class DataStoreModule { @@ -48,21 +45,9 @@ public class DataStoreModule { producePath = { context.cacheDir.resolve(DATA_STORE_CACHE_PREFERENCE_FILE_NAME).path }, ) - @AchievementsDataStoreQualifier - @Provides - @Singleton - public fun provideAchievementsDataStore( - @ApplicationContext context: Context, - ): DataStore = createDataStore( - coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()), - producePath = { context.filesDir.resolve(DATA_STORE_ACHIEVEMENTS_FILE_NAME).path }, - ) - public companion object { private const val DATA_STORE_PREFERENCE_FILE_NAME = "confsched2024.preferences_pb" private const val DATA_STORE_CACHE_PREFERENCE_FILE_NAME = "confsched2024.cache.preferences_pb" - private const val DATA_STORE_ACHIEVEMENTS_FILE_NAME = - "confsched2024.achievements.preferences_pb" } } diff --git a/core/data/src/commonMain/kotlin/io/github/droidkaigi/confsched/data/achievements/AchievementsDataStore.kt b/core/data/src/commonMain/kotlin/io/github/droidkaigi/confsched/data/achievements/AchievementsDataStore.kt deleted file mode 100644 index 45c2343db..000000000 --- a/core/data/src/commonMain/kotlin/io/github/droidkaigi/confsched/data/achievements/AchievementsDataStore.kt +++ /dev/null @@ -1,75 +0,0 @@ -package io.github.droidkaigi.confsched.data.achievements - -import androidx.datastore.core.DataStore -import androidx.datastore.core.IOException -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.emptyPreferences -import androidx.datastore.preferences.core.stringPreferencesKey -import io.github.droidkaigi.confsched.model.Achievement -import kotlinx.collections.immutable.PersistentSet -import kotlinx.collections.immutable.toPersistentSet -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.map - -public class AchievementsDataStore(private val dataStore: DataStore) { - - public fun getAchievementsStream(): Flow> { - return dataStore.data - .catch { - emit(emptyPreferences()) - } - .map { preferences: Preferences -> - (preferences[KEY_ACHIEVEMENTS]?.split(",") ?: listOf()) - .mapNotNull { Achievement.ofOrNull(it) } - .toPersistentSet() - } - } - - public suspend fun saveAchievements(achievement: Achievement) { - val updatedAchievements = getAchievementsStream().first().toMutableSet() - - updatedAchievements.add(achievement) - - dataStore.edit { preferences -> - preferences[KEY_ACHIEVEMENTS] = updatedAchievements - .joinToString(",") { it.id } - } - } - - public suspend fun resetAchievements() { - dataStore.edit { preferences -> - preferences[KEY_ACHIEVEMENTS] = "" - } - } - - internal suspend fun saveInitialDialogDisplayState( - isInitialDialogDisplay: Boolean, - ) { - dataStore.edit { preferences -> - preferences[KEY_ACHIEVEMENTS_INITIAL_DIALOG_DISPLAY] = isInitialDialogDisplay.toString() - } - } - - public fun isInitialDialogDisplayStateStream(): Flow { - return dataStore.data - .catch { exception -> - if (exception is IOException) { - emit(emptyPreferences()) - } else { - throw exception - } - } - .map { preferences: Preferences -> - preferences[KEY_ACHIEVEMENTS_INITIAL_DIALOG_DISPLAY]?.toBoolean() ?: false - } - } - - private companion object { - private val KEY_ACHIEVEMENTS = stringPreferencesKey("KEY_ACHIEVEMENTS") - private val KEY_ACHIEVEMENTS_INITIAL_DIALOG_DISPLAY = - stringPreferencesKey("KEY_ACHIEVEMENTS_INITIAL_DIALOG_DISPLAY") - } -} diff --git a/core/data/src/commonMain/kotlin/io/github/droidkaigi/confsched/data/achievements/DefaultAchievementRepository.kt b/core/data/src/commonMain/kotlin/io/github/droidkaigi/confsched/data/achievements/DefaultAchievementRepository.kt deleted file mode 100644 index ec80a15c0..000000000 --- a/core/data/src/commonMain/kotlin/io/github/droidkaigi/confsched/data/achievements/DefaultAchievementRepository.kt +++ /dev/null @@ -1,68 +0,0 @@ -package io.github.droidkaigi.confsched.data.achievements - -import io.github.droidkaigi.confsched.data.remoteconfig.RemoteConfigApi -import io.github.droidkaigi.confsched.model.Achievement -import io.github.droidkaigi.confsched.model.AchievementRepository -import kotlinx.collections.immutable.PersistentSet -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.onStart - -public class DefaultAchievementRepository( - private val remoteConfigApi: RemoteConfigApi, - private val achievementsDataStore: AchievementsDataStore, -) : AchievementRepository { - private val isAchievementsEnabledStateFlow: MutableStateFlow = MutableStateFlow(false) - private val achievementDetailDescriptionStateFlow: MutableStateFlow = - MutableStateFlow("") - private val isResetAchievementsEnabledStateFlow: MutableStateFlow = - MutableStateFlow(false) - - override fun getAchievementEnabledStream(): Flow { - return isAchievementsEnabledStateFlow.onStart { - isAchievementsEnabledStateFlow.value = remoteConfigApi.getBoolean( - IS_ACHIEVEMENTS_ENABLED_KEY, - ) - } - } - - override fun getAchievementDetailDescriptionStream(): Flow { - return achievementDetailDescriptionStateFlow.onStart { - achievementDetailDescriptionStateFlow.value = - remoteConfigApi.getString(ACHIEVEMENT_DETAIL_DESCRIPTION_KEY) - } - } - - override fun getResetAchievementsEnabledStream(): Flow { - return isResetAchievementsEnabledStateFlow.onStart { - isResetAchievementsEnabledStateFlow.value = - remoteConfigApi.getBoolean(IS_RESET_ACHIEVEMENTS_ENABLED_KEY) - } - } - - override fun getAchievementsStream(): Flow> { - return achievementsDataStore.getAchievementsStream() - } - - override suspend fun saveAchievements(achievement: Achievement) { - achievementsDataStore.saveAchievements(achievement) - } - - override suspend fun resetAchievements() { - achievementsDataStore.resetAchievements() - } - - override fun getIsInitialDialogDisplayStateStream(): Flow { - return achievementsDataStore.isInitialDialogDisplayStateStream() - } - - override suspend fun displayedInitialDialog() { - achievementsDataStore.saveInitialDialogDisplayState(true) - } - - private companion object { - private const val IS_ACHIEVEMENTS_ENABLED_KEY = "is_achievements_enable" - private const val ACHIEVEMENT_DETAIL_DESCRIPTION_KEY = "achievements_detail_description" - private const val IS_RESET_ACHIEVEMENTS_ENABLED_KEY = "is_reset_achievements_enable" - } -} diff --git a/core/data/src/iosMain/kotlin/io/github/droidkaigi/confsched/data/DataModule.kt b/core/data/src/iosMain/kotlin/io/github/droidkaigi/confsched/data/DataModule.kt index fa410033d..e8455b62e 100644 --- a/core/data/src/iosMain/kotlin/io/github/droidkaigi/confsched/data/DataModule.kt +++ b/core/data/src/iosMain/kotlin/io/github/droidkaigi/confsched/data/DataModule.kt @@ -1,8 +1,6 @@ package io.github.droidkaigi.confsched.data import de.jensklingenberg.ktorfit.Ktorfit -import io.github.droidkaigi.confsched.data.achievements.AchievementsDataStore -import io.github.droidkaigi.confsched.data.achievements.DefaultAchievementRepository import io.github.droidkaigi.confsched.data.auth.AuthApi import io.github.droidkaigi.confsched.data.auth.DefaultAuthApi import io.github.droidkaigi.confsched.data.contributors.ContributorsApiClient @@ -21,7 +19,6 @@ import io.github.droidkaigi.confsched.data.staff.DefaultStaffApiClient import io.github.droidkaigi.confsched.data.staff.DefaultStaffRepository import io.github.droidkaigi.confsched.data.staff.StaffApiClient import io.github.droidkaigi.confsched.data.user.UserDataStore -import io.github.droidkaigi.confsched.model.AchievementRepository import io.github.droidkaigi.confsched.model.ContributorsRepository import io.github.droidkaigi.confsched.model.SessionsRepository import io.github.droidkaigi.confsched.model.SponsorsRepository @@ -114,22 +111,6 @@ public val dataModule: Module = module { ) SessionCacheDataStore(dataStore, get()) } - single { - val dataStore = createDataStore( - coroutineScope = CoroutineScope(Dispatchers.Default + SupervisorJob()), - producePath = { - val documentDirectory: NSURL? = NSFileManager.defaultManager.URLForDirectory( - directory = NSDocumentDirectory, - inDomain = NSUserDomainMask, - appropriateForURL = null, - create = false, - error = null, - ) - requireNotNull(documentDirectory).path + "/confsched2024.achievements.preferences_pb" - }, - ) - AchievementsDataStore(dataStore) - } singleOf(::DefaultAuthApi) bind AuthApi::class singleOf(::DefaultSessionsApiClient) bind SessionsApiClient::class @@ -138,7 +119,6 @@ public val dataModule: Module = module { singleOf(::DefaultStaffApiClient) bind StaffApiClient::class singleOf(::NetworkService) - singleOf(::DefaultAchievementRepository) bind AchievementRepository::class singleOf(::DefaultSessionsRepository) bind SessionsRepository::class singleOf(::DefaultContributorsRepository) bind ContributorsRepository::class singleOf(::DefaultStaffRepository) bind StaffRepository::class @@ -146,7 +126,6 @@ public val dataModule: Module = module { single { DefaultRepositories( mapOf( - AchievementRepository::class to get(), SessionsRepository::class to get(), ContributorsRepository::class to get(), StaffRepository::class to get(), diff --git a/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/theme/RoomTheme.kt b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/theme/RoomTheme.kt new file mode 100644 index 000000000..097c09589 --- /dev/null +++ b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/theme/RoomTheme.kt @@ -0,0 +1,69 @@ +package io.github.droidkaigi.confsched.designsystem.theme + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.ProvidableCompositionLocal +import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.graphics.Color + +sealed interface RoomTheme { + data object Iguana : RoomTheme { + override val primaryColor = Color(0xFF45E761) + override val containerColor = Color(0xFF45E761).copy(alpha = 0.1f) + override val dimColor = Color(0xFF132417) + } + + data object Hedgehog : RoomTheme { + override val primaryColor = Color(0xFFFF974B) + override val containerColor = Color(0xFFFF974B).copy(alpha = 0.1f) + override val dimColor = Color(0xFF251C15) + } + + data object Giraffe : RoomTheme { + override val primaryColor = Color(0xFFDDD33C) + override val containerColor = Color(0xFFDDD33C).copy(alpha = 0.1f) + override val dimColor = Color(0xFF222213) + } + + data object Flamingo : RoomTheme { + override val primaryColor = Color(0xFFBB85FF) + override val containerColor = Color(0xFFBB85FF).copy(alpha = 0.1f) + override val dimColor = Color(0xFF1E1A27) + } + + data object Jellyfish : RoomTheme { + override val primaryColor = Color(0xFF44ADE7) + override val containerColor = Color(0xFF44ADE7).copy(alpha = 0.1f) + override val dimColor = Color(0xFF121E25) + } + + val primaryColor: Color + val containerColor: Color + val dimColor: Color + + companion object { + fun ofOrNull(roomName: String): RoomTheme? { + return when (roomName.lowercase()) { + "iguana" -> Iguana + "hedgehog" -> Hedgehog + "giraffe" -> Giraffe + "flamingo" -> Flamingo + "jellyfish" -> Jellyfish + else -> null + } + } + } +} + +@Suppress("CompositionLocalAllowlist") +val LocalRoomTheme: ProvidableCompositionLocal = staticCompositionLocalOf { + error("No RoomTheme provided") +} + +@Composable +fun ProvideRoomTheme(roomName: String, content: @Composable () -> Unit) { + val roomTheme = RoomTheme.ofOrNull(roomName) ?: RoomTheme.Iguana + CompositionLocalProvider(LocalRoomTheme provides roomTheme) { + content() + } +} diff --git a/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/theme/Theme.kt b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/theme/Theme.kt index 42a326d27..52abbd8d6 100644 --- a/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/theme/Theme.kt +++ b/core/designsystem/src/commonMain/kotlin/io/github/droidkaigi/confsched/designsystem/theme/Theme.kt @@ -6,8 +6,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.Immutable -import androidx.compose.ui.graphics.Color private val lightScheme = lightColorScheme( primary = primaryLight, @@ -237,44 +235,6 @@ private val highContrastDarkColorScheme = darkColorScheme( surfaceContainerHighest = surfaceContainerHighestDarkHighContrast, ) -@Immutable -data class ColorFamily( - val color: Color, - val onColor: Color, - val colorContainer: Color, - val onColorContainer: Color, -) - -val unspecified_scheme = ColorFamily( - Color.Unspecified, - Color.Unspecified, - Color.Unspecified, - Color.Unspecified, -) - -sealed class HallColorScheme { - abstract val hallA: Color - abstract val hallB: Color - abstract val hallC: Color - abstract val hallD: Color - abstract val hallE: Color - abstract val hallText: Color - abstract val hallTextWhenWithoutSpeakers: Color - - data class Dark( - override val hallA: Color = md_theme_dark_room_hall_a, - override val hallB: Color = md_theme_dark_room_hall_b, - override val hallC: Color = md_theme_dark_room_hall_c, - override val hallD: Color = md_theme_dark_room_hall_d, - override val hallE: Color = md_theme_dark_room_hall_e, - override val hallText: Color = md_theme_dark_room_hall_text, - override val hallTextWhenWithoutSpeakers: Color = md_theme_dark_onSurfaceVariant, - ) : HallColorScheme() -} - -@Composable -fun hallColors() = HallColorScheme.Dark() - @Composable fun KaigiTheme( content: diff --git a/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/Achievement.kt b/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/Achievement.kt deleted file mode 100644 index f5be80ef2..000000000 --- a/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/Achievement.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.github.droidkaigi.confsched.model - -enum class Achievement(val id: String, val sha256: String) { - ArcticFox( - id = "Arctic Fox", - sha256 = "8370e6e6326c36f24b22f92a47ded5c3f1897aad46dfd9527016d2f3034d5f6e", - ), - Bumblebee( - id = "Bumblebee", - sha256 = "91b87cf77270a8df73d759282741a7e7a16c53c836c399006a82fb9f5d06025c", - ), - Chipmunk( - id = "Chipmunk", - sha256 = "7fd60aa90d14695e466a2ff181e883b1e77c4d8c88a3c4bd64025a47779edd1a", - ), - Dolphin( - id = "Dolphin", - sha256 = "cf27745b0afa6d34e6b45fa1940ddef63982f5e01c71c093b871f0d6ffe6f14d", - ), - ElectricEel( - id = "Electric Eel", - sha256 = "4e52d5a2f091279b365576319ee18384b3340e61f6c97a549ad8a7940c51d0bb", - ), - ; - companion object { - fun ofOrNull(id: String): Achievement? { - return entries.find { it.id == id } - } - } -} diff --git a/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/AchievementAnimation.kt b/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/AchievementAnimation.kt deleted file mode 100644 index 5257532a3..000000000 --- a/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/AchievementAnimation.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.droidkaigi.confsched.model - -data class AchievementAnimation( - val achievement: Achievement, - val hasDrawableResId: Int, - val notHasDrawableResId: Int, - val hasAchievement: Boolean = false, - val contentDescription: String, -) { - fun getDrawableResId() = if (hasAchievement) hasDrawableResId else notHasDrawableResId -} diff --git a/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/AchievementRepository.kt b/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/AchievementRepository.kt deleted file mode 100644 index c80f9a345..000000000 --- a/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/AchievementRepository.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.github.droidkaigi.confsched.model - -import androidx.compose.runtime.Composable -import io.github.droidkaigi.confsched.model.compositionlocal.LocalRepositories -import kotlinx.collections.immutable.PersistentSet -import kotlinx.coroutines.flow.Flow - -interface AchievementRepository { - fun getAchievementEnabledStream(): Flow - fun getAchievementDetailDescriptionStream(): Flow - fun getResetAchievementsEnabledStream(): Flow - fun getAchievementsStream(): Flow> - fun getIsInitialDialogDisplayStateStream(): Flow - suspend fun saveAchievements(achievement: Achievement) - suspend fun resetAchievements() - suspend fun displayedInitialDialog() -} - -@Composable -fun localAchievementRepository(): AchievementRepository { - return LocalRepositories.current[AchievementRepository::class] as AchievementRepository -} diff --git a/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/EventMapEvent.kt b/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/EventMapEvent.kt index d2a5665fd..c9747308f 100644 --- a/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/EventMapEvent.kt +++ b/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/EventMapEvent.kt @@ -19,7 +19,7 @@ fun createSampleEventMapEvent( isFavorite: Boolean = false, ) = EventMapEvent( name = "ランチミートアップ", - roomName = "Arctic Fox", + roomName = "Iguana", dateLabel = "DAY1", isFavorite = isFavorite, description = "様々なテーマごとに集まって、一緒にランチを食べながらお話ししましょう。席に限りがありますので、お弁当受け取り後お早めにお越しください。", diff --git a/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/TimetableRoom.kt b/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/TimetableRoom.kt index 48919271b..28c8cd850 100644 --- a/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/TimetableRoom.kt +++ b/core/model/src/commonMain/kotlin/io/github/droidkaigi/confsched/model/TimetableRoom.kt @@ -1,7 +1,5 @@ package io.github.droidkaigi.confsched.model -import androidx.compose.ui.graphics.Color - @Immutable data class TimetableRooms(val rooms: List) @@ -18,25 +16,7 @@ data class TimetableRoom( return sort.compareTo(other.sort) } - fun getColor(): Color { - return when (name.enTitle) { - "Chipmunk" -> { - Color(0xFFFF974B) - } - "Dolphin" -> { - Color(0xFFBB85FF) - } - "Bumblebee" -> { - Color(0xFFDDD33C) - } - "Arctic Fox" -> { - Color(0xFF45E761) - } - else -> { - Color.White - } - } - } + fun getThemeKey(): String = name.enTitle.lowercase() fun getShape(): Shapes { return when (name.enTitle) { diff --git a/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/data/TestDataStoreModule.kt b/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/data/TestDataStoreModule.kt index cfa539bd1..e2e323c39 100644 --- a/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/data/TestDataStoreModule.kt +++ b/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/data/TestDataStoreModule.kt @@ -9,7 +9,6 @@ import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import dagger.hilt.testing.TestInstallIn import io.github.droidkaigi.confsched.data.createDataStore -import io.github.droidkaigi.confsched.data.user.AchievementsDataStoreQualifier import io.github.droidkaigi.confsched.data.user.DataStoreModule import io.github.droidkaigi.confsched.data.user.SessionCacheDataStoreQualifier import io.github.droidkaigi.confsched.data.user.UserDataStoreQualifier @@ -48,23 +47,8 @@ class TestDataStoreModule { }, ) - @Provides - @Singleton - @AchievementsDataStoreQualifier - fun provideAchievementsDataStore( - @ApplicationContext context: Context, - testDispatcher: TestDispatcher, - ): DataStore = createDataStore( - coroutineScope = CoroutineScope(testDispatcher + Job()), - producePath = { - context.cacheDir.resolve(TEST_DATASTORE_ACHIEVEMENTS_NAME).path - }, - ) - companion object { private const val TEST_DATASTORE_NAME = "test_datastore.preferences_pb" private const val TEST_DATASTORE_CACHE_NAME = "test_datastore_cache.preferences_pb" - private const val TEST_DATASTORE_ACHIEVEMENTS_NAME = - "confsched2024.achievements.preferences_pb" } } diff --git a/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/KaigiAppRobot.kt b/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/KaigiAppRobot.kt index 73caa7eab..620bf4dad 100644 --- a/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/KaigiAppRobot.kt +++ b/core/testing/src/main/java/io/github/droidkaigi/confsched/testing/robot/KaigiAppRobot.kt @@ -1,7 +1,6 @@ package io.github.droidkaigi.confsched.testing.robot import androidx.compose.ui.test.hasTestTag -import androidx.compose.ui.test.onFirst import androidx.compose.ui.test.performClick import io.github.droidkaigi.confsched.main.MainScreenTab import io.github.droidkaigi.confsched.testing.DefaultScreenRobot @@ -26,15 +25,4 @@ class KaigiAppRobot @Inject constructor( .performClick() waitUntilIdle() } - - fun goToAchievements() { - composeTestRule - .onAllNodes( - matcher = hasTestTag(MainScreenTab.ProfileCard.testTag), - useUnmergedTree = true, - ) - .onFirst() - .performClick() - waitUntilIdle() - } } diff --git a/feature/eventmap/src/commonMain/kotlin/io/github/droidkaigi/confsched/eventmap/component/EventMapItem.kt b/feature/eventmap/src/commonMain/kotlin/io/github/droidkaigi/confsched/eventmap/component/EventMapItem.kt index d8b003a48..cad53bead 100644 --- a/feature/eventmap/src/commonMain/kotlin/io/github/droidkaigi/confsched/eventmap/component/EventMapItem.kt +++ b/feature/eventmap/src/commonMain/kotlin/io/github/droidkaigi/confsched/eventmap/component/EventMapItem.kt @@ -24,6 +24,8 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import io.github.droidkaigi.confsched.designsystem.theme.LocalRoomTheme +import io.github.droidkaigi.confsched.designsystem.theme.ProvideRoomTheme import io.github.droidkaigi.confsched.model.EventMapEvent @Composable @@ -34,68 +36,70 @@ fun EventMapItem( onClickFavorite: (eventMapEvent: EventMapEvent) -> Unit, modifier: Modifier = Modifier, ) { - val green = Color(0xFF45E761) - val gray = Color(0xFFC5C7C4) - Column( - modifier = modifier - .border(1.dp, gray, RoundedCornerShape(5.dp)) - .background(Color.Transparent, RoundedCornerShape(5.dp)) - .clickable { + ProvideRoomTheme(eventMapEvent.roomName) { + val green = LocalRoomTheme.current.primaryColor + val gray = Color(0xFFC5C7C4) + Column( + modifier = modifier + .border(1.dp, gray, RoundedCornerShape(5.dp)) + .background(Color.Transparent, RoundedCornerShape(5.dp)) + .clickable { // eventMapEvent.profileUrl?.let(onClick) - } - .padding(12.dp), - verticalArrangement = Arrangement.Top, - horizontalAlignment = Alignment.Start, - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Start, + } + .padding(12.dp), + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.Start, ) { - ToolTip( - text = eventMapEvent.roomName, - icon = Icons.Filled.Star, - color = green, + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start, + ) { + ToolTip( + text = eventMapEvent.roomName, + icon = Icons.Filled.Star, + color = green, + ) + Spacer(Modifier.width(4.dp)) + ToolTip( + text = eventMapEvent.dateLabel, + color = Color(0xFFBFC9C2), + ) + Spacer(Modifier.weight(1F)) + Icon( + imageVector = Icons.Default.Star, + contentDescription = null, + modifier = Modifier.size(24.dp).clickable { onClickFavorite(eventMapEvent) }, + tint = if (eventMapEvent.isFavorite) green else gray, + ) + } + Spacer(Modifier.height(8.dp)) + Text( + text = eventMapEvent.name, + fontSize = 17.sp, + lineHeight = 23.8.sp, + fontWeight = FontWeight.W600, + letterSpacing = 0.1.sp, + color = gray, ) - Spacer(Modifier.width(4.dp)) - ToolTip( - text = eventMapEvent.dateLabel, - color = Color(0xFFBFC9C2), + Spacer(Modifier.height(8.dp)) + Text( + text = eventMapEvent.description, + fontSize = 13.sp, + lineHeight = 20.sp, + fontWeight = FontWeight.W400, + letterSpacing = 0.25.sp, + color = Color.White.copy(alpha = 0.7F), ) - Spacer(Modifier.weight(1F)) - Icon( - imageVector = Icons.Default.Star, - contentDescription = null, - modifier = Modifier.size(24.dp).clickable { onClickFavorite(eventMapEvent) }, - tint = if (eventMapEvent.isFavorite) green else gray, + Spacer(Modifier.height(8.dp)) + Text( + text = eventMapEvent.timeDuration, + fontSize = 11.sp, + lineHeight = 15.sp, + fontWeight = FontWeight.W600, + letterSpacing = 0.1.sp, + color = green, ) } - Spacer(Modifier.height(8.dp)) - Text( - text = eventMapEvent.name, - fontSize = 17.sp, - lineHeight = 23.8.sp, - fontWeight = FontWeight.W600, - letterSpacing = 0.1.sp, - color = gray, - ) - Spacer(Modifier.height(8.dp)) - Text( - text = eventMapEvent.description, - fontSize = 13.sp, - lineHeight = 20.sp, - fontWeight = FontWeight.W400, - letterSpacing = 0.25.sp, - color = Color.White.copy(alpha = 0.7F), - ) - Spacer(Modifier.height(8.dp)) - Text( - text = eventMapEvent.timeDuration, - fontSize = 11.sp, - lineHeight = 15.sp, - fontWeight = FontWeight.W600, - letterSpacing = 0.1.sp, - color = green, - ) } } diff --git a/feature/main/src/commonMain/kotlin/io/github/droidkaigi/confsched/main/MainScreen.kt b/feature/main/src/commonMain/kotlin/io/github/droidkaigi/confsched/main/MainScreen.kt index 014f03b7a..5c0c1d035 100644 --- a/feature/main/src/commonMain/kotlin/io/github/droidkaigi/confsched/main/MainScreen.kt +++ b/feature/main/src/commonMain/kotlin/io/github/droidkaigi/confsched/main/MainScreen.kt @@ -175,7 +175,6 @@ enum class MainScreenTab( } data class MainScreenUiState( - val isAchievementsEnabled: Boolean = false, val userMessageStateHolder: UserMessageStateHolder, ) diff --git a/feature/main/src/commonMain/kotlin/io/github/droidkaigi/confsched/main/MainScreenPresenter.kt b/feature/main/src/commonMain/kotlin/io/github/droidkaigi/confsched/main/MainScreenPresenter.kt index 6f7fe5d83..f3de20cb6 100644 --- a/feature/main/src/commonMain/kotlin/io/github/droidkaigi/confsched/main/MainScreenPresenter.kt +++ b/feature/main/src/commonMain/kotlin/io/github/droidkaigi/confsched/main/MainScreenPresenter.kt @@ -2,9 +2,6 @@ package io.github.droidkaigi.confsched.main import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import io.github.droidkaigi.confsched.compose.safeCollectAsRetainedState -import io.github.droidkaigi.confsched.model.AchievementRepository -import io.github.droidkaigi.confsched.model.localAchievementRepository import io.github.droidkaigi.confsched.ui.providePresenterDefaults import kotlinx.coroutines.flow.Flow @@ -14,15 +11,8 @@ sealed interface MainScreenEvent fun mainScreenPresenter( @Suppress("UnusedParameter") events: Flow, - achievementRepository: AchievementRepository = localAchievementRepository(), ): MainScreenUiState = providePresenterDefaults { userMessageStateHolder -> - val isAchievementsEnabled: Boolean by achievementRepository - .getAchievementEnabledStream() - .safeCollectAsRetainedState( - initial = false, - ) MainScreenUiState( - isAchievementsEnabled = isAchievementsEnabled, userMessageStateHolder = userMessageStateHolder, ) } diff --git a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableItemDetailPresenter.kt b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableItemDetailPresenter.kt index 7f8c6532d..c2778857a 100644 --- a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableItemDetailPresenter.kt +++ b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableItemDetailPresenter.kt @@ -88,6 +88,7 @@ fun timetableItemDetailPresenter( isBookmarked = bookmarked, isLangSelectable = timetableItem.sessionType == NORMAL, currentLang = selectedDescriptionLanguage, + roomThemeKey = timetableItem.room.getThemeKey(), userMessageStateHolder = userMessageStateHolder, ) } diff --git a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableItemDetailScreen.kt b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableItemDetailScreen.kt index d4ea4ebd5..fa975d8a2 100644 --- a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableItemDetailScreen.kt +++ b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableItemDetailScreen.kt @@ -28,16 +28,17 @@ import io.github.droidkaigi.confsched.compose.EventEmitter import io.github.droidkaigi.confsched.compose.rememberEventEmitter import io.github.droidkaigi.confsched.designsystem.component.LoadingText import io.github.droidkaigi.confsched.designsystem.theme.KaigiTheme +import io.github.droidkaigi.confsched.designsystem.theme.ProvideRoomTheme import io.github.droidkaigi.confsched.model.Lang import io.github.droidkaigi.confsched.model.TimetableItem import io.github.droidkaigi.confsched.model.TimetableItem.Session import io.github.droidkaigi.confsched.model.fake import io.github.droidkaigi.confsched.sessions.TimetableItemDetailScreenUiState.Loaded import io.github.droidkaigi.confsched.sessions.TimetableItemDetailScreenUiState.Loading -import io.github.droidkaigi.confsched.sessions.component.TimeTableItemDetailContent -import io.github.droidkaigi.confsched.sessions.component.TimeTableItemDetailHeadline -import io.github.droidkaigi.confsched.sessions.component.TimeTableItemDetailSummaryCard import io.github.droidkaigi.confsched.sessions.component.TimetableItemDetailBottomAppBar +import io.github.droidkaigi.confsched.sessions.component.TimetableItemDetailContent +import io.github.droidkaigi.confsched.sessions.component.TimetableItemDetailHeadline +import io.github.droidkaigi.confsched.sessions.component.TimetableItemDetailSummaryCard import io.github.droidkaigi.confsched.sessions.component.TimetableItemDetailTopAppBar import io.github.droidkaigi.confsched.ui.SnackbarMessageEffect import io.github.droidkaigi.confsched.ui.UserMessageStateHolder @@ -113,12 +114,14 @@ sealed interface TimetableItemDetailScreenUiState { data class Loading( override val userMessageStateHolder: UserMessageStateHolder, ) : TimetableItemDetailScreenUiState + data class Loaded( val timetableItem: TimetableItem, val timetableItemDetailSectionUiState: TimetableItemDetailSectionUiState, val isBookmarked: Boolean, val isLangSelectable: Boolean, val currentLang: Lang?, + val roomThemeKey: String, override val userMessageStateHolder: UserMessageStateHolder, ) : TimetableItemDetailScreenUiState @@ -148,12 +151,14 @@ private fun TimetableItemDetailScreen( .nestedScroll(scrollBehavior.nestedScrollConnection), topBar = { if (uiState is Loaded) { - TimetableItemDetailTopAppBar( - isLangSelectable = uiState.isLangSelectable, - onNavigationIconClick = onNavigationIconClick, - onSelectedLanguage = onSelectedLanguage, - scrollBehavior = scrollBehavior, - ) + ProvideRoomTheme(uiState.roomThemeKey) { + TimetableItemDetailTopAppBar( + isLangSelectable = uiState.isLangSelectable, + onNavigationIconClick = onNavigationIconClick, + onSelectedLanguage = onSelectedLanguage, + scrollBehavior = scrollBehavior, + ) + } } }, bottomBar = { @@ -170,27 +175,29 @@ private fun TimetableItemDetailScreen( snackbarHost = { SnackbarHost(hostState = snackbarHostState) }, ) { innerPadding -> if (uiState is Loaded) { - LazyColumn( - modifier = Modifier.fillMaxSize().padding(innerPadding), - ) { - item { - TimeTableItemDetailHeadline( - timetableItem = uiState.timetableItem, - ) - } + ProvideRoomTheme(uiState.roomThemeKey) { + LazyColumn( + modifier = Modifier.fillMaxSize().padding(innerPadding), + ) { + item { + TimetableItemDetailHeadline( + timetableItem = uiState.timetableItem, + ) + } - item { - TimeTableItemDetailSummaryCard( - timetableItem = uiState.timetableItem, - ) - } + item { + TimetableItemDetailSummaryCard( + timetableItem = uiState.timetableItem, + ) + } - item { - TimeTableItemDetailContent( - timetableItem = uiState.timetableItem, - currentLang = uiState.currentLang, - onLinkClick = onLinkClick, - ) + item { + TimetableItemDetailContent( + timetableItem = uiState.timetableItem, + currentLang = uiState.currentLang, + onLinkClick = onLinkClick, + ) + } } } } @@ -227,6 +234,7 @@ fun TimetableItemDetailScreenPreview() { isBookmarked = isBookMarked, isLangSelectable = true, currentLang = Lang.JAPANESE, + roomThemeKey = "iguana", userMessageStateHolder = UserMessageStateHolderImpl(), ), onNavigationIconClick = {}, diff --git a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimeTableItemDetailContent.kt b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimeTableItemDetailContent.kt index c3bf1a61f..12f389c34 100644 --- a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimeTableItemDetailContent.kt +++ b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimeTableItemDetailContent.kt @@ -24,11 +24,12 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import io.github.droidkaigi.confsched.designsystem.component.ClickableLinkText import io.github.droidkaigi.confsched.designsystem.theme.KaigiTheme +import io.github.droidkaigi.confsched.designsystem.theme.LocalRoomTheme +import io.github.droidkaigi.confsched.designsystem.theme.ProvideRoomTheme import io.github.droidkaigi.confsched.model.Lang import io.github.droidkaigi.confsched.model.MultiLangText import io.github.droidkaigi.confsched.model.TimetableItem @@ -38,7 +39,7 @@ import io.github.droidkaigi.confsched.model.fake import org.jetbrains.compose.ui.tooling.preview.Preview @Composable -fun TimeTableItemDetailContent( +fun TimetableItemDetailContent( timetableItem: TimetableItem, currentLang: Lang?, modifier: Modifier = Modifier, @@ -101,8 +102,7 @@ private fun DescriptionSection( Text( text = "続きを読む", style = MaterialTheme.typography.labelLarge, - // FIXME: Implement and use a theme color instead of fixed colors like RoomColors.primary and RoomColors.primaryDim - color = Color(0xFF45E761), + color = LocalRoomTheme.current.primaryColor, ) } } @@ -119,8 +119,7 @@ private fun TargetAudienceSection( Text( text = "対象者", style = MaterialTheme.typography.titleLarge, - // FIXME: Implement and use a theme color instead of fixed colors like RoomColors.primary and RoomColors.primaryDim - color = Color(0xFF45E761), + color = LocalRoomTheme.current.primaryColor, ) Spacer(Modifier.height(8.dp)) Text( @@ -142,8 +141,7 @@ private fun ArchiveSection( Text( text = "アーカイブ", style = MaterialTheme.typography.titleLarge, - // FIXME: Implement and use a theme color instead of fixed colors like RoomColors.primary and RoomColors.primaryDim - color = Color(0xFF45E761), + color = LocalRoomTheme.current.primaryColor, ) Spacer(Modifier.height(8.dp)) Row( @@ -154,8 +152,7 @@ private fun ArchiveSection( modifier = Modifier.weight(1f), onClick = { onViewSlideClick(slideUrl) }, colors = ButtonDefaults.buttonColors().copy( - // FIXME: Implement and use a theme color instead of fixed colors like RoomColors.primary and RoomColors.primaryDim - containerColor = Color(0xFF45E761), + containerColor = LocalRoomTheme.current.primaryColor, ), ) { Icon( @@ -174,8 +171,7 @@ private fun ArchiveSection( modifier = Modifier.weight(1f), onClick = { onWatchVideoClick(videoUrl) }, colors = ButtonDefaults.buttonColors().copy( - // FIXME: Implement and use a theme color instead of fixed colors like RoomColors.primary and RoomColors.primaryDim - containerColor = Color(0xFF45E761), + containerColor = LocalRoomTheme.current.primaryColor, ), ) { Icon( @@ -194,42 +190,55 @@ private fun ArchiveSection( @Composable @Preview -fun TimeTableItemDetailContentPreview() { +fun TimetableItemDetailContentPreview() { KaigiTheme { - Surface { - TimeTableItemDetailContent( - timetableItem = Session.fake(), - currentLang = Lang.JAPANESE, - onLinkClick = {}, - ) + ProvideFakeRoomTheme { + Surface { + TimetableItemDetailContent( + timetableItem = Session.fake(), + currentLang = Lang.JAPANESE, + onLinkClick = {}, + ) + } } } } @Composable @Preview -fun TimeTableItemDetailContentWithEnglishPreview() { +fun TimetableItemDetailContentWithEnglishPreview() { KaigiTheme { - Surface { - TimeTableItemDetailContent( - timetableItem = Session.fake(), - currentLang = Lang.ENGLISH, - onLinkClick = {}, - ) + ProvideFakeRoomTheme { + Surface { + TimetableItemDetailContent( + timetableItem = Session.fake(), + currentLang = Lang.ENGLISH, + onLinkClick = {}, + ) + } } } } @Composable @Preview -fun TimeTableItemDetailContentWithMixedPreview() { +fun TimetableItemDetailContentWithMixedPreview() { KaigiTheme { - Surface { - TimeTableItemDetailContent( - timetableItem = Session.fake(), - currentLang = Lang.MIXED, - onLinkClick = {}, - ) + ProvideFakeRoomTheme { + Surface { + TimetableItemDetailContent( + timetableItem = Session.fake(), + currentLang = Lang.MIXED, + onLinkClick = {}, + ) + } } } } + +@Composable +fun ProvideFakeRoomTheme(content: @Composable () -> Unit) { + ProvideRoomTheme(Session.fake().room.getThemeKey()) { + content() + } +} diff --git a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimeTableItemDetailHeadline.kt b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimeTableItemDetailHeadline.kt index 97aacddef..d01996a03 100644 --- a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimeTableItemDetailHeadline.kt +++ b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimeTableItemDetailHeadline.kt @@ -19,9 +19,9 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import io.github.droidkaigi.confsched.designsystem.theme.KaigiTheme +import io.github.droidkaigi.confsched.designsystem.theme.LocalRoomTheme import io.github.droidkaigi.confsched.model.TimetableItem import io.github.droidkaigi.confsched.model.fake import io.github.droidkaigi.confsched.sessions.section.TagView @@ -29,22 +29,21 @@ import io.github.droidkaigi.confsched.ui.rememberAsyncImagePainter import org.jetbrains.compose.ui.tooling.preview.Preview @Composable -fun TimeTableItemDetailHeadline( +fun TimetableItemDetailHeadline( timetableItem: TimetableItem, modifier: Modifier = Modifier, ) { Column( modifier = modifier // FIXME: Implement and use a theme color instead of fixed colors like RoomColors.primary and RoomColors.primaryDim - .background(Color(0xFF132417)) + .background(LocalRoomTheme.current.dimColor) .padding(8.dp) .fillMaxWidth(), ) { Row { TagView( tagText = timetableItem.room.name.currentLangTitle, - // FIXME: Implement and use a theme color instead of fixed colors like RoomColors.primary and RoomColors.primaryDim - tagColor = Color(0xFF45E761), + tagColor = LocalRoomTheme.current.primaryColor, ) timetableItem.language.labels.forEach { label -> Spacer(modifier = Modifier.padding(4.dp)) @@ -91,12 +90,14 @@ fun TimeTableItemDetailHeadline( @Composable @Preview -fun TimeTableItemDetailHeadlinePreview() { +fun TimetableItemDetailHeadlinePreview() { KaigiTheme { - Surface { - TimeTableItemDetailHeadline( - timetableItem = TimetableItem.Session.fake(), - ) + ProvideFakeRoomTheme { + Surface { + TimetableItemDetailHeadline( + timetableItem = TimetableItem.Session.fake(), + ) + } } } } diff --git a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimeTableItemDetailSummaryCard.kt b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimeTableItemDetailSummaryCard.kt index 022a11295..c18bfe917 100644 --- a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimeTableItemDetailSummaryCard.kt +++ b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimeTableItemDetailSummaryCard.kt @@ -21,12 +21,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.CornerRadius -import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathEffect import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.unit.dp import io.github.droidkaigi.confsched.designsystem.theme.KaigiTheme +import io.github.droidkaigi.confsched.designsystem.theme.LocalRoomTheme import io.github.droidkaigi.confsched.model.Locale import io.github.droidkaigi.confsched.model.TimetableItem import io.github.droidkaigi.confsched.model.fake @@ -35,10 +35,11 @@ import io.github.droidkaigi.confsched.model.nameAndFloor import org.jetbrains.compose.ui.tooling.preview.Preview @Composable -fun TimeTableItemDetailSummaryCard( +fun TimetableItemDetailSummaryCard( timetableItem: TimetableItem, modifier: Modifier = Modifier, ) { + val primaryColor = LocalRoomTheme.current.primaryColor Column( modifier = modifier .padding( @@ -49,8 +50,7 @@ fun TimeTableItemDetailSummaryCard( ) .drawBehind { drawRoundRect( - // FIXME: Implement and use a theme color instead of fixed colors like RoomColors.primary and RoomColors.primaryDim - color = Color(0xFF45E761), + color = primaryColor, style = Stroke( width = 2f, pathEffect = PathEffect.dashPathEffect(floatArrayOf(5f, 5f), 0f), @@ -108,14 +108,12 @@ private fun SummaryCardRow( modifier = modifier, verticalAlignment = Alignment.CenterVertically, ) { - // FIXME: Implement and use a theme color instead of fixed colors like RoomColors.primary and RoomColors.primaryDim - Icon(imageVector = imageVector, contentDescription = contentDescription, tint = Color(0xFF45E761)) + Icon(imageVector = imageVector, contentDescription = contentDescription, tint = LocalRoomTheme.current.primaryColor) Spacer(Modifier.width(8.dp)) Text( text = title, style = MaterialTheme.typography.titleSmall, - // FIXME: Implement and use a theme color instead of fixed colors like RoomColors.primary and RoomColors.primaryDim - color = Color(0xFF45E761), + color = LocalRoomTheme.current.primaryColor, ) Spacer(Modifier.width(8.dp)) Text( @@ -127,12 +125,14 @@ private fun SummaryCardRow( @Composable @Preview -fun TimeTableItemDetailSummaryCardPreview() { +fun TimetableItemDetailSummaryCardPreview() { KaigiTheme { - Surface { - TimeTableItemDetailSummaryCard( - timetableItem = TimetableItem.Session.fake(), - ) + ProvideFakeRoomTheme { + Surface { + TimetableItemDetailSummaryCard( + timetableItem = TimetableItem.Session.fake(), + ) + } } } } diff --git a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableGridItem.kt b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableGridItem.kt index 45b1c4421..af8b61963 100644 --- a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableGridItem.kt +++ b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableGridItem.kt @@ -36,7 +36,8 @@ import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.github.droidkaigi.confsched.designsystem.theme.KaigiTheme -import io.github.droidkaigi.confsched.designsystem.theme.hallColors +import io.github.droidkaigi.confsched.designsystem.theme.LocalRoomTheme +import io.github.droidkaigi.confsched.designsystem.theme.ProvideRoomTheme import io.github.droidkaigi.confsched.designsystem.theme.md_theme_light_outline import io.github.droidkaigi.confsched.model.MultiLangText import io.github.droidkaigi.confsched.model.RoomType.RoomC @@ -81,122 +82,108 @@ fun TimetableGridItem( val speaker = timetableItem.speakers.firstOrNull() val speakers = timetableItem.speakers - val hallColor = hallColors() - val backgroundColor = timetableItem.room.getColor() - val textColor = if (speaker != null) { - hallColor.hallText - } else { - hallColor.hallTextWhenWithoutSpeakers - } - val height = with(localDensity) { gridItemHeightPx.toDp() } - val titleTextStyle = MaterialTheme.typography.labelLarge.let { - check(it.fontSize.isSp) - val (titleFontSize, titleLineHeight) = calculateFontSizeAndLineHeight( - textStyle = it, - localDensity = localDensity, - gridItemHeightPx = gridItemHeightPx, - speaker = speaker, - titleLength = timetableItem.title.currentLangTitle.length, - ) - it.copy(fontSize = titleFontSize, lineHeight = titleLineHeight, color = textColor) - } - Column( - modifier = modifier - .background( - color = if (speakers.isEmpty()) { - MaterialTheme.colorScheme.surfaceVariant - } else { - backgroundColor - }, - shape = RoundedCornerShape(4.dp), + ProvideRoomTheme(timetableItem.room.getThemeKey()) { + val titleTextStyle = MaterialTheme.typography.labelLarge.let { + check(it.fontSize.isSp) + val (titleFontSize, titleLineHeight) = calculateFontSizeAndLineHeight( + textStyle = it, + localDensity = localDensity, + gridItemHeightPx = gridItemHeightPx, + speaker = speaker, + titleLength = timetableItem.title.currentLangTitle.length, ) - .width(TimetableGridItemSizes.width) - .height(height) - .clickable { - onTimetableItemClick(timetableItem) - } - .padding(TimetableGridItemSizes.padding), - ) { + it.copy(fontSize = titleFontSize, lineHeight = titleLineHeight, color = LocalRoomTheme.current.primaryColor) + } Column( - modifier = Modifier.weight(3f), - verticalArrangement = Arrangement.Top, - ) { - Text( - modifier = Modifier.weight(1f, fill = false), - text = timetableItem.title.currentLangTitle, - style = titleTextStyle, - overflow = TextOverflow.Ellipsis, - ) - - Row( - modifier = Modifier - .weight(1f, fill = false) - .padding(top = TimetableGridItemSizes.titleToSchedulePadding), - ) { - Icon( - modifier = Modifier.height(TimetableGridItemSizes.scheduleHeight), - imageVector = Icons.Default.Schedule, - tint = if (speaker != null) { - hallColor.hallText - } else { - hallColor.hallTextWhenWithoutSpeakers - }, - contentDescription = ScheduleIcon.asString(), + modifier = modifier + .background( + color = LocalRoomTheme.current.containerColor, + shape = RoundedCornerShape(4.dp), ) - Spacer(modifier = Modifier.width(4.dp)) - var scheduleTextStyle = MaterialTheme.typography.bodySmall - if (titleTextStyle.fontSize < scheduleTextStyle.fontSize) { - scheduleTextStyle = scheduleTextStyle.copy(fontSize = titleTextStyle.fontSize) + .width(TimetableGridItemSizes.width) + .height(height) + .clickable { + onTimetableItemClick(timetableItem) } + .padding(TimetableGridItemSizes.padding), + ) { + Column( + modifier = Modifier.weight(3f), + verticalArrangement = Arrangement.Top, + ) { Text( - text = "${timetableItem.startsTimeString} - ${timetableItem.endsTimeString}", - style = scheduleTextStyle, - color = textColor, + modifier = Modifier.weight(1f, fill = false), + text = timetableItem.title.currentLangTitle, + style = titleTextStyle, + overflow = TextOverflow.Ellipsis, ) - } - } - val shouldShowError = timetableItem is Session && timetableItem.message != null + Row( + modifier = Modifier + .weight(1f, fill = false) + .padding(top = TimetableGridItemSizes.titleToSchedulePadding), + ) { + Icon( + modifier = Modifier.height(TimetableGridItemSizes.scheduleHeight), + imageVector = Icons.Default.Schedule, + contentDescription = ScheduleIcon.asString(), + ) + Spacer(modifier = Modifier.width(4.dp)) + var scheduleTextStyle = MaterialTheme.typography.bodySmall + if (titleTextStyle.fontSize < scheduleTextStyle.fontSize) { + scheduleTextStyle = + scheduleTextStyle.copy(fontSize = titleTextStyle.fontSize) + } + Text( + text = "${timetableItem.startsTimeString} - ${timetableItem.endsTimeString}", + style = scheduleTextStyle, + color = LocalRoomTheme.current.primaryColor, + ) + } + } - if (speakers.isNotEmpty() || shouldShowError) { - Row( - modifier = Modifier.weight(1f, fill = false), - verticalAlignment = Alignment.CenterVertically, - ) { - if (speakers.isNotEmpty()) { - val speakerModifier = Modifier.weight(1f) - if (speakers.size == 1) { - var speakerTextStyle = MaterialTheme.typography.labelMedium - if (titleTextStyle.fontSize < speakerTextStyle.fontSize) { - speakerTextStyle = - speakerTextStyle.copy(fontSize = titleTextStyle.fontSize) + val shouldShowError = timetableItem is Session && timetableItem.message != null + + if (speakers.isNotEmpty() || shouldShowError) { + Row( + modifier = Modifier.weight(1f, fill = false), + verticalAlignment = Alignment.CenterVertically, + ) { + if (speakers.isNotEmpty()) { + val speakerModifier = Modifier.weight(1f) + if (speakers.size == 1) { + var speakerTextStyle = MaterialTheme.typography.labelMedium + if (titleTextStyle.fontSize < speakerTextStyle.fontSize) { + speakerTextStyle = + speakerTextStyle.copy(fontSize = titleTextStyle.fontSize) + } + SingleSpeaker( + speaker = speakers.first(), + textColor = MaterialTheme.colorScheme.onSurface, + textStyle = speakerTextStyle, + modifier = speakerModifier, + ) + } else { + MultiSpeakers( + speakers = speakers, + modifier = speakerModifier, + ) } - SingleSpeaker( - speaker = speakers.first(), - textColor = textColor, - textStyle = speakerTextStyle, - modifier = speakerModifier, - ) } else { - MultiSpeakers( - speakers = speakers, - modifier = speakerModifier, - ) + Spacer(modifier = Modifier.weight(1f)) } - } else { - Spacer(modifier = Modifier.weight(1f)) - } - if (shouldShowError) { - Icon( - modifier = Modifier - .size(TimetableGridItemSizes.errorHeight), - imageVector = Icons.Default.Error, - contentDescription = SessionsStrings.ErrorIcon.asString(), - tint = MaterialTheme.colorScheme.errorContainer, - ) + if (shouldShowError) { + Icon( + modifier = Modifier + .size(TimetableGridItemSizes.errorHeight), + imageVector = Icons.Default.Error, + contentDescription = SessionsStrings.ErrorIcon.asString(), + tint = MaterialTheme.colorScheme.errorContainer, + ) + } } } } diff --git a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableItemDetailTopAppBar.kt b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableItemDetailTopAppBar.kt index 70cc6c117..004938cae 100644 --- a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableItemDetailTopAppBar.kt +++ b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/component/TimetableItemDetailTopAppBar.kt @@ -20,8 +20,8 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import io.github.droidkaigi.confsched.designsystem.theme.KaigiTheme +import io.github.droidkaigi.confsched.designsystem.theme.LocalRoomTheme import io.github.droidkaigi.confsched.model.Lang import org.jetbrains.compose.ui.tooling.preview.Preview @@ -37,9 +37,8 @@ fun TimetableItemDetailTopAppBar( TopAppBar( modifier = modifier, colors = TopAppBarDefaults.topAppBarColors().copy( - // FIXME: Implement and use a theme color instead of fixed colors like RoomColors.primary and RoomColors.primaryDim - containerColor = Color(0xFF132417), - scrolledContainerColor = Color(0xFF132417), + containerColor = LocalRoomTheme.current.dimColor, + scrolledContainerColor = LocalRoomTheme.current.dimColor, ), title = {}, navigationIcon = { @@ -101,13 +100,15 @@ fun TimetableItemDetailTopAppBar( @Preview fun TimetableItemDetailTopAppBarPreview() { KaigiTheme { - Surface { - TimetableItemDetailTopAppBar( - isLangSelectable = true, - onNavigationIconClick = {}, - onSelectedLanguage = {}, - scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(), - ) + ProvideFakeRoomTheme { + Surface { + TimetableItemDetailTopAppBar( + isLangSelectable = true, + onNavigationIconClick = {}, + onSelectedLanguage = {}, + scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(), + ) + } } } } @@ -117,13 +118,15 @@ fun TimetableItemDetailTopAppBarPreview() { @Preview fun TimetableItemDetailTopAppBarUnSelectablePreview() { KaigiTheme { - Surface { - TimetableItemDetailTopAppBar( - isLangSelectable = false, - onNavigationIconClick = {}, - onSelectedLanguage = {}, - scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(), - ) + ProvideFakeRoomTheme { + Surface { + TimetableItemDetailTopAppBar( + isLangSelectable = false, + onNavigationIconClick = {}, + onSelectedLanguage = {}, + scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(), + ) + } } } } diff --git a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/section/TimetableList.kt b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/section/TimetableList.kt index 2e979091d..f25f93c52 100644 --- a/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/section/TimetableList.kt +++ b/feature/sessions/src/commonMain/kotlin/io/github/droidkaigi/confsched/sessions/section/TimetableList.kt @@ -37,6 +37,8 @@ import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import io.github.droidkaigi.confsched.designsystem.theme.LocalRoomTheme +import io.github.droidkaigi.confsched.designsystem.theme.ProvideRoomTheme import io.github.droidkaigi.confsched.model.Timetable import io.github.droidkaigi.confsched.model.TimetableItem import io.github.droidkaigi.confsched.model.TimetableRoom.Shapes.CIRCLE @@ -96,74 +98,74 @@ fun TimetableList( Icons.Filled.Star } } - val roomColor = timetableItem.room.getColor() - - Column( - modifier = Modifier - .border( - border = BorderStroke(width = 1.dp, color = Color.White), - shape = RoundedCornerShape(5.dp), - ) - .padding(15.dp), - ) { - Row { - TagView(tagText = roomName, icon = roomIcon, tagColor = roomColor) - Spacer(modifier = Modifier.padding(3.dp)) - timetableItem.language.labels.forEach { label -> - TagView(tagText = label, tagColor = Color.White) + ProvideRoomTheme(timetableItem.room.getThemeKey()) { + Column( + modifier = Modifier + .border( + border = BorderStroke(width = 1.dp, color = Color.White), + shape = RoundedCornerShape(5.dp), + ) + .padding(15.dp), + ) { + Row { + TagView(tagText = roomName, icon = roomIcon, tagColor = LocalRoomTheme.current.primaryColor) Spacer(modifier = Modifier.padding(3.dp)) + timetableItem.language.labels.forEach { label -> + TagView(tagText = label, tagColor = Color.White) + Spacer(modifier = Modifier.padding(3.dp)) + } + Spacer(modifier = Modifier.weight(1f)) + TextButton( + onClick = { onBookmarkClick(timetableItem, true) }, + modifier = Modifier.testTag(TimetableListItemBookmarkIconTestTag), + ) { + if (isBookmarked) { + Icon( + Icons.Filled.Favorite, + contentDescription = "Bookmarked", + tint = Color.Green, + ) + } else { + Icon( + Icons.Outlined.FavoriteBorder, + contentDescription = "Not Bookmarked", + tint = Color.White, + ) + } + } } - Spacer(modifier = Modifier.weight(1f)) - TextButton( - onClick = { onBookmarkClick(timetableItem, true) }, - modifier = Modifier.testTag(TimetableListItemBookmarkIconTestTag), - ) { - if (isBookmarked) { - Icon( - Icons.Filled.Favorite, - contentDescription = "Bookmarked", - tint = Color.Green, + Text( + text = timetableItem.title.currentLangTitle, + fontSize = 24.sp, + modifier = Modifier + .testTag(TimetableListItemTestTag) + .padding(bottom = 5.dp) + .clickable { onTimetableItemClick(timetableItem) }, + ) + timetableItem.speakers.forEach { speaker -> + Row { + // TODO: Fixed image loading again but its still slow. Maybe we need smaller images? + val painter = rememberAsyncImagePainter(speaker.iconUrl) + Image( + painter = painter, + modifier = Modifier + .width(32.dp) + .height(32.dp) + .clip(CircleShape), + contentDescription = "image", ) - } else { - Icon( - Icons.Outlined.FavoriteBorder, - contentDescription = "Not Bookmarked", - tint = Color.White, + Text( + text = speaker.name, + fontSize = 24.sp, + modifier = Modifier + .testTag(TimetableListItemTestTag) + .padding(5.dp) + .align(Alignment.CenterVertically), ) + // TODO: Message goes here (missing from object we can access here?) } } } - Text( - text = timetableItem.title.currentLangTitle, - fontSize = 24.sp, - modifier = Modifier - .testTag(TimetableListItemTestTag) - .padding(bottom = 5.dp) - .clickable { onTimetableItemClick(timetableItem) }, - ) - timetableItem.speakers.forEach { speaker -> - Row { - // TODO: Fixed image loading again but its still slow. Maybe we need smaller images? - val painter = rememberAsyncImagePainter(speaker.iconUrl) - Image( - painter = painter, - modifier = Modifier - .width(32.dp) - .height(32.dp) - .clip(CircleShape), - contentDescription = "image", - ) - Text( - text = speaker.name, - fontSize = 24.sp, - modifier = Modifier - .testTag(TimetableListItemTestTag) - .padding(5.dp) - .align(Alignment.CenterVertically), - ) - // TODO: Message goes here (missing from object we can access here?) - } - } // TODO: There is no data for the warning string right now. (Should go here) } }