Skip to content

Commit 9d9a72b

Browse files
committed
add mixed timeline when new account added
1 parent ac26478 commit 9d9a72b

File tree

6 files changed

+143
-27
lines changed

6 files changed

+143
-27
lines changed

app/src/main/java/dev/dimension/flare/ui/screen/home/HomeTimelineScreen.kt

Lines changed: 79 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ import androidx.compose.ui.unit.dp
5151
import compose.icons.FontAwesomeIcons
5252
import compose.icons.fontawesomeicons.Solid
5353
import compose.icons.fontawesomeicons.solid.AnglesUp
54-
import compose.icons.fontawesomeicons.solid.Sliders
54+
import compose.icons.fontawesomeicons.solid.Plus
5555
import dev.chrisbanes.haze.hazeSource
5656
import dev.chrisbanes.haze.rememberHazeState
5757
import dev.dimension.flare.R
@@ -75,6 +75,7 @@ import dev.dimension.flare.ui.component.platform.isBigScreen
7575
import dev.dimension.flare.ui.component.status.AdaptiveCard
7676
import dev.dimension.flare.ui.component.status.LazyStatusVerticalStaggeredGrid
7777
import dev.dimension.flare.ui.component.status.status
78+
import dev.dimension.flare.ui.model.UiRssSource
7879
import dev.dimension.flare.ui.model.UiTimeline
7980
import dev.dimension.flare.ui.model.collectAsUiState
8081
import dev.dimension.flare.ui.model.map
@@ -84,6 +85,7 @@ import dev.dimension.flare.ui.presenter.home.NotificationBadgePresenter
8485
import dev.dimension.flare.ui.presenter.home.UserPresenter
8586
import dev.dimension.flare.ui.presenter.home.UserState
8687
import dev.dimension.flare.ui.presenter.invoke
88+
import dev.dimension.flare.ui.presenter.settings.AccountEventPresenter
8789
import dev.dimension.flare.ui.screen.settings.TabIcon
8890
import dev.dimension.flare.ui.screen.settings.TabTitle
8991
import dev.dimension.flare.ui.theme.screenHorizontalPadding
@@ -138,20 +140,29 @@ internal fun HomeTimelineScreen(
138140
title = {
139141
state.pagerState.onSuccess { pagerState ->
140142
state.tabState.onSuccess { tabs ->
141-
if (tabs.size > 1) {
143+
if (tabs.any()) {
142144
SecondaryScrollableTabRow(
143145
containerColor = Color.Transparent,
144146
modifier =
145147
Modifier
146148
.fillMaxWidth(),
147-
selectedTabIndex = minOf(pagerState.currentPage, tabs.lastIndex),
149+
selectedTabIndex =
150+
minOf(
151+
pagerState.currentPage,
152+
tabs.lastIndex,
153+
),
148154
edgePadding = 0.dp,
149155
divider = {},
150156
indicator = {
151157
TabRowIndicator(
152-
selectedIndex = minOf(pagerState.currentPage, tabs.lastIndex),
158+
selectedIndex =
159+
minOf(
160+
pagerState.currentPage,
161+
tabs.lastIndex,
162+
),
153163
)
154164
},
165+
minTabWidth = 48.dp,
155166
) {
156167
state.tabState.onSuccess { tabs ->
157168
tabs.forEachIndexed { index, tab ->
@@ -186,9 +197,17 @@ internal fun HomeTimelineScreen(
186197
)
187198
}
188199
}
200+
IconButton(
201+
onClick = {
202+
toTabSettings.invoke()
203+
},
204+
) {
205+
FAIcon(
206+
imageVector = FontAwesomeIcons.Solid.Plus,
207+
contentDescription = null,
208+
)
209+
}
189210
}
190-
} else {
191-
TabTitle(title = tabs[0].timelineTabItem.metaData.title)
192211
}
193212
}
194213
}
@@ -217,14 +236,6 @@ internal fun HomeTimelineScreen(
217236
Text(text = stringResource(id = R.string.login_button))
218237
}
219238
}.onSuccess {
220-
IconButton(
221-
onClick = toTabSettings,
222-
) {
223-
FAIcon(
224-
FontAwesomeIcons.Solid.Sliders,
225-
contentDescription = null,
226-
)
227-
}
228239
}
229240
},
230241
)
@@ -373,6 +384,43 @@ private fun timelinePresenter(
373384
)
374385
}.invoke()
375386

