Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions shared/src/commonMain/composeResources/values-en/strings.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<resources>
<string name="app_name">Bilby</string>
<string name="action_back">Back</string>
<string name="action_cancel">Cancel</string>
<string name="nav_already_here">Already on this page</string>
Expand Down Expand Up @@ -396,6 +397,7 @@
<string name="settings_theme_system">System</string>
<string name="settings_theme_light">Light</string>
<string name="settings_theme_dark">Dark</string>
<string name="settings_start_tab">Start page</string>
<string name="settings_pure_black">Pure black</string>
<string name="settings_pure_black_subtitle">Black background in dark theme</string>
<string name="settings_theme_palette">Color</string>
Expand Down
3 changes: 3 additions & 0 deletions shared/src/commonMain/composeResources/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
<resources>
<string name="app_name">Bilby</string>

<!-- 导航。 -->
<string name="nav_already_here">已在当前页面</string>

Expand Down Expand Up @@ -479,6 +481,7 @@
<string name="settings_theme_system">跟随系统</string>
<string name="settings_theme_light">浅色</string>
<string name="settings_theme_dark">深色</string>
<string name="settings_start_tab">默认启动页</string>
<string name="settings_pure_black">纯黑背景</string>
<string name="settings_pure_black_subtitle">深色时页面底色为纯黑</string>
<string name="settings_theme_palette">配色</string>
Expand Down
15 changes: 15 additions & 0 deletions shared/src/commonMain/kotlin/dev/bilby/data/SettingsStore.kt
Original file line number Diff line number Diff line change
Expand Up @@ -237,9 +237,15 @@ class SettingsStore(private val store: DataStore<Preferences>) {
?: ThemeMode.System,
pureBlack = p[KEY_PURE_BLACK] ?: false,
palette = p[KEY_THEME_PALETTE] ?: AppearancePrefs.DYNAMIC,
startTab = p[KEY_START_TAB]?.let { name -> StartTab.entries.firstOrNull { it.name == name } }
?: StartTab.Search,
)
}

val startTab: Flow<StartTab?> = store.data.map { p ->
p[KEY_START_TAB]?.let { name -> StartTab.entries.firstOrNull { it.name == name } } ?: StartTab.Search
}

suspend fun saveThemeMode(mode: ThemeMode) {
store.edit { p -> p[KEY_THEME_MODE] = mode.name }
}
Expand All @@ -253,6 +259,10 @@ class SettingsStore(private val store: DataStore<Preferences>) {
store.edit { p -> p[KEY_THEME_PALETTE] = palette }
}

suspend fun saveStartTab(tab: StartTab) {
store.edit { p -> p[KEY_START_TAB] = tab.name }
}

/**
* 同时下几条缓存。为什么默认 1、上限 3,见 [dev.bilby.offline.OfflineDownloader] 的
* "并发度"一节 —— 这里只负责把用户选的那个数存下来。
Expand Down Expand Up @@ -452,6 +462,7 @@ class SettingsStore(private val store: DataStore<Preferences>) {
private val KEY_THEME_MODE = stringPreferencesKey("theme_mode")
private val KEY_PURE_BLACK = booleanPreferencesKey("theme_pure_black")
private val KEY_THEME_PALETTE = stringPreferencesKey("theme_palette")
private val KEY_START_TAB = stringPreferencesKey("start_tab")
private val KEY_SESSDATA = stringPreferencesKey("sessdata")
private val KEY_BILI_JCT = stringPreferencesKey("bili_jct")
private val KEY_DEDE_USER_ID = stringPreferencesKey("dede_user_id")
Expand Down Expand Up @@ -758,6 +769,9 @@ data class PlayerPrefs(
/** 明暗。三档互斥;纯黑是深色之下的另一个开关,不是第四档,见 [AppearancePrefs.pureBlack]。 */
enum class ThemeMode { System, Light, Dark }

/** 启动时默认落到哪一栏:动态、搜索着陆页或我的。 */
enum class StartTab { Feed, Search, Profile }

