Gitnuro/src/main/kotlin/app/ui/FileHistory.kt
2022-06-06 15:02:32 +02:00

243 lines
7.4 KiB
Kotlin

@file:OptIn(ExperimentalComposeUiApi::class, ExperimentalSplitPaneApi::class)
package app.ui
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.material.IconButton
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.onKeyEvent
import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.input.pointer.PointerIconDefaults
import androidx.compose.ui.input.pointer.pointerHoverIcon
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import app.extensions.handMouseClickable
import app.extensions.toSmartSystemString
import app.extensions.toSystemDateTimeString
import app.git.diff.DiffResult
import app.theme.headerBackground
import app.theme.primaryTextColor
import app.theme.secondaryTextColor
import app.ui.components.AvatarImage
import app.ui.components.ScrollableLazyColumn
import app.ui.components.TooltipText
import app.viewmodels.HistoryState
import app.viewmodels.HistoryViewModel
import app.viewmodels.ViewDiffResult
import org.eclipse.jgit.revwalk.RevCommit
import org.jetbrains.compose.splitpane.ExperimentalSplitPaneApi
@Composable
fun FileHistory(
historyViewModel: HistoryViewModel,
onClose: () -> Unit
) {
val historyState by historyViewModel.historyState.collectAsState()
val focusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
Column(
modifier = Modifier
.fillMaxSize()
.focusRequester(focusRequester)
.focusable()
.onKeyEvent {
if (it.key == Key.Escape) {
onClose()
true
} else
false
},
) {
Header(filePath = historyState.filePath, onClose = onClose)
HistoryContent(
historyViewModel,
historyState,
onCommitSelected = { historyViewModel.selectCommit(it) }
)
}
}
@Composable
private fun Header(
filePath: String,
onClose: () -> Unit,
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(40.dp)
.background(MaterialTheme.colors.headerBackground)
.padding(start = 8.dp, end = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = filePath,
color = MaterialTheme.colors.primaryTextColor,
fontSize = 13.sp,
modifier = Modifier.padding(horizontal = 16.dp),
)
Spacer(modifier = Modifier.weight(1f))
IconButton(
onClick = onClose,
modifier = Modifier
.pointerHoverIcon(PointerIconDefaults.Hand)
) {
Image(
painter = painterResource("close.svg"),
contentDescription = "Close history",
colorFilter = ColorFilter.tint(MaterialTheme.colors.primaryTextColor),
)
}
}
}
@Composable
private fun HistoryContent(
historyViewModel: HistoryViewModel,
historyState: HistoryState,
onCommitSelected: (RevCommit) -> Unit,
) {
val textScrollState by historyViewModel.lazyListState.collectAsState()
val viewDiffResult by historyViewModel.viewDiffResult.collectAsState()
when (historyState) {
is HistoryState.Loaded -> HistoryContentLoaded(
historyState = historyState,
viewDiffResult = viewDiffResult,
scrollState = textScrollState,
onCommitSelected = onCommitSelected,
)
is HistoryState.Loading -> Box { }
}
}
@Composable
fun HistoryContentLoaded(
historyState: HistoryState.Loaded,
viewDiffResult: ViewDiffResult?,
scrollState: LazyListState,
onCommitSelected: (RevCommit) -> Unit,
) {
Row(
modifier = Modifier
.fillMaxSize()
) {
ScrollableLazyColumn(
modifier = Modifier
.fillMaxHeight()
.width(300.dp)
.background(MaterialTheme.colors.surface)
) {
items(historyState.commits) { commit ->
HistoryCommit(commit, onCommitSelected = { onCommitSelected(commit) })
}
}
Column(
modifier = Modifier
.fillMaxSize()
) {
if (
viewDiffResult != null &&
viewDiffResult is ViewDiffResult.Loaded
) {
val diffResult = viewDiffResult.diffResult
if (diffResult is DiffResult.Text) {
TextDiff(
diffEntryType = viewDiffResult.diffEntryType,
scrollState = scrollState,
diffResult = diffResult,
onUnstageHunk = { _, _ -> },
onStageHunk = { _, _ -> }
)
} else {
Box(
modifier = Modifier.fillMaxSize()
.background(MaterialTheme.colors.background)
)
}
} else {
Box(
modifier = Modifier.fillMaxSize()
.background(MaterialTheme.colors.background)
)
}
}
}
}
@Composable
fun HistoryCommit(commit: RevCommit, onCommitSelected: () -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.handMouseClickable { onCommitSelected() }
.padding(vertical = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
AvatarImage(
modifier = Modifier
.padding(horizontal = 16.dp)
.size(40.dp),
personIdent = commit.authorIdent,
)
Column {
Text(
text = commit.shortMessage,
maxLines = 1,
fontSize = 14.sp,
color = MaterialTheme.colors.primaryTextColor,
)
Row {
Text(
text = commit.name.take(7),
maxLines = 1,
fontSize = 12.sp,
color = MaterialTheme.colors.secondaryTextColor,
)
Spacer(modifier = Modifier.weight(1f))
val date = remember(commit.authorIdent) {
commit.authorIdent.`when`.toSmartSystemString()
}
TooltipText(
text = date,
color = MaterialTheme.colors.secondaryTextColor,
maxLines = 1,
modifier = Modifier.padding(horizontal = 16.dp),
fontSize = 12.sp,
tooltipTitle = date
)
}
}
}
}