diff --git a/shared/src/commonMain/composeResources/values-de/strings.xml b/shared/src/commonMain/composeResources/values-de/strings.xml
index 1d5fd5fe8..c9fc4079d 100644
--- a/shared/src/commonMain/composeResources/values-de/strings.xml
+++ b/shared/src/commonMain/composeResources/values-de/strings.xml
@@ -26,7 +26,6 @@ Für den Quellcode und weitere technische Details besuchen Sie das GitHub-Reposi
Über die App
Unsere Partner
Verhaltenskodex
- Newsfeed
Einstellungen
Twitter
Slack
@@ -79,10 +78,6 @@ Wir verarbeiten Ihre Daten gemäß dem Datenschutzhinweis der App. Sie können I
Erhalten Sie Erinnerungen für Sitzungen, die Sie im Zeitplan mit einem Lesezeichen versehen haben, damit Sie sie nicht verpassen.
Zeitplan-Updates
Bleiben Sie über wichtige Änderungen informiert! Erhalten Sie eine Benachrichtigung, wenn es eine bedeutende Aktualisierung des Veranstaltungszeitplans gibt.
- KotlinConf-Neuigkeiten
- Erhalten Sie Neuigkeiten zwischen KotlinConf-Veranstaltungen, einschließlich Benachrichtigungen über Ticketverkäufe, neue Videos, Wettbewerbe, Zeitplanankündigungen und mehr.
- JetBrains-Neuigkeiten
- Erhalten Sie Neuigkeiten über JetBrains-Produkte.
Lass uns anfangen!
Verhaltenskodex
@@ -135,9 +130,6 @@ Wir verarbeiten Ihre Daten gemäß dem Datenschutzhinweis der App. Sie können I
Erdgeschoss
Erster Stock
- Neuigkeiten
- Keine Neuigkeiten... ist das eine gute Nachricht?
-
Sprecher
Keine Ergebnisse
Unsere Sprecher spielen Verstecken. Geben Sie uns einen Moment, um sie zu finden!
diff --git a/shared/src/commonMain/composeResources/values/strings.xml b/shared/src/commonMain/composeResources/values/strings.xml
index a14b9893f..6ebf7958b 100644
--- a/shared/src/commonMain/composeResources/values/strings.xml
+++ b/shared/src/commonMain/composeResources/values/strings.xml
@@ -26,7 +26,6 @@ For the source code and more technical details, visit the GitHub repository.About the app
Our partners
Code of conduct
- News feed
Settings
Twitter
Slack
@@ -79,10 +78,6 @@ We will process your data in accordance with the App Privacy Notice. You can adj
Get reminders for sessions that you’ve bookmarked in the schedule to make sure you don’t miss them.
Schedule updates
Stay informed about crucial changes! Get an alert if there's a significant update to the event schedule.
- KotlinConf News
- Receive news in between KotlinConf events, including notifications about ticket sales, new videos, contests, schedule announcements, and more.
- JetBrains News
- Get news about JetBrains products.
Let’s get started!
Code of Conduct
@@ -135,9 +130,6 @@ We will process your data in accordance with the App Privacy Notice. You can adj
Ground floor
First floor
- News
- No news… is good news?
-
Speakers
No results
Our speakers are playing hide and seek. Give us a moment to find them!
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/APIClient.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/APIClient.kt
index a8e23889e..a5e6f7b9c 100644
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/APIClient.kt
+++ b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/APIClient.kt
@@ -142,9 +142,6 @@ class APIClient(
}
}
- suspend fun getNews(): List? {
- return safeApiCall { client.get("news").body().items }
- }
/**
* Runs the [call], returning its result or `null` if exceptions occurred.
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/AppInit.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/AppInit.kt
index 12ec898b6..8a8ed62af 100644
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/AppInit.kt
+++ b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/AppInit.kt
@@ -7,12 +7,9 @@ import com.mmk.kmpnotifier.notification.configuration.NotificationPlatformConfig
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
-import org.jetbrains.kotlinconf.navigation.navigateToNews
import org.jetbrains.kotlinconf.navigation.navigateToSession
import org.jetbrains.kotlinconf.screens.AboutConferenceViewModel
import org.jetbrains.kotlinconf.screens.LicensesViewModel
-import org.jetbrains.kotlinconf.screens.NewsDetailViewModel
-import org.jetbrains.kotlinconf.screens.NewsListViewModel
import org.jetbrains.kotlinconf.screens.PrivacyNoticeViewModel
import org.jetbrains.kotlinconf.screens.ScheduleViewModel
import org.jetbrains.kotlinconf.screens.SessionViewModel
@@ -81,8 +78,6 @@ private fun initKoin(
val viewModelModule = module {
viewModelOf(::AboutConferenceViewModel)
viewModelOf(::LicensesViewModel)
- viewModelOf(::NewsDetailViewModel)
- viewModelOf(::NewsListViewModel)
viewModelOf(::PrivacyNoticeViewModel)
viewModelOf(::ScheduleViewModel)
viewModelOf(::SessionViewModel)
@@ -104,31 +99,24 @@ private fun initNotifier(
) {
NotifierManager.initialize(configuration)
NotifierManager.addListener(object : Listener {
- var taggedLogger: TaggedLogger? = logger.tagged("KMPNotifier")
+ var taggedLogger: TaggedLogger = logger.tagged("KMPNotifier")
override fun onNotificationClicked(data: PayloadData) {
super.onNotificationClicked(data)
- taggedLogger?.log { "Notification clicked with $data" }
-
- val newsId = data[PushNotificationConstants.KEY_NEWS_ID] as? String
- if (newsId != null) {
- taggedLogger?.log { "Navigating to news: $newsId" }
- navigateToNews(newsId)
- return
- }
+ taggedLogger.log { "Notification clicked with $data" }
val sessionId = data[PushNotificationConstants.KEY_SESSION_ID] as? String
if (sessionId != null) {
- taggedLogger?.log { "Navigating to session: $sessionId" }
+ taggedLogger.log { "Navigating to session: $sessionId" }
navigateToSession(SessionId(sessionId))
return
}
- taggedLogger?.log { "No data to navigate with, ignoring notification" }
+ taggedLogger.log { "No data to navigate with, ignoring notification" }
}
override fun onNewToken(token: String) {
- taggedLogger?.log { "New token received: $token" }
+ taggedLogger.log { "New token received: $token" }
}
})
}
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/ConferenceService.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/ConferenceService.kt
index 8823a368f..4e325dbed 100644
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/ConferenceService.kt
+++ b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/ConferenceService.kt
@@ -54,9 +54,6 @@ class ConferenceService(
// Download fresh conference data
loadConferenceData()
- // Load fresh news items
- loadNews()
-
// Wait for user ID to be loaded
userIdLoaded.await()
@@ -76,8 +73,6 @@ class ConferenceService(
val notifier = NotifierManager.getPushNotifier()
listOf(
settings.scheduleUpdates to PushNotificationConstants.TOPIC_SCHEDULE_UPDATES,
- settings.kotlinConfNews to PushNotificationConstants.TOPIC_KOTLINCONF_NEWS,
- settings.jetBrainsNews to PushNotificationConstants.TOPIC_JETBRAINS_NEWS,
).forEach { (enabled, topic) ->
if (enabled) notifier.subscribeToTopic(topic)
else notifier.unSubscribeFromTopic(topic)
@@ -86,12 +81,6 @@ class ConferenceService(
}
}
- val news: Flow> = storage.getNews()
- .map { newsItems ->
- val now = timeProvider.now()
- newsItems.map { newsItem -> mapNewsItemToDisplayItem(newsItem, now) }
- }
- .flowOn(Dispatchers.Default)
val agenda: StateFlow> =
combine(
@@ -235,8 +224,6 @@ class ConferenceService(
it ?: NotificationSettings(
sessionReminders = true,
scheduleUpdates = true,
- kotlinConfNews = true,
- jetBrainsNews = true,
)
}
@@ -284,10 +271,6 @@ class ConferenceService(
fun sessionsForSpeakerFlow(id: SpeakerId): Flow> =
sessionCards.map { sessions -> sessions.filter { id in it.speakerIds } }
- fun newsById(newsId: String): Flow =
- news.map { allNews ->
- allNews.find { it.id == newsId }
- }
fun setFavorite(sessionId: SessionId, favorite: Boolean) {
scope.launch {
@@ -362,32 +345,6 @@ class ConferenceService(
localNotificationService.cancel(LocalNotificationId(Type.SessionEnd, sessionId.id))
}
- private fun mapNewsItemToDisplayItem(
- item: NewsItem,
- now: LocalDateTime,
- ): NewsDisplayItem {
- return NewsDisplayItem(
- id = item.id,
- photoUrl = item.photoUrl,
- date = item.publicationDate.toNewsDisplayTime(now),
- title = item.title,
- content = item.content,
- )
- }
-
- private fun LocalDateTime.toNewsDisplayTime(now: LocalDateTime): String {
- val isToday = year == now.year && dayOfYear == now.dayOfYear
- return when {
- isToday -> DateTimeFormatting.time(this)
- year == now.year -> DateTimeFormatting.date(this)
- else -> DateTimeFormatting.dateWithYear(this)
- }
- }
-
- suspend fun loadNews() {
- val news = client.getNews() ?: return
- storage.setNews(news)
- }
private suspend fun syncVotes() {
if (!checkUserId()) return
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/Model.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/Model.kt
index 4f7976d8c..f79dd7c2d 100644
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/Model.kt
+++ b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/Model.kt
@@ -119,8 +119,6 @@ data class NewsListResponse(
data class NotificationSettings(
val sessionReminders: Boolean,
val scheduleUpdates: Boolean,
- val kotlinConfNews: Boolean,
- val jetBrainsNews: Boolean,
) {
- fun hasAnyEnabled() = sessionReminders || scheduleUpdates || kotlinConfNews || jetBrainsNews
+ fun hasAnyEnabled() = sessionReminders || scheduleUpdates
}
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/PushNotificationConstants.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/PushNotificationConstants.kt
index 065b6862d..b1171d401 100644
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/PushNotificationConstants.kt
+++ b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/PushNotificationConstants.kt
@@ -1,10 +1,7 @@
package org.jetbrains.kotlinconf
object PushNotificationConstants {
- const val TOPIC_JETBRAINS_NEWS = "JetBrainsNews"
- const val TOPIC_KOTLINCONF_NEWS = "KotlinConfNews"
const val TOPIC_SCHEDULE_UPDATES = "ScheduleUpdates"
- const val KEY_NEWS_ID = "newsId"
const val KEY_SESSION_ID = "sessionId"
}
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/navigation/KotlinConfNavHost.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/navigation/KotlinConfNavHost.kt
index c43df2c31..b04093095 100644
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/navigation/KotlinConfNavHost.kt
+++ b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/navigation/KotlinConfNavHost.kt
@@ -32,8 +32,6 @@ import org.jetbrains.kotlinconf.screens.DeveloperMenuScreen
import org.jetbrains.kotlinconf.screens.LicensesScreen
import org.jetbrains.kotlinconf.screens.MainScreen
import org.jetbrains.kotlinconf.screens.NestedMapScreen
-import org.jetbrains.kotlinconf.screens.NewsDetailScreen
-import org.jetbrains.kotlinconf.screens.NewsListScreen
import org.jetbrains.kotlinconf.screens.PartnerDetailScreen
import org.jetbrains.kotlinconf.screens.PartnersScreen
import org.jetbrains.kotlinconf.screens.VisitorPrivacyNotice
@@ -61,10 +59,6 @@ fun navigateToSession(sessionId: SessionId) {
notificationNavRequests.trySend(SessionScreen(sessionId))
}
-fun navigateToNews(newsId: String) {
- notificationNavRequests.trySend(NewsDetailScreen(newsId = newsId))
-}
-
private val notificationNavRequests = Channel(capacity = 1)
@Composable
@@ -236,17 +230,6 @@ fun NavGraphBuilder.screens(navController: NavHostController) {
)
}
- composable {
- NewsListScreen(
- onNewsClick = { newsId -> navController.navigate(NewsDetailScreen(newsId)) },
- onBack = navController::navigateUp,
- )
- }
- composable {
- val newsId = it.toRoute().newsId
- NewsDetailScreen(newsId, onBack = navController::navigateUp)
- }
-
composable {
DeveloperMenuScreen(onBack = navController::navigateUp)
}
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/navigation/Routes.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/navigation/Routes.kt
index 8658ab672..8ff003811 100644
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/navigation/Routes.kt
+++ b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/navigation/Routes.kt
@@ -108,13 +108,6 @@ data object MapScreen
@SerialName("MapDetail")
data class NestedMapScreen(val roomName: String)
-@Serializable
-@SerialName("News")
-data object NewsListScreen
-
-@Serializable
-@SerialName("News")
-data class NewsDetailScreen(val newsId: String)
@Serializable
@SerialName("DeveloperMenu")
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/InfoScreen.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/InfoScreen.kt
index e333a6110..c42733118 100644
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/InfoScreen.kt
+++ b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/InfoScreen.kt
@@ -28,7 +28,6 @@ import kotlinconfapp.shared.generated.resources.info_link_code_of_conduct
import kotlinconfapp.shared.generated.resources.info_link_description_bluesky
import kotlinconfapp.shared.generated.resources.info_link_description_slack
import kotlinconfapp.shared.generated.resources.info_link_description_twitter
-import kotlinconfapp.shared.generated.resources.info_link_news_feed
import kotlinconfapp.shared.generated.resources.info_link_partners
import kotlinconfapp.shared.generated.resources.info_title
import kotlinconfapp.shared.generated.resources.kotlinconf_by_jetbrains
@@ -47,7 +46,6 @@ import org.jetbrains.kotlinconf.ui.theme.KotlinConfTheme
fun InfoScreen(
onAboutConf: () -> Unit,
onAboutApp: () -> Unit,
- onNewsFeed: () -> Unit,
onOurPartners: () -> Unit,
onCodeOfConduct: () -> Unit,
onTwitter: () -> Unit,
@@ -77,7 +75,6 @@ fun InfoScreen(
PageMenuItem(stringResource(Res.string.info_link_about_conf), onClick = onAboutConf)
PageMenuItem(stringResource(Res.string.info_link_about_app), onClick = onAboutApp)
PageMenuItem(stringResource(Res.string.info_link_settings), onClick = onSettings)
- PageMenuItem(stringResource(Res.string.info_link_news_feed), onClick = onNewsFeed)
PageMenuItem(stringResource(Res.string.info_link_partners), onClick = onOurPartners)
PageMenuItem(stringResource(Res.string.info_link_code_of_conduct), onClick = onCodeOfConduct)
Row(
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/MainScreen.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/MainScreen.kt
index 845d5d865..1d2a9e96d 100644
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/MainScreen.kt
+++ b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/MainScreen.kt
@@ -53,7 +53,6 @@ import org.jetbrains.kotlinconf.navigation.AboutConferenceScreen
import org.jetbrains.kotlinconf.navigation.CodeOfConductScreen
import org.jetbrains.kotlinconf.navigation.InfoScreen
import org.jetbrains.kotlinconf.navigation.MapScreen
-import org.jetbrains.kotlinconf.navigation.NewsListScreen
import org.jetbrains.kotlinconf.navigation.PartnersScreen
import org.jetbrains.kotlinconf.navigation.AppPrivacyNoticePrompt
import org.jetbrains.kotlinconf.navigation.ScheduleScreen
@@ -100,7 +99,6 @@ fun MainScreen(
InfoScreen(
onAboutConf = { rootNavController.navigate(AboutConferenceScreen) },
onAboutApp = { rootNavController.navigate(AboutAppScreen) },
- onNewsFeed = { rootNavController.navigate(NewsListScreen) },
onOurPartners = { rootNavController.navigate(PartnersScreen) },
onCodeOfConduct = { rootNavController.navigate(CodeOfConductScreen) },
onTwitter = { uriHandler.openUri(URLs.TWITTER) },
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NewsDetailScreen.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NewsDetailScreen.kt
deleted file mode 100644
index 9a675dfac..000000000
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NewsDetailScreen.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-package org.jetbrains.kotlinconf.screens
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import kotlinconfapp.shared.generated.resources.Res
-import kotlinconfapp.shared.generated.resources.arrow_left_24
-import kotlinconfapp.shared.generated.resources.navigate_back
-import kotlinconfapp.shared.generated.resources.news_feed_title
-import org.jetbrains.compose.resources.stringResource
-import org.jetbrains.kotlinconf.ui.components.Divider
-import org.jetbrains.kotlinconf.ui.components.MainHeaderTitleBar
-import org.jetbrains.kotlinconf.ui.components.MarkdownView
-import org.jetbrains.kotlinconf.ui.components.NetworkImage
-import org.jetbrains.kotlinconf.ui.components.Text
-import org.jetbrains.kotlinconf.ui.components.TopMenuButton
-import org.jetbrains.kotlinconf.ui.theme.KotlinConfTheme
-import org.jetbrains.kotlinconf.utils.topInsetPadding
-import org.koin.compose.viewmodel.koinViewModel
-import org.koin.core.parameter.parametersOf
-
-@Composable
-fun NewsDetailScreen(
- newsId: String,
- onBack: () -> Unit,
- viewModel: NewsDetailViewModel = koinViewModel { parametersOf(newsId) }
-) {
- val state = viewModel.newsItem.collectAsStateWithLifecycle().value
-
- Column(
- Modifier
- .fillMaxSize()
- .background(color = KotlinConfTheme.colors.mainBackground)
- .padding(topInsetPadding())
- ) {
- MainHeaderTitleBar(
- title = state?.date ?: stringResource(Res.string.news_feed_title),
- startContent = {
- TopMenuButton(
- icon = Res.drawable.arrow_left_24,
- contentDescription = stringResource(Res.string.navigate_back),
- onClick = onBack,
- )
- }
- )
-
- Divider(thickness = 1.dp, color = KotlinConfTheme.colors.strokePale)
-
- Column(
- Modifier
- .fillMaxSize()
- .background(color = KotlinConfTheme.colors.mainBackground)
- .verticalScroll(rememberScrollState())
- ) {
- if (state != null) {
- state.photoUrl?.let { url ->
- NetworkImage(
- photoUrl = url,
- contentDescription = null,
- modifier = Modifier.fillMaxWidth(),
- )
- }
-
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .padding(vertical = 16.dp, horizontal = 12.dp)
- ) {
- Text(text = state.title, style = KotlinConfTheme.typography.h2)
- Spacer(Modifier.height(12.dp))
- MarkdownView(text = state.content)
- }
- }
- }
- }
-}
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NewsDetailViewModel.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NewsDetailViewModel.kt
deleted file mode 100644
index 6d4f39cd0..000000000
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NewsDetailViewModel.kt
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.jetbrains.kotlinconf.screens
-
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.stateIn
-import org.jetbrains.kotlinconf.ConferenceService
-import org.jetbrains.kotlinconf.NewsDisplayItem
-
-class NewsDetailViewModel(
- service: ConferenceService,
- newsId: String,
-) : ViewModel() {
- val newsItem: StateFlow = service.newsById(newsId)
- .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)
-}
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NewsListScreen.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NewsListScreen.kt
deleted file mode 100644
index 8c06f7442..000000000
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NewsListScreen.kt
+++ /dev/null
@@ -1,64 +0,0 @@
-package org.jetbrains.kotlinconf.screens
-
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.PaddingValues
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.items
-import androidx.compose.foundation.lazy.rememberLazyListState
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
-import kotlinconfapp.shared.generated.resources.Res
-import kotlinconfapp.shared.generated.resources.news_feed_empty
-import kotlinconfapp.shared.generated.resources.news_feed_title
-import org.jetbrains.compose.resources.stringResource
-import org.jetbrains.kotlinconf.ScreenWithTitle
-import org.jetbrains.kotlinconf.ScrollToTopHandler
-import org.jetbrains.kotlinconf.ui.components.MajorError
-import org.jetbrains.kotlinconf.ui.components.NewsCard
-import org.jetbrains.kotlinconf.utils.bottomInsetPadding
-import org.jetbrains.kotlinconf.utils.plus
-import org.koin.compose.viewmodel.koinViewModel
-
-@Composable
-fun NewsListScreen(
- onNewsClick: (String) -> Unit,
- onBack: () -> Unit,
- viewModel: NewsListViewModel = koinViewModel(),
-) {
- ScreenWithTitle(
- title = stringResource(Res.string.news_feed_title),
- onBack = onBack,
- ) {
- val news by viewModel.news.collectAsStateWithLifecycle()
-
- if (news.isNotEmpty()) {
- val listState = rememberLazyListState()
- ScrollToTopHandler(listState)
- LazyColumn(
- modifier = Modifier.weight(1f),
- contentPadding = PaddingValues(vertical = 16.dp) + bottomInsetPadding(),
- verticalArrangement = Arrangement.spacedBy(16.dp),
- state = listState,
- ) {
- items(news) { newsItem ->
- NewsCard(
- title = newsItem.title,
- date = newsItem.date,
- photoUrl = newsItem.photoUrl,
- onClick = { onNewsClick(newsItem.id) }
- )
- }
- }
- } else {
- Box(Modifier.fillMaxSize().weight(1f), contentAlignment = Alignment.Center) {
- MajorError(message = stringResource(Res.string.news_feed_empty))
- }
- }
- }
-}
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NewsListViewModel.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NewsListViewModel.kt
deleted file mode 100644
index a5872fd0e..000000000
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NewsListViewModel.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package org.jetbrains.kotlinconf.screens
-
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.viewModelScope
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.stateIn
-import org.jetbrains.kotlinconf.ConferenceService
-import org.jetbrains.kotlinconf.NewsDisplayItem
-
-class NewsListViewModel(
- service: ConferenceService
-) : ViewModel() {
- val news: StateFlow> = service.news
- .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
-}
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NotificationSettings.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NotificationSettings.kt
index 1b4d08b9a..44464d033 100644
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NotificationSettings.kt
+++ b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/screens/NotificationSettings.kt
@@ -5,10 +5,6 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import kotlinconfapp.shared.generated.resources.Res
-import kotlinconfapp.shared.generated.resources.notifications_jetbrains_news_description
-import kotlinconfapp.shared.generated.resources.notifications_jetbrains_news_title
-import kotlinconfapp.shared.generated.resources.notifications_kotlinconf_news_description
-import kotlinconfapp.shared.generated.resources.notifications_kotlinconf_news_title
import kotlinconfapp.shared.generated.resources.notifications_schedule_update_description
import kotlinconfapp.shared.generated.resources.notifications_schedule_update_title
import kotlinconfapp.shared.generated.resources.notifications_session_reminders_description
@@ -38,17 +34,5 @@ fun NotificationSettings(
onToggle = { enabled -> onChangeSettings(notificationSettings.copy(scheduleUpdates = enabled)) },
note = stringResource(Res.string.notifications_schedule_update_description),
)
- SettingsItem(
- title = stringResource(Res.string.notifications_kotlinconf_news_title),
- enabled = notificationSettings.kotlinConfNews,
- onToggle = { enabled -> onChangeSettings(notificationSettings.copy(kotlinConfNews = enabled)) },
- note = stringResource(Res.string.notifications_kotlinconf_news_description),
- )
- SettingsItem(
- title = stringResource(Res.string.notifications_jetbrains_news_title),
- enabled = notificationSettings.jetBrainsNews,
- onToggle = { enabled -> onChangeSettings(notificationSettings.copy(jetBrainsNews = enabled)) },
- note = stringResource(Res.string.notifications_jetbrains_news_description),
- )
}
}
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/storage/ApplicationStorage.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/storage/ApplicationStorage.kt
index 576e676b0..fab9dd778 100644
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/storage/ApplicationStorage.kt
+++ b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/storage/ApplicationStorage.kt
@@ -3,7 +3,6 @@ package org.jetbrains.kotlinconf.storage
import kotlinx.coroutines.flow.Flow
import org.jetbrains.kotlinconf.Conference
import org.jetbrains.kotlinconf.Flags
-import org.jetbrains.kotlinconf.NewsItem
import org.jetbrains.kotlinconf.NotificationSettings
import org.jetbrains.kotlinconf.SessionId
import org.jetbrains.kotlinconf.Theme
@@ -28,9 +27,6 @@ interface ApplicationStorage {
fun getFavorites(): Flow>
suspend fun setFavorites(value: Set)
- fun getNews(): Flow>
- suspend fun setNews(value: List)
-
fun getNotificationSettings(): Flow
suspend fun setNotificationSettings(value: NotificationSettings)
diff --git a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/storage/MultiplatformSettingsStorage.kt b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/storage/MultiplatformSettingsStorage.kt
index fbffdd009..567352dad 100644
--- a/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/storage/MultiplatformSettingsStorage.kt
+++ b/shared/src/commonMain/kotlin/org/jetbrains/kotlinconf/storage/MultiplatformSettingsStorage.kt
@@ -11,20 +11,25 @@ import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import org.jetbrains.kotlinconf.Conference
import org.jetbrains.kotlinconf.Flags
-import org.jetbrains.kotlinconf.NewsItem
import org.jetbrains.kotlinconf.NotificationSettings
import org.jetbrains.kotlinconf.SessionId
import org.jetbrains.kotlinconf.Theme
import org.jetbrains.kotlinconf.VoteInfo
+import org.jetbrains.kotlinconf.utils.Logger
+import org.jetbrains.kotlinconf.utils.TaggedLogger
+import org.jetbrains.kotlinconf.utils.tagged
@OptIn(ExperimentalSettingsApi::class)
class MultiplatformSettingsStorage(
private val settings: ObservableSettings,
+ logger: Logger? = null, // TODO inject a logger here https://github.com/JetBrains/kotlinconf-app/issues/544
) : ApplicationStorage {
private val json = Json {
ignoreUnknownKeys = true
}
+ private var taggedLogger: TaggedLogger? = logger?.tagged("MultiplatformSettingsStorage")
+
private inline fun String?.decodeOrNull(): T? {
if (this == null) return null
@@ -62,12 +67,6 @@ class MultiplatformSettingsStorage(
override suspend fun setFavorites(value: Set) = settings
.set(Keys.FAVORITES, json.encodeToString(value))
- override fun getNews(): Flow> = settings.getStringOrNullFlow(Keys.NEWS_CACHE)
- .map { it.decodeOrNull>() ?: emptyList() }
-
- override suspend fun setNews(value: List) = settings
- .set(Keys.NEWS_CACHE, json.encodeToString(value))
-
override fun getNotificationSettings(): Flow =
settings.getStringOrNullFlow(Keys.NOTIFICATION_SETTINGS)
.map { it.decodeOrNull() }
@@ -91,16 +90,62 @@ class MultiplatformSettingsStorage(
.set(Keys.FLAGS, json.encodeToString(value))
override fun ensureCurrentVersion() {
- val version = settings.getInt(Keys.STORAGE_VERSION, 0)
- if (version < CURRENT_STORAGE_VERSION) {
- // Fully destructive migration on version mismatch
- settings.clear()
- settings.set(Keys.STORAGE_VERSION, CURRENT_STORAGE_VERSION)
+ var version = settings.getInt(Keys.STORAGE_VERSION, 0)
+
+ taggedLogger?.log { "Storage version is $version" }
+
+ if (version == 0) {
+ // Fully destructive migration on unknown previous version
+ destructiveUpgrade()
+ return
+ }
+
+ while (version < CURRENT_STORAGE_VERSION) {
+ taggedLogger?.log { "Finding migrations from $version to $CURRENT_STORAGE_VERSION..." }
+
+ // Find a migration from the current version that takes us as far forward as possible
+ val nextMigration = migrations.filter { it.from == version }.maxByOrNull { it.to }
+ if (nextMigration == null) {
+ taggedLogger?.log { "No matching migrations found" }
+
+ // Failed to find a migration path to latest, fall back to destructive
+ destructiveUpgrade()
+ return
+ }
+
+ taggedLogger?.log { "Running migration from ${nextMigration.from} to ${nextMigration.to}" }
+
+ nextMigration.migrate()
+ version = nextMigration.to
+ settings.set(Keys.STORAGE_VERSION, version)
+
+ taggedLogger?.log { "Successfully migrated to $version" }
}
}
+ private fun destructiveUpgrade() {
+ taggedLogger?.log { "Performing destructive upgrade to $CURRENT_STORAGE_VERSION" }
+ settings.clear()
+ settings.set(Keys.STORAGE_VERSION, CURRENT_STORAGE_VERSION)
+ }
+
+ private data class Migration(val from: Int, val to: Int, val migrate: () -> Unit)
+
+ private val migrations = listOf(
+ Migration(V2025, V2026) {
+ // News were removed
+ settings.remove(Keys.NEWS_CACHE)
+ // We removed news fields from NotificationSettings, read/write it once to update
+ settings.getStringOrNull(Keys.NOTIFICATION_SETTINGS)
+ ?.decodeOrNull()
+ ?.let { settings.set(Keys.NOTIFICATION_SETTINGS, json.encodeToString(it)) }
+ },
+ )
+
private companion object {
- const val CURRENT_STORAGE_VERSION: Int = 2025_000
+ const val V2025 = 2025_000
+ const val V2026 = 2026_000
+ const val CURRENT_STORAGE_VERSION: Int = V2026
}
private object Keys {
diff --git a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/NewsCard.kt b/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/NewsCard.kt
deleted file mode 100644
index 6156dd837..000000000
--- a/ui-components/src/commonMain/kotlin/org/jetbrains/kotlinconf/ui/components/NewsCard.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-package org.jetbrains.kotlinconf.ui.components
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.heightIn
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
-import androidx.compose.ui.layout.ContentScale
-import androidx.compose.ui.unit.dp
-import coil3.compose.AsyncImage
-import org.jetbrains.kotlinconf.ui.theme.KotlinConfTheme
-
-@Composable
-fun NewsCard(
- title: String,
- date: String,
- photoUrl: String?,
- onClick: () -> Unit,
- modifier: Modifier = Modifier
-) {
- Column(
- modifier = modifier
- .fillMaxWidth()
- .clip(RoundedCornerShape(8.dp))
- .background(color = KotlinConfTheme.colors.tileBackground)
- .clickable(onClick = onClick)
- ) {
- if (photoUrl != null) {
- AsyncImage(
- model = photoUrl,
- contentDescription = null,
- contentScale = ContentScale.FillWidth,
- modifier = Modifier
- .fillMaxWidth()
- .heightIn(max = 140.dp)
- .background(KotlinConfTheme.colors.purpleText),
- )
- }
- Column(Modifier.fillMaxWidth().padding(16.dp)) {
- Text(
- text = date,
- style = KotlinConfTheme.typography.text2,
- color = KotlinConfTheme.colors.secondaryText,
- )
- Spacer(modifier = Modifier.size(4.dp))
- Text(
- text = title,
- style = KotlinConfTheme.typography.h3,
- color = KotlinConfTheme.colors.primaryText,
- )
- }
- }
-}