387+
val accountEvent =
388+
remember {
389+
AccountEventPresenter()
390+
}.invoke()
391+
392+
LaunchedEffect(accountEvent.onAdded) {
393+
accountEvent.onAdded.collect { account ->
394+
val tab =
395+
HomeTimelineTabItem(
396+
accountKey = account.accountKey,
397+
icon = UiRssSource.favIconUrl(account.accountKey.host),
398+
title =
399+
account.accountKey.host
400+
.substringBeforeLast('.')
401+
.substringAfter('.'),
402+
)
403+
settingsRepository.updateTabSettings {
404+
copy(
405+
mainTabs =
406+
(mainTabs + tab).distinctBy {
407+
it.key
408+
},
409+
)
410+
}
411+
}
412+
}
413+
414+
LaunchedEffect(accountEvent.onRemoved) {
415+
accountEvent.onRemoved.collect { accountKey ->
416+
settingsRepository.updateTabSettings {
417+
copy(
418+
mainTabs = mainTabs.filterNot { it.account == AccountType.Specific(accountKey) },
419+
)
420+
}
421+
}
422+
}
423+
376424
val tabs by remember {
377425
settingsRepository.tabSettings
378426
.map { settings ->
@@ -381,15 +429,23 @@ private fun timelinePresenter(
381429
HomeTimelineTabItem(AccountType.Guest),
382430
)
383431
} else {
384-
listOfNotNull(
385-
if (settings.enableMixedTimeline && settings.mainTabs.size > 1) {
386-
MixedTimelineTabItem(
387-
subTimelineTabItem = settings.mainTabs,
388-
)
389-
} else {
390-
null
391-
},
392-
) + settings.mainTabs
432+
(
433+
listOfNotNull(
434+
if (settings.enableMixedTimeline && settings.mainTabs.size > 1) {
435+
MixedTimelineTabItem(
436+
subTimelineTabItem = settings.mainTabs,
437+
)
438+
} else {
439+
null
440+
},
441+
) + settings.mainTabs
442+
).ifEmpty {
443+
listOf(
444+
HomeTimelineTabItem(
445+
accountType = AccountType.Active,
446+
),
447+
)
448+
}
393449
}
394450
}.map {
395451
it.toImmutableList()

shared/src/androidJvmMain/kotlin/dev/dimension/flare/data/model/TabSettings.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ import java.io.OutputStream
2727
@Serializable
2828
public data class TabSettings(
2929
val secondaryItems: List<TabItem>? = null,
30-
val enableMixedTimeline: Boolean = false,
31-
val mainTabs: List<TimelineTabItem> = listOf(HomeTimelineTabItem(AccountType.Active)),
30+
val enableMixedTimeline: Boolean = true,
31+
val mainTabs: List<TimelineTabItem> = listOf(),
3232
)
3333

3434
@Serializable
@@ -630,6 +630,16 @@ public data class HomeTimelineTabItem(
630630
icon = IconType.Material(IconType.Material.MaterialIcon.Home),
631631
),
632632
)
633+
634+
public constructor(accountKey: MicroBlogKey, icon: String, title: String) :
635+
this(
636+
account = AccountType.Specific(accountKey),
637+
metaData =
638+
TabMetaData(
639+
title = TitleType.Text(title),
640+
icon = IconType.Url(icon),
641+
),
642+
)
633643
}
634644

635645
@Serializable

