Skip to content

Commit 8f2b5ab

Browse files
committed
refactor: Enhance UX, build process, and progress reporting
This commit introduces several improvements focused on user experience, build configuration, and the clarity of the download progress dialog. - **UX & UI Polish:** - Adds a hand-cursor (`PointerIcon`) on hover for all interactive elements, including buttons, checkboxes, icon buttons, and repository cards, improving discoverability. - Changes the default download directory from `~/Git Backup Hub` to `~/Git-Backup-Hub` for better compatibility with command-line tools. - **Progress Dialog Refinement:** - Replaces the overall `LinearProgressIndicator` with a progress bar that tracks the *current* repository being cloned, providing more immediate feedback. - Removes the total percentage from the text display, as the circular progress indicator already conveys this information. - The Estimated Time Remaining (ETA) is now only shown when a calculation is available and is left-aligned for better readability. - **Build & CI:** - Adds `packagePkg` to the macOS build matrix and `jar` to the Linux build matrix in the GitHub Actions workflow (`build-desktop.yml`). - Updates the release step to include `.pkg` and `.jar` artifacts. - Removes the `.rpm` target from `build.gradle.kts` to streamline Linux package generation, focusing on `.deb`. - **Minor Code Cleanup:** - Removes an unnecessary section header ("Social Links") in the `AboutDialog`. - Standardizes import statements in `ProgressDialog.kt`.
1 parent dbb9aee commit 8f2b5ab

File tree

10 files changed

+159
-82
lines changed

10 files changed

+159
-82
lines changed

.github/workflows/build-desktop.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,18 @@ jobs:
1515
- os: macos-latest
1616
format: dmg
1717
gradle_task: packageDmg
18+
- os: macos-latest
19+
format: pkg
20+
gradle_task: packagePkg
1821
- os: windows-latest
1922
format: msi
2023
gradle_task: packageMsi
2124
- os: ubuntu-latest
2225
format: deb
2326
gradle_task: packageDeb
27+
- os: ubuntu-latest
28+
format: jar
29+
gradle_task: jar
2430

2531
steps:
2632
- name: Checkout code
@@ -40,13 +46,15 @@ jobs:
4046
run: ./gradlew ${{ matrix.gradle_task }} --stacktrace
4147

4248
- name: Check build output
43-
run: ls -R composeApp/build/compose/binaries || true
49+
run: ls -R composeApp/build || true
4450