data class AppearancePrefs(
val mode: ThemeMode = ThemeMode.System,
/**
Expand All @@ -772,6 +786,7 @@ data class AppearancePrefs(
* 指到别的颜色上。
*/
val palette: String = DYNAMIC,
val startTab: StartTab = StartTab.Search,
) {
companion object {
const val DYNAMIC = "dynamic"
Expand Down
52 changes: 46 additions & 6 deletions shared/src/commonMain/kotlin/dev/bilby/ui/BilbyApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ import dev.bilby.stringResource
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import dev.bilby.data.AppearancePrefs
import dev.bilby.data.StartTab
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
Expand Down Expand Up @@ -1115,24 +1116,38 @@ private fun RootTabs(
/** 搜索框里的编号或链接直接打开的那一页,可以是任何一种目的地。 */
onOpen: (NavKey) -> Unit,
) {
var selected by rememberSaveable { mutableStateOf(RootTab.Feed) }
val persistedStartTab by container.settings.startTab.collectAsStateWithLifecycle(initialValue = null)
var selected by rememberSaveable { mutableStateOf(RootTab.Search) }
var appliedStartTab by rememberSaveable { mutableStateOf<StartTab?>(null) }
LaunchedEffect(persistedStartTab) {
val tab = persistedStartTab ?: return@LaunchedEffect
if (appliedStartTab != tab) {
appliedStartTab = tab
selected = when (tab) {
StartTab.Feed -> RootTab.Feed
StartTab.Search -> RootTab.Search
StartTab.Profile -> RootTab.Profile
}
}
}
val windowSize = rememberBilbyWindowSize()

// 重按当前 tab 回到顶部,navigation-bar.md 的明文要求。
// 重按当前 tab 回到这一页的起点,navigation-bar.md 的明文要求。
//
// **每个 tab 一个计数器,不共用一个。** 共用的话,在动态页连点几下再切到个人页,
// 个人页看到的是一个变过的计数器,会跟着滚一次它自己没被点过的。
//
// 搜索页不接:那一页是对话式的,输入在下、一轮轮结果往上滚,顶端是最早的一轮,
// 回到顶不是"重来一次",而是翻到最旧的地方。
// 搜索页的"起点"是着陆页不是顶端:那一页是对话式的,没有"回到顶",搜过之后
// 重按是清掉查询翻回居中的框 —— 与回顶同一件事,都是回到进来的那一屏。
var feedScrollToTop by remember { mutableIntStateOf(0) }
var profileScrollToTop by remember { mutableIntStateOf(0) }
var searchHomeClicks by remember { mutableIntStateOf(0) }
val onTabClick: (RootTab) -> Unit = { tab ->
if (selected == tab) {
when (tab) {
RootTab.Feed -> feedScrollToTop++
RootTab.Profile -> profileScrollToTop++
RootTab.Search -> Unit
RootTab.Search -> searchHomeClicks++
}
} else {
selected = tab
Expand Down Expand Up @@ -1181,6 +1196,7 @@ private fun RootTabs(
feedScrollToTop = feedScrollToTop,
profileScrollToTop = profileScrollToTop,
focusSearchNonce = focusSearchNonce,
searchHomeClicks = searchHomeClicks,
container = container,
onVideoClick = onVideoClick,
onVideoInContext = onVideoInContext,
Expand Down Expand Up @@ -1218,6 +1234,7 @@ private fun RootTabs(
feedScrollToTop = feedScrollToTop,
profileScrollToTop = profileScrollToTop,
focusSearchNonce = focusSearchNonce,
searchHomeClicks = searchHomeClicks,
container = container,
onVideoClick = onVideoClick,
onVideoInContext = onVideoInContext,
Expand Down Expand Up @@ -1343,6 +1360,8 @@ private fun RootTabsContent(
feedScrollToTop: Int,
profileScrollToTop: Int,
focusSearchNonce: Int,
/** 每变一次就翻回着陆页,与回顶同一语义(搜索页没有"顶端"可回)。 */
searchHomeClicks: Int,
container: AppContainer,
onVideoClick: (String) -> Unit,
onVideoInContext: (String, QueueContext) -> Unit,
Expand Down Expand Up @@ -1416,7 +1435,15 @@ private fun RootTabsContent(
)

// 不限宽:宽屏结果是格子、助理是侧栏,搜索框自己限宽(见 SearchChatScreen)。
RootTab.Search -> SearchPane(container, focusSearchNonce, onVideoClick, onUserClick, onOpenArticle, onOpen)
RootTab.Search -> SearchPane(
container = container,
focusSearchNonce = focusSearchNonce,
searchHomeClicks = searchHomeClicks,
onVideoClick = onVideoClick,
onUserClick = onUserClick,
onOpenArticle = onOpenArticle,
onOpen = onOpen,
)

// 不限宽:宽屏每节是一排卡片,宽度换成条数(见 ProfileScreen);窄屏账号卡自己限宽。
RootTab.Profile -> ProfilePane(
Expand Down Expand Up @@ -1518,6 +1545,8 @@ private fun FeedPane(
private fun SearchPane(
container: AppContainer,
focusSearchNonce: Int,
/** 重按搜索 tab 的次数:每变一次回一次着陆页。 */
searchHomeClicks: Int,
onVideoClick: (String) -> Unit,
onUserClick: (Long) -> Unit,
onOpenArticle: (ArticleRef) -> Unit,
Expand All @@ -1536,6 +1565,15 @@ private fun SearchPane(
}
},
)
// **只认计数的变化,不认它的值。** 切走再切回来时 remember 重取当前值,effect 会再跑
// 一趟但两次读数相同 —— 没有那一下按下,就不该把结果页翻回着陆页。
var lastHomeClick by remember { mutableIntStateOf(searchHomeClicks) }
LaunchedEffect(searchHomeClicks) {
if (searchHomeClicks != lastHomeClick) {
lastHomeClick = searchHomeClicks
vm.backToLanding()
}
}
val currentOnOpen by rememberUpdatedState(onOpen)
LaunchedEffect(vm) { vm.open.collect { currentOnOpen(it) } }
val state by vm.state.collectAsStateWithLifecycle()
Expand All @@ -1551,6 +1589,7 @@ private fun SearchPane(
onHistoryRemove = vm::removeSearchHistory,
onHistoryClear = vm::clearSearchHistory,
onSuggestionClick = vm::searchSuggestion,
onBackToLanding = vm::backToLanding,
onInputChange = vm::onInputChange,
onSearch = vm::search,
onAskAgent = vm::askAgent,
Expand Down Expand Up @@ -1765,6 +1804,7 @@ private fun SettingsPageRoute(
onModeChange = vm::setThemeMode,
onPureBlackChange = vm::setPureBlack,
onPaletteChange = vm::setThemePalette,
onStartTabChange = vm::setStartTab,
onLanguageChange = system::applyLanguage,
onBack = onBack,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,12 @@ class NormalSearchController(
restart(userPager)
}

/** 回到还没搜过的状态:三栏归零、查询抹掉,界面翻回着陆页。重按搜索 tab 走这里。 */
fun clear() {
_state.update { it.copy(query = "") }
SearchTab.entries.forEach { pagerFor(it).reset(keepVisible = false) }
}

/** 重试和下拉刷新:当前栏从第一页重来,已有结果留在屏幕上等新页落地。 */
fun retry() {
val s = _state.value
Expand Down
Loading
Loading