shared/src/commonMain/kotlin/dev/dimension/flare/data/repository/AccountRepository.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import kotlinx.collections.immutable.ImmutableList
2626
import kotlinx.collections.immutable.toImmutableList
2727
import kotlinx.coroutines.CoroutineScope
2828
import kotlinx.coroutines.flow.Flow
29+
import kotlinx.coroutines.flow.MutableStateFlow
2930
import kotlinx.coroutines.flow.distinctUntilChangedBy
3031
import kotlinx.coroutines.flow.flowOf
3132
import kotlinx.coroutines.flow.map
@@ -59,6 +60,23 @@ internal class AccountRepository(
5960
}
6061
}
6162

63+
private val _onAdded by lazy {
64+
MutableStateFlow<UiAccount?>(null)
65+
}
66+
val onAdded: Flow<UiAccount> by lazy {
67+
_onAdded
68+
.mapNotNull { it }
69+
.distinctUntilChangedBy { it.accountKey }
70+
}
71+
private val _onRemoved by lazy {
72+
MutableStateFlow<MicroBlogKey?>(null)
73+
}
74+
val onRemoved: Flow<MicroBlogKey> by lazy {
75+
_onRemoved
76+
.mapNotNull { it }
77+
.distinctUntilChangedBy { it }
78+
}
79+
6280
fun addAccount(
6381
account: UiAccount,
6482
credential: UiAccount.Credential,
@@ -71,6 +89,7 @@ internal class AccountRepository(
7189
credential_json = credential.encodeJson(),
7290
),
7391
)
92+
_onAdded.value = account
7493
}
7594

7695
fun setActiveAccount(accountKey: MicroBlogKey) =
@@ -83,6 +102,7 @@ internal class AccountRepository(
83102

84103
fun delete(accountKey: MicroBlogKey) =
85104
coroutineScope.launch {
105+
_onRemoved.value = accountKey
86106
cacheDatabase.pagingTimelineDao().deleteByAccountType(
87107
AccountType.Specific(accountKey),
88108
)

shared/src/commonMain/kotlin/dev/dimension/flare/ui/presenter/home/TimelinePresenter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public abstract class TimelinePresenter :
125125
when (data.timeline.accountType) {
126126
AccountType.Guest -> null
127127
is AccountType.Specific -> {
128-
accounts.first {
128+
accounts.firstOrNull {
129129
it.accountKey == data.timeline.accountType.accountKey
130130
}
131131
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package dev.dimension.flare.ui.presenter.settings
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.compose.runtime.Immutable
5+
import dev.dimension.flare.data.repository.AccountRepository
6+
import dev.dimension.flare.model.MicroBlogKey
7+
import dev.dimension.flare.ui.model.UiAccount
8+
import dev.dimension.flare.ui.presenter.PresenterBase
9+
import kotlinx.coroutines.flow.Flow
10+
import org.koin.core.component.KoinComponent
11+
import org.koin.core.component.inject
12+
13+
public class AccountEventPresenter :
14+
PresenterBase<AccountEventPresenter.State>(),
15+
KoinComponent {
16+
@Immutable
17+
public interface State {
18+
public val onAdded: Flow<UiAccount>
19+
public val onRemoved: Flow<MicroBlogKey>
20+
}
21+
22+
private val accountRepository: AccountRepository by inject()
23+
24+
@Composable
25+
override fun body(): State =
26+
object : State {
27+
override val onAdded = accountRepository.onAdded
28+
override val onRemoved = accountRepository.onRemoved
29+
}
30+
}

shared/ui/component/src/commonMain/kotlin/dev/dimension/flare/ui/component/status/AdaptiveCard.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public fun AdaptiveCard(
2727
horizontal = 2.dp,
2828
vertical = 6.dp,
2929
),
30-
elevated = true,
30+
elevated = false,
3131
containerColor = PlatformTheme.colorScheme.card,
3232
) {
3333
content.invoke()

0 commit comments

Comments
 (0)