Skip to content
Merged
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
392 changes: 297 additions & 95 deletions Cargo.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,6 @@ fun GithubMarkdown(
return false
}
})
loadDataWithBaseURL(
"https://appassets.androidplatform.net", html,
"text/html", StandardCharsets.UTF_8.name(), null
)
} catch (e: Throwable) {
Log.e("GithubMarkdown", "WebView setup failed", e)
}
Expand All @@ -319,11 +315,14 @@ fun GithubMarkdown(
update = { frameLayout ->
val webView = frameLayout.getChildAt(0) as? WebView ?: return@AndroidView
webView.settings.textZoom = newTextZoom
onLoadingChange(true)
webView.loadDataWithBaseURL(
"https://appassets.androidplatform.net", html,
"text/html", StandardCharsets.UTF_8.name(), null
)
if (webView.tag != html) {
webView.tag = html
onLoadingChange(true)
webView.loadDataWithBaseURL(
"https://appassets.androidplatform.net", html,
"text/html", StandardCharsets.UTF_8.name(), null
)
}
},
onRelease = { frameLayout ->
val webView = frameLayout.getChildAt(0) as? WebView
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,15 @@ import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.Sort
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.automirrored.outlined.ChromeReaderMode
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.outlined.Download
import androidx.compose.material.icons.outlined.InstallMobile
import androidx.compose.material.icons.outlined.Link
import androidx.compose.material.icons.rounded.Star
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Checkbox
import androidx.compose.material3.CircularWavyProgressIndicator
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
Expand All @@ -65,6 +64,7 @@ import androidx.compose.material3.LargeFlexibleTopAppBar
import androidx.compose.material3.LoadingIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Tab
import androidx.compose.material3.Text
Expand All @@ -87,7 +87,6 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.LocalLocale
import androidx.compose.ui.platform.UriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
Expand All @@ -99,17 +98,16 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import me.weishu.kernelsu.R
import me.weishu.kernelsu.data.model.RepoModule
import me.weishu.kernelsu.ui.component.markdown.GithubMarkdown
import me.weishu.kernelsu.ui.component.dialog.ConfirmDialogHandle
import me.weishu.kernelsu.ui.component.dialog.rememberConfirmDialog
import me.weishu.kernelsu.ui.component.markdown.GithubMarkdown
import me.weishu.kernelsu.ui.component.material.SearchAppBar
import me.weishu.kernelsu.ui.component.material.SegmentedColumn
import me.weishu.kernelsu.ui.component.material.SegmentedListItem
import me.weishu.kernelsu.ui.component.material.TonalCard
import me.weishu.kernelsu.ui.component.statustag.StatusTag
import me.weishu.kernelsu.ui.util.download
import me.weishu.kernelsu.ui.util.rememberContentReady
import java.text.Collator

@SuppressLint("LocalContextGetResourceValueCall")
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
Expand Down Expand Up @@ -139,43 +137,50 @@ fun ModuleRepoScreenMaterial(
)
},
actions = {
var showDropdown by remember { mutableStateOf(false) }
var showSortMenu by remember { mutableStateOf(false) }

IconButton(
onClick = { showDropdown = true }
onClick = { showSortMenu = true }
) {
Icon(
imageVector = Icons.Filled.MoreVert,
contentDescription = stringResource(id = R.string.settings)
imageVector = Icons.AutoMirrored.Filled.Sort,
contentDescription = stringResource(R.string.menu_sort)
)

DropdownMenu(expanded = showDropdown, onDismissRequest = {
showDropdown = false
DropdownMenu(expanded = showSortMenu, onDismissRequest = {
showSortMenu = false
}) {
DropdownMenuItem(
text = { Text(stringResource(R.string.module_repos_sort_name)) },
trailingIcon = { Checkbox(state.sortByName, null) },
onClick = {
haptic.performHapticFeedback(HapticFeedbackType.VirtualKey)
actions.onToggleSortByName()
}
val sortOptions = listOf(
RepoSort.UPDATED to R.string.module_repos_sort_updated,
RepoSort.CREATED to R.string.module_repos_sort_created,
RepoSort.NAME to R.string.module_repos_sort_name,
RepoSort.STARS to R.string.module_repos_sort_stars,
)
sortOptions.forEach { (order, resId) ->
DropdownMenuItem(
text = { Text(stringResource(resId)) },
trailingIcon = {
RadioButton(
selected = state.sortOrder == order,
onClick = null,
)
},
onClick = {
haptic.performHapticFeedback(HapticFeedbackType.VirtualKey)
actions.onSetSortOrder(order)
showSortMenu = false
}
)
}
}
}
},
searchContent = { _, closeSearch ->
LaunchedEffect(state.searchStatus.searchText) {
searchListState.scrollToItem(0)
}
val sortByName = state.sortByName
val collator = Collator.getInstance(LocalLocale.current.platformLocale)
val searchModules = if (!sortByName) {
state.searchResults
} else {
state.searchResults.sortedWith(compareBy(collator) { it.moduleName })
}
RepoModuleList(
modules = searchModules,
modules = state.searchResults,
listState = searchListState,
modifier = Modifier.fillMaxSize(),
onModuleClick = {
Expand Down Expand Up @@ -215,13 +220,11 @@ fun ModuleRepoScreenMaterial(
}
}
if (!isLoading && contentReady) {
val platformLocale = LocalLocale.current.platformLocale
val displayModules = remember(state.modules, state.sortByName) {
val collator = Collator.getInstance(platformLocale)
if (!state.sortByName) state.modules else state.modules.sortedWith(compareBy(collator) { it.moduleName })
LaunchedEffect(state.sortOrder) {
listState.scrollToItem(0)
}
RepoModuleList(
modules = displayModules,
modules = state.modules,
listState = listState,
modifier = Modifier
.fillMaxSize()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.RoundedCornerShape
Expand Down Expand Up @@ -64,7 +65,6 @@ import androidx.compose.ui.layout.positionInWindow
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.platform.LocalLocale
import androidx.compose.ui.platform.UriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
Expand Down Expand Up @@ -119,14 +119,13 @@ import top.yukonga.miuix.kmp.icon.extended.Back
import top.yukonga.miuix.kmp.icon.extended.FileDownloads
import top.yukonga.miuix.kmp.icon.extended.HorizontalSplit
import top.yukonga.miuix.kmp.icon.extended.Link
import top.yukonga.miuix.kmp.icon.extended.MoreCircle
import top.yukonga.miuix.kmp.icon.extended.Sort
import top.yukonga.miuix.kmp.icon.extended.TopDownloads
import top.yukonga.miuix.kmp.overlay.OverlayListPopup
import top.yukonga.miuix.kmp.theme.MiuixTheme.colorScheme
import top.yukonga.miuix.kmp.utils.PressFeedbackType
import top.yukonga.miuix.kmp.utils.overScrollVertical
import top.yukonga.miuix.kmp.utils.scrollEndHaptic
import java.text.Collator

@SuppressLint("LocalContextGetResourceValueCall")
@Composable
Expand All @@ -136,7 +135,6 @@ fun ModuleRepoScreenMiuix(
) {
val searchStatus = state.searchStatus
val density = LocalDensity.current
val platformLocale = LocalLocale.current.platformLocale
val metaBg = colorScheme.tertiaryContainer.copy(alpha = 0.6f)
val metaTint = colorScheme.onTertiaryContainer.copy(alpha = 0.8f)

Expand All @@ -162,33 +160,42 @@ fun ModuleRepoScreenMiuix(
color = barColor,
title = stringResource(R.string.module_repos),
actions = {
val showTopPopup = remember { mutableStateOf(false) }
val showSortPopup = remember { mutableStateOf(false) }
OverlayListPopup(
show = showTopPopup.value, popupPositionProvider = ListPopupDefaults.MenuPositionProvider,
show = showSortPopup.value,
popupPositionProvider = ListPopupDefaults.MenuPositionProvider,
alignment = PopupPositionProvider.Align.TopEnd,
onDismissRequest = { showTopPopup.value = false },
onDismissRequest = { showSortPopup.value = false },
content = {
ListPopupColumn {
DropdownImpl(
text = stringResource(R.string.module_repos_sort_name),
optionSize = 1,
isSelected = state.sortByName,
onSelectedIndexChange = {
actions.onToggleSortByName()
showTopPopup.value = false
},
index = 0
val sortOptions = listOf(
RepoSort.UPDATED to R.string.module_repos_sort_updated,
RepoSort.CREATED to R.string.module_repos_sort_created,
RepoSort.NAME to R.string.module_repos_sort_name,
RepoSort.STARS to R.string.module_repos_sort_stars,
)
sortOptions.forEachIndexed { index, (order, resId) ->
DropdownImpl(
text = stringResource(resId),
optionSize = sortOptions.size,
isSelected = state.sortOrder == order,
onSelectedIndexChange = {
actions.onSetSortOrder(order)
showSortPopup.value = false
},
index = index,
)
}
}
})
IconButton(
onClick = { showTopPopup.value = true },
holdDownState = showTopPopup.value
onClick = { showSortPopup.value = true },
holdDownState = showSortPopup.value
) {
Icon(
imageVector = MiuixIcons.MoreCircle,
imageVector = MiuixIcons.Sort,
tint = colorScheme.onSurface,
contentDescription = null,
contentDescription = stringResource(R.string.menu_sort),
)
}
},
Expand Down Expand Up @@ -242,10 +249,6 @@ fun ModuleRepoScreenMiuix(
defaultResult = {},
searchBarTopPadding = dynamicTopPadding,
) {
val displaySearch = remember(state.searchResults, state.sortByName) {
val collator = Collator.getInstance(platformLocale)
if (!state.sortByName) state.searchResults else state.searchResults.sortedWith(compareBy(collator) { it.moduleName })
}
LazyColumn(
modifier = Modifier
.fillMaxSize()
Expand All @@ -254,7 +257,7 @@ fun ModuleRepoScreenMiuix(
item {
Spacer(Modifier.height(6.dp))
}
items(displaySearch, key = { it.moduleId }, contentType = { "module" }) { module ->
items(state.searchResults, key = { it.moduleId }, contentType = { "module" }) { module ->
Card(
modifier = Modifier
.fillMaxWidth()
Expand Down Expand Up @@ -382,6 +385,10 @@ fun ModuleRepoScreenMiuix(
}
if (!isLoading && contentReady) {
val pullToRefreshState = rememberPullToRefreshState()
val lazyListState = rememberLazyListState()
LaunchedEffect(state.sortOrder) {
lazyListState.scrollToItem(0)
}
val refreshTexts = listOf(
stringResource(R.string.refresh_pulling),
stringResource(R.string.refresh_release),
Expand All @@ -399,12 +406,9 @@ fun ModuleRepoScreenMiuix(
end = innerPadding.calculateEndPadding(layoutDirection)
),
) {
val displayModules = remember(state.modules, state.sortByName) {
val collator = Collator.getInstance(platformLocale)
if (!state.sortByName) state.modules else state.modules.sortedWith(compareBy(collator) { it.moduleName })
}
Box(modifier = if (backdrop != null) Modifier.layerBackdrop(backdrop) else Modifier) {
LazyColumn(
state = lazyListState,
modifier = Modifier
.fillMaxHeight()
.scrollEndHaptic()
Expand All @@ -417,7 +421,7 @@ fun ModuleRepoScreenMiuix(
),
overscrollEffect = null,
) {
items(items = displayModules, key = { it.moduleId }, contentType = { "module" }) { module ->
items(items = state.modules, key = { it.moduleId }, contentType = { "module" }) { module ->
val moduleAuthor = stringResource(id = R.string.module_author)

Card(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ fun ModuleRepoScreen() {
onSearchTextChange = viewModel::updateSearchText,
onClearSearch = { viewModel.updateSearchText("") },
onSearchStatusChange = viewModel::updateSearchStatus,
onToggleSortByName = viewModel::toggleSortByName,
onSetSortOrder = viewModel::setSortOrder,
onOpenRepoDetail = { module ->
val args = RepoModuleArg(
moduleId = module.moduleId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@ import androidx.compose.runtime.Immutable
import me.weishu.kernelsu.data.model.RepoModule
import me.weishu.kernelsu.ui.component.SearchStatus

enum class RepoSort {
UPDATED,
CREATED,
NAME,
STARS,
}

data class ModuleRepoUiState(
val isRefreshing: Boolean = false,
val sortByName: Boolean = false,
val sortOrder: RepoSort = RepoSort.UPDATED,
val offline: Boolean = false,
val modules: List<RepoModule> = emptyList(),
val searchStatus: SearchStatus = SearchStatus(""),
Expand All @@ -21,7 +28,7 @@ data class ModuleRepoActions(
val onSearchTextChange: (String) -> Unit,
val onClearSearch: () -> Unit,
val onSearchStatusChange: (SearchStatus) -> Unit,
val onToggleSortByName: () -> Unit,
val onSetSortOrder: (RepoSort) -> Unit,
val onOpenRepoDetail: (RepoModule) -> Unit,
)

Expand Down
Loading