4551
- name: Upload artifact
4652
uses: actions/upload-artifact@v4
4753
with:
4854
name: build-${{ matrix.format }}
49-
path: composeApp/build/compose/binaries/main/${{ matrix.format }}/
55+
path: |
56+
composeApp/build/compose/binaries/**/${{ matrix.format }}/
57+
composeApp/build/compose/jars/*.jar
5058
5159
release:
5260
name: Create GitHub Release
@@ -66,10 +74,13 @@ jobs:
6674
uses: softprops/action-gh-release@v2
6775
with:
6876
files: |
77+
artifacts/build-app/*.app
6978
artifacts/build-dmg/*.dmg
79+
artifacts/build-pkg/*.pkg
7080
artifacts/build-msi/*.msi
7181
artifacts/build-rpm/*.rpm
7282
artifacts/build-deb/*.deb
83+
artifacts/build-jar/*.jar
7384
draft: true
7485
tag_name: ${{ github.ref_name }}
7586
env:

composeApp/build.gradle.kts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,16 +78,13 @@ compose {
7878
application {
7979
mainClass = "com.meet.gitbackup.hub.MainKt"
8080
// javaHome = System.getenv("JAVA_HOME")
81-
// javaHome = "/Users/meet/Library/Java/JavaVirtualMachines/ms-17.0.15/Contents/Home"
81+
javaHome = "/Users/meet/Library/Java/JavaVirtualMachines/ms-17.0.15/Contents/Home"
8282

8383
nativeDistributions {
8484
targetFormats(
85-
TargetFormat.Dmg,
86-
TargetFormat.Pkg,
87-
TargetFormat.Msi,
88-
TargetFormat.Exe,
89-
TargetFormat.Deb,
90-
TargetFormat.Rpm,
85+
TargetFormat.Dmg, TargetFormat.Pkg, // macOS
86+
TargetFormat.Msi, TargetFormat.Exe, // Windows
87+
TargetFormat.Deb // Linux
9188
)
9289
packageName = "Git Backup Hub"
9390
packageVersion = "1.0.0"

composeApp/src/jvmMain/kotlin/com/meet/gitbackup/hub/presentation/components/CustomButton.kt

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ import androidx.compose.runtime.Composable
1414
import androidx.compose.ui.Modifier
1515
import androidx.compose.ui.graphics.StrokeCap
1616
import androidx.compose.ui.graphics.vector.ImageVector
17+
import androidx.compose.ui.input.pointer.PointerIcon
18+
import androidx.compose.ui.input.pointer.pointerHoverIcon
1719
import androidx.compose.ui.text.font.FontWeight
1820
import androidx.compose.ui.unit.dp
1921
import com.meet.gitbackup.hub.*
2022
import org.jetbrains.compose.resources.stringResource
23+
import java.awt.Cursor
2124

2225

2326
@Composable
@@ -33,7 +36,13 @@ fun CustomFilledButton(
3336
Button(
3437
onClick = onClick,
3538
enabled = enabled,
36-
modifier = modifier,
39+
modifier = modifier.pointerHoverIcon(
40+
PointerIcon(
41+
Cursor.getPredefinedCursor(
42+
Cursor.HAND_CURSOR
43+
)
44+
)
45+
),
3746
shape = RoundedCornerShape(12.dp)
3847
) {
3948
if (isLoading) {
@@ -71,7 +80,13 @@ fun CustomOutlinedButton(
7180
OutlinedButton(
7281
onClick = onClick,
7382
enabled = enabled,
74-
modifier = modifier,
83+
modifier = modifier.pointerHoverIcon(
84+
PointerIcon(
85+
Cursor.getPredefinedCursor(
86+
Cursor.HAND_CURSOR
87+
)
88+
)
89+
),
7590
shape = RoundedCornerShape(12.dp)
7691
) {
7792
if (isLoading) {

composeApp/src/jvmMain/kotlin/com/meet/gitbackup/hub/presentation/screen/about/AboutDialog.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ fun AboutDialog(
377377

378378
HorizontalDivider()
379379

380-
// Author & Social Links
380+
// Author
381381
Column(
382382
modifier = Modifier.fillMaxWidth(),
383383
horizontalAlignment = Alignment.CenterHorizontally,

composeApp/src/jvmMain/kotlin/com/meet/gitbackup/hub/presentation/screen/repo/RepoScreen.kt

Lines changed: 47 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ import androidx.compose.runtime.saveable.rememberSaveable
3838
import androidx.compose.runtime.setValue
3939
import androidx.compose.ui.Alignment
4040
import androidx.compose.ui.Modifier
41+
import androidx.compose.ui.input.pointer.PointerIcon
42+
import androidx.compose.ui.input.pointer.pointerHoverIcon
4143
import androidx.compose.ui.text.font.FontWeight
4244
import androidx.compose.ui.unit.dp
4345
import com.meet.gitbackup.hub.Res
@@ -55,11 +57,11 @@ import com.meet.gitbackup.hub.selected_count
5557
import com.meet.gitbackup.hub.toggle_theme
5658
import org.jetbrains.compose.resources.stringResource
5759
import org.koin.compose.viewmodel.koinViewModel
60+
import java.awt.Cursor
5861

5962
@Composable
6063
fun RepoScreen(
61-
isDarkTheme: Boolean,
62-
onThemeChange: (Boolean) -> Unit
64+
isDarkTheme: Boolean, onThemeChange: (Boolean) -> Unit
6365
) {
6466
val viewModel = koinViewModel<RepoViewModel>()
6567
val state by viewModel.state.collectAsState()
@@ -71,33 +73,41 @@ fun RepoScreen(
7173
onDismiss = { showAboutDialog = false },
7274
)
7375
}
74-
Scaffold(
75-
topBar = {
76-
TopAppBar(
77-
title = stringResource(Res.string.app_name),
78-
icon = Icons.Default.CloudDownload,
79-
actions = {
80-
IconButton(onClick = { onThemeChange(!isDarkTheme) }) {
81-
Icon(
82-
if (isDarkTheme) Icons.Default.LightMode else Icons.Default.DarkMode,
83-
contentDescription = stringResource(Res.string.toggle_theme)
76+
Scaffold(topBar = {
77+
TopAppBar(
78+
title = stringResource(Res.string.app_name),
79+
icon = Icons.Default.CloudDownload,
80+
actions = {
81+
IconButton(
82+
modifier = Modifier.pointerHoverIcon(
83+
PointerIcon(
84+
Cursor.getPredefinedCursor(
85+
Cursor.HAND_CURSOR
86+
)
8487
)
85-
}
86-
IconButton(onClick = { showAboutDialog = true }) {
87-
Icon(Icons.Default.Info, "About")
88-
}
88+
), onClick = { onThemeChange(!isDarkTheme) }) {
89+
Icon(
90+
if (isDarkTheme) Icons.Default.LightMode else Icons.Default.DarkMode,
91+
contentDescription = stringResource(Res.string.toggle_theme)
92+
)
8993
}
90-
)
94+
IconButton(
95+
modifier = Modifier.pointerHoverIcon(
96+
PointerIcon(
97+
Cursor.getPredefinedCursor(
98+
Cursor.HAND_CURSOR
99+
)
100+
)
101+
), onClick = { showAboutDialog = true }) {
102+
Icon(Icons.Default.Info, "About")
103+
}
104+
})
91105

92-
},
93-
bottomBar = {
94-
BottomBar(state = state, viewModel = viewModel)
95-
}
96-
) { scaffoldPadding ->
106+
}, bottomBar = {
107+
BottomBar(state = state, viewModel = viewModel)
108+
}) { scaffoldPadding ->
97109
BoxWithConstraints(
98-
modifier = Modifier
99-
.fillMaxSize()
100-
.padding(scaffoldPadding)
110+
modifier = Modifier.fillMaxSize().padding(scaffoldPadding)
101111
) {
102112
LazyColumn(
103113
state = listState,
@@ -135,12 +145,18 @@ fun RepoScreen(
135145
Res.string.selected_count,
136146
state.repos.count { it.isSelected },
137147
state.repos.size
138-
),
139-
fontWeight = FontWeight.Medium
148+
), fontWeight = FontWeight.Medium
140149
)
141150
Row(verticalAlignment = Alignment.CenterVertically) {
142151
Checkbox(
143152
checked = state.selectAll,
153+
modifier = Modifier.pointerHoverIcon(
154+
PointerIcon(
155+
Cursor.getPredefinedCursor(
156+
Cursor.HAND_CURSOR
157+
)
158+
)
159+
),
144160
onCheckedChange = { viewModel.handleIntent(RepoIntent.ToggleSelectAll) },
145161
enabled = !state.isLoading && !state.isDownloading
146162
)
@@ -152,13 +168,11 @@ fun RepoScreen(
152168
// --- Repo items ---
153169
items(state.filteredRepos, key = { it.repo.name }) { repoSelection ->
154170
RepoCard(
155-
repoSelection = repoSelection,
156-
onSelect = {
171+
repoSelection = repoSelection, onSelect = {
157172
viewModel.handleIntent(
158173
RepoIntent.ToggleRepoSelection(repoSelection.repo.name)
159174
)
160-
},
161-
enabled = !state.isLoading && !state.isDownloading
175+
}, enabled = !state.isLoading && !state.isDownloading
162176
)
163177
}
164178
}
@@ -168,10 +182,7 @@ fun RepoScreen(
168182

169183
// Vertical scrollbar
170184
VerticalScrollbar(
171-
modifier = Modifier
172-
.align(Alignment.CenterEnd)
173-
.fillMaxHeight()
174-
.padding(end = 2.dp),
185+
modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight().padding(end = 2.dp),
175186
adapter = rememberScrollbarAdapter(listState),
176187
style = LocalScrollbarStyle.current.copy(
177188
hoverColor = MaterialTheme.colorScheme.outline,
@@ -185,8 +196,7 @@ fun RepoScreen(
185196
ProgressDialog(
186197
progress = progress,
187198
onCancel = { viewModel.handleIntent(RepoIntent.CancelDownload) },
188-
onClose = { viewModel.handleIntent(RepoIntent.CloseDownloadDialog) }
189-
)
199+
onClose = { viewModel.handleIntent(RepoIntent.CloseDownloadDialog) })
190200
}
191201
state.errorMessage?.let { message ->
192202
ErrorDialog(message = message) {

composeApp/src/jvmMain/kotlin/com/meet/gitbackup/hub/presentation/screen/repo/RepoState.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package com.meet.gitbackup.hub.presentation.screen.repo
22

33
import com.meet.gitbackup.hub.data.models.GitHubRepoItem
4+
import java.io.File
45

56
data class RepoState(
67
val username: String = "",
78
val accessToken: String = "",
8-
val downloadPath: String = System.getProperty("user.home") + "/Git Backup Hub",
9+
val downloadPath :String = File(System.getProperty("user.home"), "Git-Backup-Hub").absolutePath,
910
val repos: List<RepoSelection> = emptyList(),
1011
val filteredRepos: List<RepoSelection> = emptyList(),
1112
val searchQuery: String = "",

composeApp/src/jvmMain/kotlin/com/meet/gitbackup/hub/presentation/screen/repo/components/BottomBar.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import androidx.compose.material3.Text
1919
import androidx.compose.runtime.Composable
2020
import androidx.compose.ui.Alignment
2121
import androidx.compose.ui.Modifier
22+
import androidx.compose.ui.input.pointer.PointerIcon
23+
import androidx.compose.ui.input.pointer.pointerHoverIcon
2224
import androidx.compose.ui.unit.dp
2325
import com.meet.gitbackup.hub.Res
2426
import com.meet.gitbackup.hub.browse
@@ -31,6 +33,7 @@ import com.meet.gitbackup.hub.presentation.screen.repo.RepoViewModel
3133
import io.github.vinceglb.filekit.PlatformFile
3234
import io.github.vinceglb.filekit.dialogs.compose.rememberDirectoryPickerLauncher
3335
import org.jetbrains.compose.resources.stringResource
36+
import java.awt.Cursor
3437

3538
@Composable
3639
fun BottomBar(state: RepoState, viewModel: RepoViewModel) {
@@ -69,6 +72,13 @@ fun BottomBar(state: RepoState, viewModel: RepoViewModel) {
6972

7073
// Browse button
7174
IconButton(
75+
modifier = Modifier.pointerHoverIcon(
76+
PointerIcon(
77+
Cursor.getPredefinedCursor(
78+
Cursor.HAND_CURSOR
79+
)
80+
)
81+
),
7282
onClick = { directoryPickerLauncher.launch() },
7383
enabled = !state.isLoading && !state.isDownloading
7484
) {

composeApp/src/jvmMain/kotlin/com/meet/gitbackup/hub/presentation/screen/repo/components/GithubSelectionLayout.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import androidx.compose.runtime.saveable.rememberSaveable
2929
import androidx.compose.runtime.setValue
3030
import androidx.compose.ui.Alignment
3131
import androidx.compose.ui.Modifier
32+
import androidx.compose.ui.input.pointer.PointerIcon
33+
import androidx.compose.ui.input.pointer.pointerHoverIcon
3234
import androidx.compose.ui.text.font.FontWeight
3335
import androidx.compose.ui.text.input.PasswordVisualTransformation
3436
import androidx.compose.ui.text.input.VisualTransformation
@@ -39,6 +41,7 @@ import com.meet.gitbackup.hub.presentation.screen.repo.RepoState
3941
import com.meet.gitbackup.hub.presentation.screen.repo.RepoViewModel
4042
import org.jetbrains.compose.resources.stringResource
4143
import com.meet.gitbackup.hub.*
44+
import java.awt.Cursor
4245

4346
@Composable
4447
fun GithubSelectionLayout(
@@ -97,6 +100,13 @@ fun GithubSelectionLayout(
97100
horizontalArrangement = Arrangement.Start
98101
) {
99102
Checkbox(
103+
modifier = Modifier.pointerHoverIcon(
104+
PointerIcon(
105+
Cursor.getPredefinedCursor(
106+
Cursor.HAND_CURSOR
107+
)
108+
)
109+
),
100110
checked = state.showTokenInput,
101111
onCheckedChange = { viewModel.handleIntent(RepoIntent.ToggleTokenInput) }
102112
)
@@ -115,7 +125,15 @@ fun GithubSelectionLayout(
115125
label = { Text(stringResource(Res.string.access_token)) },
116126
leadingIcon = { Icon(Icons.Default.Key, contentDescription = null) },
117127
trailingIcon = {
118-
IconButton(onClick = { isTokenVisible = !isTokenVisible }) {
128+
IconButton(
129+
modifier = Modifier.pointerHoverIcon(
130+
PointerIcon(
131+
Cursor.getPredefinedCursor(
132+
Cursor.HAND_CURSOR
133+
)
134+
)
135+
),
136+
onClick = { isTokenVisible = !isTokenVisible }) {
119137
Icon(
120138
if (isTokenVisible) Icons.Default.Visibility else Icons.Default.VisibilityOff,
121139
contentDescription = null

0 commit comments

Comments
 (0)