Added log loading screen

This commit is contained in:
Abdelilah El Aissaoui 2023-08-06 12:35:58 +02:00
parent 594e41eda2
commit 0c3ced89b4

View File

@ -26,9 +26,7 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.clipRect import androidx.compose.ui.graphics.drawscope.clipRect
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.key.KeyEventType
import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.key.type
import androidx.compose.ui.input.pointer.PointerIcon import androidx.compose.ui.input.pointer.PointerIcon
import androidx.compose.ui.input.pointer.pointerHoverIcon import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
@ -94,184 +92,211 @@ fun Log(
selectedItem: SelectedItem, selectedItem: SelectedItem,
repositoryState: RepositoryState, repositoryState: RepositoryState,
) { ) {
val scope = rememberCoroutineScope()
val logStatusState = logViewModel.logStatus.collectAsState() val logStatusState = logViewModel.logStatus.collectAsState()
val logStatus = logStatusState.value val logStatus = logStatusState.value
val showLogDialog by logViewModel.logDialog.collectAsState() val showLogDialog by logViewModel.logDialog.collectAsState()
when (logStatus) {
is LogStatus.Loaded -> LogLoaded(logViewModel, logStatus, showLogDialog, selectedItem, repositoryState)
LogStatus.Loading -> LogLoading()
}
}
@Composable
private fun LogLoading() {
Column (
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.background),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
CircularProgressIndicator()
Text(
text = "Loading commits history...",
style = MaterialTheme.typography.h4,
modifier = Modifier.padding(top = 8.dp),
)
}
}
@Composable
private fun LogLoaded(
logViewModel: LogViewModel,
logStatus: LogStatus.Loaded,
showLogDialog: LogDialog,
selectedItem: SelectedItem,
repositoryState: RepositoryState
) {
val scope = rememberCoroutineScope()
val hasUncommitedChanges = logStatus.hasUncommittedChanges
val commitList = logStatus.plotCommitList
val verticalScrollState by logViewModel.verticalListState.collectAsState()
val horizontalScrollState by logViewModel.horizontalListState.collectAsState()
val searchFilter = logViewModel.logSearchFilterResults.collectAsState()
val searchFilterValue = searchFilter.value
val selectedCommit = if (selectedItem is SelectedItem.CommitBasedItem) { val selectedCommit = if (selectedItem is SelectedItem.CommitBasedItem) {
selectedItem.revCommit selectedItem.revCommit
} else { } else {
null null
} }
if (logStatus is LogStatus.Loaded) { LaunchedEffect(verticalScrollState, commitList) {
val hasUncommitedChanges = logStatus.hasUncommitedChanges launch {
val commitList = logStatus.plotCommitList logViewModel.focusCommit.collect { commit ->
val verticalScrollState by logViewModel.verticalListState.collectAsState() scrollToCommit(verticalScrollState, commitList, commit)
val horizontalScrollState by logViewModel.horizontalListState.collectAsState()
val searchFilter = logViewModel.logSearchFilterResults.collectAsState()
val searchFilterValue = searchFilter.value
// With this method, whenever the scroll changes, the log is recomposed and the graph list is updated with
// the proper scroll position
verticalScrollState.observeScrollChanges()
LaunchedEffect(verticalScrollState, commitList) {
launch {
logViewModel.focusCommit.collect { commit ->
scrollToCommit(verticalScrollState, commitList, commit)
}
}
launch {
logViewModel.scrollToUncommitedChanges.collect {
scrollToUncommitedChanges(verticalScrollState, commitList)
}
} }
} }
launch {
logViewModel.scrollToUncommitedChanges.collect {
scrollToUncommitedChanges(verticalScrollState, commitList)
}
}
}
LogDialogs( LogDialogs(
logViewModel, logViewModel,
onResetShowLogDialog = { logViewModel.showDialog(LogDialog.None) }, onResetShowLogDialog = { logViewModel.showDialog(LogDialog.None) },
showLogDialog = showLogDialog, showLogDialog = showLogDialog,
)
Column(
modifier = Modifier
.background(MaterialTheme.colors.background)
.fillMaxSize()
) {
var graphPadding by remember(logViewModel) { mutableStateOf(logViewModel.graphPadding) }
var graphWidth = (CANVAS_DEFAULT_WIDTH + graphPadding).dp
if (graphWidth.value < CANVAS_MIN_WIDTH) graphWidth = CANVAS_MIN_WIDTH.dp
val maxLinePosition = if (commitList.isNotEmpty())
commitList.maxLine
else
MIN_GRAPH_LANES
var graphRealWidth = ((maxLinePosition + MARGIN_GRAPH_LANES) * LANE_WIDTH).dp
// Using remember(graphRealWidth, graphWidth) makes the selected background color glitch when changing tabs
if (graphRealWidth < graphWidth) {
graphRealWidth = graphWidth
}
if (searchFilterValue is LogSearch.SearchResults) {
SearchFilter(logViewModel, searchFilterValue)
}
GraphHeader(
graphWidth = graphWidth,
onPaddingChange = {
graphPadding += it
logViewModel.graphPadding = graphPadding
},
onShowSearch = { scope.launch { logViewModel.onSearchValueChanged("") } }
) )
Column( Box {
modifier = Modifier Box(
.background(MaterialTheme.colors.background) Modifier
.fillMaxSize() .width(graphWidth)
) { .fillMaxHeight()
var graphPadding by remember(logViewModel) { mutableStateOf(logViewModel.graphPadding) } ) {
var graphWidth = (CANVAS_DEFAULT_WIDTH + graphPadding).dp
if (graphWidth.value < CANVAS_MIN_WIDTH) graphWidth = CANVAS_MIN_WIDTH.dp
val maxLinePosition = if (commitList.isNotEmpty())
commitList.maxLine
else
MIN_GRAPH_LANES
var graphRealWidth = ((maxLinePosition + MARGIN_GRAPH_LANES) * LANE_WIDTH).dp
// Using remember(graphRealWidth, graphWidth) makes the selected background color glitch when changing tabs
if (graphRealWidth < graphWidth) {
graphRealWidth = graphWidth
}
if (searchFilterValue is LogSearch.SearchResults) {
SearchFilter(logViewModel, searchFilterValue)
}
GraphHeader(
graphWidth = graphWidth,
onPaddingChange = {
graphPadding += it
logViewModel.graphPadding = graphPadding
},
onShowSearch = { scope.launch { logViewModel.onSearchValueChanged("") } }
)
Box {
Box( Box(
Modifier modifier = Modifier
.width(graphWidth) .fillMaxSize()
.fillMaxHeight() .horizontalScroll(horizontalScrollState)
.padding(bottom = 8.dp)
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier.width(graphRealWidth)
.fillMaxSize() )
.horizontalScroll(horizontalScrollState)
.padding(bottom = 8.dp)
) {
Box(
modifier = Modifier.width(graphRealWidth)
)
}
} }
}
// The commits' messages list overlaps with the graph list to catch all the click events but leaves // The commits' messages list overlaps with the graph list to catch all the click events but leaves
// a padding, so it doesn't cover the graph // a padding, so it doesn't cover the graph
MessagesList( MessagesList(
scrollState = verticalScrollState, scrollState = verticalScrollState,
horizontalScrollState = horizontalScrollState, horizontalScrollState = horizontalScrollState,
hasUncommitedChanges = hasUncommitedChanges, hasUncommitedChanges = hasUncommitedChanges,
searchFilter = if (searchFilterValue is LogSearch.SearchResults) searchFilterValue.commits else null, searchFilter = if (searchFilterValue is LogSearch.SearchResults) searchFilterValue.commits else null,
selectedCommit = selectedCommit, selectedCommit = selectedCommit,
logStatus = logStatus, logStatus = logStatus,
repositoryState = repositoryState, repositoryState = repositoryState,
selectedItem = selectedItem, selectedItem = selectedItem,
commitList = commitList, commitList = commitList,
logViewModel = logViewModel, logViewModel = logViewModel,
graphWidth = graphWidth, graphWidth = graphWidth,
commitsLimit = logStatus.commitsLimit, commitsLimit = logStatus.commitsLimit,
onMerge = { ref -> onMerge = { ref ->
logViewModel.mergeBranch(ref) logViewModel.mergeBranch(ref)
}, },
onRebase = { ref -> onRebase = { ref ->
logViewModel.rebaseBranch(ref) logViewModel.rebaseBranch(ref)
}, },
onShowLogDialog = { dialog -> onShowLogDialog = { dialog ->
logViewModel.showDialog(dialog) logViewModel.showDialog(dialog)
} }
) )
val density = LocalDensity.current.density val density = LocalDensity.current.density
DividerLog( DividerLog(
modifier = Modifier.draggable( modifier = Modifier.draggable(
rememberDraggableState { rememberDraggableState {
graphPadding += it / density graphPadding += it / density
logViewModel.graphPadding = graphPadding logViewModel.graphPadding = graphPadding
}, Orientation.Horizontal }, Orientation.Horizontal
), ),
graphWidth = graphWidth, graphWidth = graphWidth,
) )
// Scrollbar used to scroll horizontally the graph nodes // Scrollbar used to scroll horizontally the graph nodes
// Added after every component to have the highest priority when clicking // Added after every component to have the highest priority when clicking
HorizontalScrollbar( HorizontalScrollbar(
modifier = Modifier
.align(Alignment.BottomStart)
.width(graphWidth)
.padding(start = 4.dp, bottom = 4.dp), style = LocalScrollbarStyle.current.copy(
unhoverColor = MaterialTheme.colors.scrollbarNormal,
hoverColor = MaterialTheme.colors.scrollbarHover,
),
adapter = rememberScrollbarAdapter(horizontalScrollState)
)
val isFirstItemVisible by remember {
derivedStateOf { verticalScrollState.firstVisibleItemIndex > 0 }
}
if (isFirstItemVisible) {
Box(
modifier = Modifier modifier = Modifier
.align(Alignment.BottomStart) .align(Alignment.TopCenter)
.width(graphWidth) .padding(top = 16.dp)
.padding(start = 4.dp, bottom = 4.dp), style = LocalScrollbarStyle.current.copy( .clip(RoundedCornerShape(50))
unhoverColor = MaterialTheme.colors.scrollbarNormal, .handMouseClickable {
hoverColor = MaterialTheme.colors.scrollbarHover, scope.launch {
), verticalScrollState.scrollToItem(0)
adapter = rememberScrollbarAdapter(horizontalScrollState)
)
val isFirstItemVisible by remember {
derivedStateOf { verticalScrollState.firstVisibleItemIndex > 0 }
}
if (isFirstItemVisible) {
Box(
modifier = Modifier
.align(Alignment.TopCenter)
.padding(top = 16.dp)
.clip(RoundedCornerShape(50))
.handMouseClickable {
scope.launch {
verticalScrollState.scrollToItem(0)
}
} }
.background(MaterialTheme.colors.primary)
.padding(vertical = 4.dp, horizontal = 8.dp),
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
painterResource(AppIcons.ALIGN_TOP),
contentDescription = null,
tint = MaterialTheme.colors.onPrimary,
modifier = Modifier.size(20.dp),
)
Text(
text = "Scroll to top",
modifier = Modifier.padding(start = 8.dp),
color = MaterialTheme.colors.onPrimary,
style = MaterialTheme.typography.body2,
)
} }
.background(MaterialTheme.colors.primary)
.padding(vertical = 4.dp, horizontal = 8.dp),
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
painterResource(AppIcons.ALIGN_TOP),
contentDescription = null,
tint = MaterialTheme.colors.onPrimary,
modifier = Modifier.size(20.dp),
)
Text(
text = "Scroll to top",
modifier = Modifier.padding(start = 8.dp),
color = MaterialTheme.colors.onPrimary,
style = MaterialTheme.typography.body2,
)
} }
} }
} }