Gitnuro/src/main/kotlin/app/App.kt

272 lines
8.5 KiB
Kotlin

@file:OptIn(ExperimentalComposeUiApi::class)
package app
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Icon
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.draw.alpha
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Window
import androidx.compose.ui.window.WindowPlacement
import androidx.compose.ui.window.application
import androidx.compose.ui.window.rememberWindowState
import androidx.compose.ui.zIndex
import app.di.DaggerAppComponent
import app.theme.AppTheme
import app.theme.primaryTextColor
import app.theme.secondaryTextColor
import app.ui.components.RepositoriesTabPanel
import app.ui.components.TabInformation
import app.ui.dialogs.SettingsDialog
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import javax.inject.Inject
class App {
private val appComponent = DaggerAppComponent.create()
@Inject
lateinit var appStateManager: AppStateManager
@Inject
lateinit var appPreferences: AppPreferences
init {
appComponent.inject(this)
}
private val tabsFlow = MutableStateFlow<List<TabInformation>>(emptyList())
fun start() {
appStateManager.loadRepositoriesTabs()
loadTabs()
application {
var isOpen by remember { mutableStateOf(true) }
val theme by appPreferences.themeState.collectAsState()
if (isOpen) {
Window(
title = AppConstants.APP_NAME,
onCloseRequest = {
isOpen = false
},
state = rememberWindowState(
placement = WindowPlacement.Maximized,
size = DpSize(1280.dp, 720.dp)
),
icon = painterResource("logo.svg"),
) {
var showSettingsDialog by remember { mutableStateOf(false) }
AppTheme(theme = theme) {
Box(modifier = Modifier.background(MaterialTheme.colors.background)) {
AppTabs(
onOpenSettings = {
showSettingsDialog = true
}
)
}
if (showSettingsDialog) {
SettingsDialog(
appPreferences = appPreferences,
onDismiss = { showSettingsDialog = false }
)
}
}
}
} else {
appStateManager.cancelCoroutines()
this.exitApplication()
}
}
}
private fun loadTabs() {
val repositoriesSavedTabs = appStateManager.openRepositoriesPathsTabs
var repoTabs = repositoriesSavedTabs.map { repositoryTab ->
newAppTab(
key = repositoryTab.key,
path = repositoryTab.value
)
}
if (repoTabs.isEmpty()) {
repoTabs = listOf(
newAppTab()
)
}
tabsFlow.value = repoTabs
println("After reading prefs, got ${tabsFlow.value.count()} tabs")
}
@Composable
fun AppTabs(
onOpenSettings: () -> Unit,
) {
val tabs by tabsFlow.collectAsState()
val tabsInformationList = tabs.sortedBy { it.key }
println("Tabs count ${tabs.count()}")
val selectedTabKey = remember { mutableStateOf(0) }
println("Selected tab key: ${selectedTabKey.value}")
Column(
modifier = Modifier.background(MaterialTheme.colors.background)
) {
Tabs(
tabsInformationList = tabsInformationList,
selectedTabKey = selectedTabKey,
onOpenSettings = onOpenSettings,
onAddedTab = { tabInfo ->
addTab(tabInfo)
},
onRemoveTab = { key ->
removeTab(key)
}
)
TabsContent(tabsInformationList, selectedTabKey.value)
}
}
private fun removeTab(key: Int) = appStateManager.appStateScope.launch(Dispatchers.IO) {
// Stop any running jobs
val tabs = tabsFlow.value
val tabToRemove = tabs.firstOrNull { it.key == key } ?: return@launch
tabToRemove.tabViewModel.dispose()
// Remove tab from persistent tabs storage
appStateManager.repositoryTabRemoved(key)
// Remove from tabs flow
tabsFlow.value = tabsFlow.value.filter { tab -> tab.key != key }
}
fun addTab(tabInformation: TabInformation) = appStateManager.appStateScope.launch(Dispatchers.IO) {
tabsFlow.value = tabsFlow.value.toMutableList().apply { add(tabInformation) }
}
@Composable
fun Tabs(
selectedTabKey: MutableState<Int>,
onOpenSettings: () -> Unit,
tabsInformationList: List<TabInformation>,
onAddedTab: (TabInformation) -> Unit,
onRemoveTab: (Int) -> Unit,
) {
Row(
modifier = Modifier
.padding(top = 4.dp, bottom = 0.dp, start = 4.dp, end = 4.dp)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
RepositoriesTabPanel(
modifier = Modifier
.weight(1f),
tabs = tabsInformationList,
selectedTabKey = selectedTabKey.value,
onTabSelected = { newSelectedTabKey ->
println("New selected tab key $newSelectedTabKey")
selectedTabKey.value = newSelectedTabKey
},
newTabContent = { key ->
val newAppTab = newAppTab(
key = key
)
onAddedTab(newAppTab)
newAppTab
},
onTabClosed = onRemoveTab
)
IconButton(
modifier = Modifier
.padding(horizontal = 8.dp)
.size(24.dp),
onClick = onOpenSettings
) {
Icon(
painter = painterResource("settings.svg"),
contentDescription = null,
modifier = Modifier.fillMaxSize(),
tint = MaterialTheme.colors.primary,
)
}
}
}
private fun newAppTab(
key: Int = 0,
tabName: MutableState<String> = mutableStateOf("New tab"),
path: String? = null,
): TabInformation {
return TabInformation(
tabName = tabName,
key = key,
path = path,
appComponent = appComponent,
)
}
}
@Composable
private fun TabsContent(tabs: List<TabInformation>, selectedTabKey: Int) {
LazyColumn(
modifier = Modifier
.fillMaxSize(),
) {
items(items = tabs, key = { it.key }) {
val isItemSelected = it.key == selectedTabKey
var tabMod: Modifier = if (!isItemSelected)
Modifier.size(0.dp)
else
Modifier
.fillParentMaxSize()
tabMod = tabMod.background(MaterialTheme.colors.primary)
.alpha(if (isItemSelected) 1f else -1f)
.zIndex(if (isItemSelected) 1f else -1f)
Box(
modifier = tabMod,
) {
it.content(it)
}
}
}
}
@Composable
fun LoadingRepository(repoPath: String) {
Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("Opening repository", fontSize = 36.sp, color = MaterialTheme.colors.primaryTextColor)
Text(repoPath, fontSize = 24.sp, color = MaterialTheme.colors.secondaryTextColor)
}
}
}