Added updates check

This commit is contained in:
Abdelilah El Aissaoui 2022-04-04 02:48:40 +02:00
parent d3fd889e02
commit 8f92b6d195
11 changed files with 233 additions and 123 deletions

View file

@ -11,6 +11,7 @@ plugins {
id("org.jetbrains.compose") version "1.1.1"
}
// Remember to update Constants.APP_VERSION when changing this version
val projectVersion = "0.1.0"
val projectName = "Gitnuro"
@ -23,8 +24,6 @@ repositories {
maven { url = uri("https://maven.pkg.jetbrains.space/public/p/compose/dev") }
}
dependencies {
implementation(compose.desktop.currentOs)
@OptIn(org.jetbrains.compose.ExperimentalComposeLibrary::class)
@ -37,6 +36,8 @@ dependencies {
testImplementation(platform("org.junit:junit-bom:5.8.2"))
testImplementation("org.junit.jupiter:junit-jupiter")
testImplementation("io.mockk:mockk:1.12.3")
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-scalars:2.9.0")
}
tasks.test {

View file

@ -48,7 +48,6 @@ class App {
init {
appComponent.inject(this)
println("AppStateManagerReference $appStateManager")
}
private val tabsFlow = MutableStateFlow<List<TabInformation>>(emptyList())

View file

@ -0,0 +1,8 @@
package app
object AppConstants {
// Remember to update build.gradle when changing this
const val APP_VERSION = "0.1.0"
const val APP_VERSION_CODE = 1
const val VERSION_CHECK_URL = "https://raw.githubusercontent.com/JetpackDuba/Gitnuro/main/latest.json"
}

View file

@ -1,10 +1,18 @@
package app.di
import app.di.modules.NetworkModule
import app.ui.components.TabInformation
import dagger.Component
@TabScope
@Component(dependencies = [AppComponent::class])
@Component(
modules = [
NetworkModule::class,
],
dependencies = [
AppComponent::class
],
)
interface TabComponent {
fun inject(tabInformation: TabInformation)
}

View file

@ -0,0 +1,20 @@
package app.di.modules
import app.updates.UpdatesService
import dagger.Provides
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.scalars.ScalarsConverterFactory
import javax.inject.Singleton
@dagger.Module
class NetworkModule {
@Provides
fun provideWebService(): UpdatesService {
return Retrofit.Builder()
.baseUrl("https://github.com")
.addConverterFactory(ScalarsConverterFactory.create())
.build()
.create(UpdatesService::class.java)
}
}

View file

@ -13,16 +13,19 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.BiasAlignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import app.AppConstants
import app.extensions.dirName
import app.extensions.dirPath
import app.theme.primaryTextColor
import app.theme.secondaryTextColor
import app.ui.dialogs.CloneDialog
import app.updates.Update
import app.viewmodels.TabViewModel
import openDirectoryDialog
import openRepositoryDialog
@ -37,148 +40,165 @@ fun WelcomePage(
) {
val appStateManager = tabViewModel.appStateManager
var showCloneView by remember { mutableStateOf(false) }
var newUpdate by remember { mutableStateOf<Update?>(null) }
// Crossfade(showCloneView) {
// if(it) {
LaunchedEffect(Unit) {
val latestRelease = tabViewModel.latestRelease()
if(latestRelease != null && latestRelease.appCode > AppConstants.APP_VERSION_CODE) {
newUpdate = latestRelease
}
}
// } else {
Row(
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colors.background),
horizontalArrangement = Arrangement.Center,
verticalAlignment = BiasAlignment.Vertical(-0.5f),
) {
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.Top,
modifier = Modifier.align(BiasAlignment(0f, -0.5f))
) {
Column(
modifier = Modifier
.padding(end = 32.dp),
verticalArrangement = Arrangement.Center,
) {
Text(
text = "Gitnuro",
fontSize = 32.sp,
color = MaterialTheme.colors.primaryTextColor,
modifier = Modifier
.padding(bottom = 16.dp),
)
Column(
modifier = Modifier.padding(end = 32.dp),
) {
Text(
text = "Gitnuro",
fontSize = 32.sp,
color = MaterialTheme.colors.primaryTextColor,
modifier = Modifier.padding(bottom = 16.dp),
)
ButtonTile(
modifier = Modifier
.padding(bottom = 8.dp),
title = "Open a repository",
painter = painterResource("open.svg"),
onClick = { openRepositoryDialog(tabViewModel) }
)
ButtonTile(
modifier = Modifier.padding(bottom = 8.dp),
title = "Open a repository",
painter = painterResource("open.svg"),
onClick = { openRepositoryDialog(tabViewModel) })
ButtonTile(
modifier = Modifier
.padding(bottom = 8.dp),
title = "Clone a repository",
painter = painterResource("download.svg"),
onClick = {
showCloneView = true
ButtonTile(
modifier = Modifier.padding(bottom = 8.dp),
title = "Clone a repository",
painter = painterResource("download.svg"),
onClick = {
showCloneView = true
}
)
ButtonTile(
modifier = Modifier.padding(bottom = 8.dp),
title = "Start a local repository",
painter = painterResource("open.svg"),
onClick = {
val dir = openDirectoryDialog()
if (dir != null) tabViewModel.initLocalRepository(dir)
}
)
Text(
text = "About Gitnuro",
fontSize = 18.sp,
color = MaterialTheme.colors.primaryTextColor,
modifier = Modifier.padding(top = 16.dp, bottom = 8.dp),
)
IconTextButton(
title = "Source code",
painter = painterResource("code.svg"),
onClick = {
Desktop.getDesktop().browse(URI("https://github.com/JetpackDuba/Gitnuro"))
}
)
IconTextButton(
title = "Report a bug",
painter = painterResource("bug.svg"),
onClick = {
Desktop.getDesktop().browse(URI("https://github.com/JetpackDuba/Gitnuro/issues"))
}
)
if(newUpdate != null) {
IconTextButton(
title = "New update ${newUpdate?.appVersion} available ",
painter = painterResource("grade.svg"),
iconColor = MaterialTheme.colors.secondary,
onClick = {
newUpdate?.downloadUrl?.let {
Desktop.getDesktop().browse(URI(it))
}
}
)
}
)
}
ButtonTile(
Column(
modifier = Modifier
.padding(bottom = 8.dp),
title = "Start a local repository",
painter = painterResource("open.svg"),
onClick = {
val dir = openDirectoryDialog()
if (dir != null)
tabViewModel.initLocalRepository(dir)
}
)
.padding(start = 32.dp),
) {
Text(
text = "Recent",
fontSize = 18.sp,
modifier = Modifier.padding(top = 48.dp, bottom = 8.dp),
color = MaterialTheme.colors.primaryTextColor,
)
LazyColumn {
items(items = appStateManager.latestOpenedRepositoriesPaths) { repo ->
val repoDirName = repo.dirName
val repoDirPath = repo.dirPath
Text(
text = "About Gitnuro",
fontSize = 18.sp,
color = MaterialTheme.colors.primaryTextColor,
modifier = Modifier
.padding(top = 16.dp, bottom = 8.dp),
)
IconTextButton(
title = "Source code",
painter = painterResource("code.svg"),
onClick = {
Desktop.getDesktop().browse(URI("https://github.com/JetpackDuba/Gitnuro"))
}
)
IconTextButton(
title = "Report a bug",
painter = painterResource("bug.svg"),
onClick = {
Desktop.getDesktop().browse(URI("https://github.com/JetpackDuba/Gitnuro/issues"))
}
)
}
Column(
modifier = Modifier
.padding(start = 32.dp),
) {
Text(
text = "Recent",
fontSize = 18.sp,
modifier = Modifier
.padding(top = 16.dp, bottom = 8.dp),
color = MaterialTheme.colors.primaryTextColor,
)
LazyColumn {
items(items = appStateManager.latestOpenedRepositoriesPaths) { repo ->
val repoDirName = repo.dirName
val repoDirPath = repo.dirPath
Row(
verticalAlignment = Alignment.CenterVertically,
) {
TextButton(
onClick = {
Row(
verticalAlignment = Alignment.CenterVertically,
) {
TextButton(
onClick = {
tabViewModel.openRepository(repo)
}
) {
) {
Text(
text = repoDirName,
fontSize = 14.sp,
color = MaterialTheme.colors.primary,
)
}
Text(
text = repoDirName,
text = repoDirPath,
fontSize = 14.sp,
color = MaterialTheme.colors.primary,
modifier = Modifier.padding(start = 4.dp),
color = MaterialTheme.colors.secondaryTextColor
)
}
Text(
text = repoDirPath,
fontSize = 14.sp,
modifier = Modifier.padding(start = 4.dp),
color = MaterialTheme.colors.secondaryTextColor
)
}
}
}
}
Text(
"Version ${AppConstants.APP_VERSION}",
color = MaterialTheme.colors.primaryTextColor,
fontSize = 12.sp,
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(bottom = 16.dp, end = 16.dp)
)
}
LaunchedEffect(showCloneView) {
if (showCloneView)
tabViewModel.cloneViewModel.reset() // Reset dialog before showing it
if (showCloneView) tabViewModel.cloneViewModel.reset() // Reset dialog before showing it
}
if (showCloneView)
CloneDialog(
tabViewModel.cloneViewModel,
onClose = {
showCloneView = false
tabViewModel.cloneViewModel.reset()
},
onOpenRepository = { dir ->
tabViewModel.openRepository(dir)
},
)
if (showCloneView) CloneDialog(
tabViewModel.cloneViewModel,
onClose = {
showCloneView = false
tabViewModel.cloneViewModel.reset()
},
onOpenRepository = { dir ->
tabViewModel.openRepository(dir)
},
)
}
@Composable
@ -217,6 +237,7 @@ fun IconTextButton(
modifier: Modifier = Modifier,
title: String,
painter: Painter,
iconColor: Color = MaterialTheme.colors.primary,
onClick: () -> Unit,
) {
TextButton(
@ -224,17 +245,14 @@ fun IconTextButton(
modifier = modifier.size(width = 280.dp, height = 40.dp)
) {
Row(
modifier = Modifier
.fillMaxSize(),
modifier = Modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically,
) {
Image(
modifier = Modifier
.padding(end = 8.dp)
.size(24.dp),
modifier = Modifier.padding(end = 8.dp).size(24.dp),
painter = painter,
contentDescription = null,
colorFilter = ColorFilter.tint(MaterialTheme.colors.primary),
colorFilter = ColorFilter.tint(iconColor),
)
Text(

View file

@ -0,0 +1,10 @@
package app.updates
import kotlinx.serialization.Serializable
@Serializable
data class Update(
val appVersion: String,
val appCode: Int,
val downloadUrl: String,
)

View file

@ -0,0 +1,23 @@
package app.updates
import app.AppConstants
import app.AppPreferences
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import javax.inject.Inject
private val updateJson = Json {
this.ignoreUnknownKeys = true
}
class UpdatesRepository @Inject constructor(
private val updatesWebService: UpdatesService,
) {
suspend fun latestRelease(): Update? = withContext(Dispatchers.IO) {
val latestReleaseJson = updatesWebService.release(AppConstants.VERSION_CHECK_URL)
updateJson.decodeFromString(latestReleaseJson)
}
}

View file

@ -0,0 +1,9 @@
package app.updates
import retrofit2.http.GET
import retrofit2.http.Url
interface UpdatesService {
@GET
suspend fun release(@Url url: String): String
}

View file

@ -1,5 +1,6 @@
package app.viewmodels
import app.AppPreferences
import app.AppStateManager
import app.ErrorsManager
import app.credentials.CredentialsState
@ -7,6 +8,8 @@ import app.credentials.CredentialsStateManager
import app.git.*
import app.newErrorNow
import app.ui.SelectedItem
import app.updates.Update
import app.updates.UpdatesRepository
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -39,6 +42,7 @@ class TabViewModel @Inject constructor(
private val tabState: TabState,
val appStateManager: AppStateManager,
private val fileChangesWatcher: FileChangesWatcher,
private val updatesRepository: UpdatesRepository,
) {
val errorsManager: ErrorsManager = tabState.errorsManager
val selectedItem: StateFlow<SelectedItem> = tabState.selectedItem
@ -262,6 +266,15 @@ class TabViewModel @Inject constructor(
repositoryManager.initLocalRepo(repoDir)
openRepository(repoDir)
}
suspend fun latestRelease(): Update? = withContext(Dispatchers.IO) {
try {
updatesRepository.latestRelease()
} catch (ex: Exception) {
ex.printStackTrace()
null
}
}
}

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 0 24 24" width="24px" fill="#000000"><path d="M0 0h24v24H0z" fill="none"/><path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/></svg>

After

Width:  |  Height:  |  Size: 245 B