Skip to content

Commit c317a32

Browse files
committed
Issue #96: Add sorting options for folders
1 parent ed72b2a commit c317a32

13 files changed

Lines changed: 128 additions & 24 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.amrdeveloper.linkhub.data
2+
3+
enum class FolderSortingOption {
4+
NAME_ASC,
5+
NAME_DESC,
6+
CLICK_COUNT_ASC,
7+
CLICK_COUNT_DESC,
8+
DEFAULT
9+
}

app/src/main/java/com/amrdeveloper/linkhub/data/source/FolderDataSource.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.amrdeveloper.linkhub.data.source
22

33
import androidx.paging.PagingSource
44
import com.amrdeveloper.linkhub.data.Folder
5+
import com.amrdeveloper.linkhub.data.FolderSortingOption
56
import kotlinx.coroutines.flow.Flow
67

78
interface FolderDataSource {
@@ -24,8 +25,9 @@ interface FolderDataSource {
2425
isClicked: Boolean? = null,
2526
isInsideFolder: Boolean? = null,
2627
folderId: Int? = -1,
28+
sortingOption: FolderSortingOption = FolderSortingOption.DEFAULT,
2729
limit: Int = -1
28-
) : Flow<List<Folder>>
30+
): Flow<List<Folder>>
2931

3032
suspend fun updateFolder(folder: Folder): Result<Int>
3133

app/src/main/java/com/amrdeveloper/linkhub/data/source/FolderRepository.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.amrdeveloper.linkhub.data.source
22

33
import androidx.paging.PagingSource
44
import com.amrdeveloper.linkhub.data.Folder
5+
import com.amrdeveloper.linkhub.data.FolderSortingOption
56
import kotlinx.coroutines.flow.Flow
67

78
class FolderRepository(private val dataSource: FolderDataSource) {
@@ -32,9 +33,18 @@ class FolderRepository(private val dataSource: FolderDataSource) {
3233
isClicked: Boolean? = null,
3334
isInsideFolder: Boolean? = null,
3435
folderId: Int? = -1,
36+
sortingOption: FolderSortingOption = FolderSortingOption.DEFAULT,
3537
limit: Int = -1
3638
): Flow<List<Folder>> =
37-
dataSource.getSortedFolders(keyword, isPinned, isClicked, isInsideFolder, folderId, limit)
39+
dataSource.getSortedFolders(
40+
keyword,
41+
isPinned,
42+
isClicked,
43+
isInsideFolder,
44+
folderId,
45+
sortingOption,
46+
limit
47+
)
3848

3949
suspend fun updateFolder(folder: Folder): Result<Int> {
4050
return dataSource.updateFolder(folder)

app/src/main/java/com/amrdeveloper/linkhub/data/source/local/FolderDao.kt

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import androidx.room.Dao
55
import androidx.room.Query
66
import androidx.room.Transaction
77
import com.amrdeveloper.linkhub.data.Folder
8+
import com.amrdeveloper.linkhub.data.FolderSortingOption
89
import kotlinx.coroutines.flow.Flow
910

1011
@Dao
@@ -19,30 +20,41 @@ interface FolderDao : BaseDao<Folder> {
1920
@Query("SELECT * FROM folder")
2021
suspend fun getFolders(): List<Folder>
2122

22-
@Query("""
23+
@Query(
24+
"""
2325
SELECT * FROM folder
2426
ORDER BY pinned DESC, click_count DESC
25-
""")
27+
"""
28+
)
2629
fun getMoseUsedFoldersWithPagination(): PagingSource<Int, Folder>
2730

28-
@Query("""
31+
@Query(
32+
"""
2933
SELECT * FROM folder
3034
WHERE ((:keyword IS NULL) OR (:keyword = '') OR (name LIKE '%' || :keyword || '%'))
3135
AND ((:isPinned IS NULL) OR (pinned = :isPinned))
3236
AND ((:isClicked IS NULL) OR (click_count > 0))
3337
AND ((:isInsideFolder IS NULL) OR (folder_id != -1))
3438
AND ((:folderId IS NULL) OR ((:folderId = -1)) OR (folder_id = :folderId))
35-
ORDER BY pinned DESC, click_count DESC
39+
ORDER BY
40+
CASE WHEN :sortingOption = 'NAME_ASC' THEN name END ASC,
41+
CASE WHEN :sortingOption = 'NAME_DESC' THEN name END DESC,
42+
CASE WHEN :sortingOption = 'CLICK_COUNT_ASC' THEN click_count END ASC,
43+
CASE WHEN :sortingOption = 'CLICK_COUNT_DESC' THEN click_count END DESC,
44+
CASE WHEN :sortingOption = 'DEFAULT' THEN pinned END DESC,
45+
CASE WHEN :sortingOption = 'DEFAULT' THEN click_count END DESC
3646
LIMIT :limit
37-
""")
47+
"""
48+
)
3849
fun getSortedFolders(
3950
keyword: String? = null,
4051
isPinned: Boolean? = null,
4152
isClicked: Boolean? = null,
4253
isInsideFolder: Boolean? = null,
4354
folderId: Int? = null,
55+
sortingOption: FolderSortingOption = FolderSortingOption.DEFAULT,
4456
limit: Int = -1
45-
) : Flow<List<Folder>>
57+
): Flow<List<Folder>>
4658

4759
@Query("UPDATE folder SET click_count = :count WHERE id = :folderId")
4860
suspend fun updateClickCountByFolderId(folderId: Int, count: Int): Int

app/src/main/java/com/amrdeveloper/linkhub/data/source/local/FolderLocalDataSource.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.amrdeveloper.linkhub.data.source.local
22

33
import androidx.paging.PagingSource
44
import com.amrdeveloper.linkhub.data.Folder
5+
import com.amrdeveloper.linkhub.data.FolderSortingOption
56
import com.amrdeveloper.linkhub.data.source.FolderDataSource
67
import kotlinx.coroutines.CoroutineDispatcher
78
import kotlinx.coroutines.Dispatchers
@@ -64,9 +65,18 @@ class FolderLocalDataSource internal constructor(
6465
isClicked: Boolean?,
6566
isInsideFolder: Boolean?,
6667
folderId: Int?,
68+
sortingOption: FolderSortingOption,
6769
limit: Int
6870
): Flow<List<Folder>> =
69-
folderDao.getSortedFolders(keyword, isPinned, isClicked, isInsideFolder, folderId, limit)
71+
folderDao.getSortedFolders(
72+
keyword,
73+
isPinned,
74+
isClicked,
75+
isInsideFolder,
76+
folderId,
77+
sortingOption,
78+
limit
79+
)
7080

7181
override suspend fun updateFolder(folder: Folder): Result<Int> = withContext(ioDispatcher) {
7282
return@withContext try {

app/src/main/java/com/amrdeveloper/linkhub/ui/components/ShowIOptionsMenu.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ data class ShowOption(
3232
fun ShowItemsOptionsDropdownButton(
3333
options: List<ShowOption>,
3434
selectedOptionIndex: Int = 0,
35-
onOptionSelected: (ShowOption) -> Unit = {},
35+
onOptionSelected: (Int) -> Unit = {},
3636
) {
3737
var isExpanded by remember { mutableStateOf(false) }
3838
var selectedIndex by remember { mutableStateOf(selectedOptionIndex) }
@@ -67,7 +67,7 @@ fun ShowItemsOptionsDropdownButton(
6767
}
6868
},
6969
onClick = {
70-
onOptionSelected(option)
70+
onOptionSelected(index)
7171
selectedIndex = index
7272
isExpanded = false
7373
},

app/src/main/java/com/amrdeveloper/linkhub/ui/folders/FoldersScreen.kt

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import androidx.compose.material3.MaterialTheme
1111
import androidx.compose.material3.Scaffold
1212
import androidx.compose.material3.Text
1313
import androidx.compose.runtime.Composable
14+
import androidx.compose.runtime.LaunchedEffect
1415
import androidx.compose.runtime.getValue
1516
import androidx.compose.runtime.mutableStateOf
1617
import androidx.compose.runtime.remember
@@ -24,13 +25,27 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
2425
import androidx.lifecycle.viewmodel.compose.viewModel
2526
import androidx.navigation.NavController
2627
import com.amrdeveloper.linkhub.R
28+
import com.amrdeveloper.linkhub.data.FolderSortingOption
2729
import com.amrdeveloper.linkhub.ui.components.FolderList
2830
import com.amrdeveloper.linkhub.ui.components.FolderViewKind
2931
import com.amrdeveloper.linkhub.ui.components.LinkhubToolbar
3032
import com.amrdeveloper.linkhub.ui.components.ShowItemsOptionsDropdownButton
3133
import com.amrdeveloper.linkhub.ui.components.ShowOption
3234
import com.amrdeveloper.linkhub.util.UiPreferences
3335

36+
private val showOptions = listOf(
37+
ShowOption(FolderViewKind.List.name, R.drawable.ic_list),
38+
ShowOption(FolderViewKind.Grid.name, R.drawable.ic_grid)
39+
)
40+
41+
private val sortingOptions = listOf(
42+
ShowOption("Default", R.drawable.ic_sorting),
43+
ShowOption("Name A-Z", R.drawable.ic_sort_az),
44+
ShowOption("Name Z-A", R.drawable.ic_sort_za),
45+
ShowOption("Clicks Asc", R.drawable.ic_sort_12),
46+
ShowOption("Clicks Desc", R.drawable.ic_sort_21),
47+
)
48+
3449
@Composable
3550
fun FoldersScreen(
3651
viewModel: FolderListViewModel = viewModel(),
@@ -39,6 +54,12 @@ fun FoldersScreen(
3954
) {
4055
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
4156
var showItemsOption by remember { mutableStateOf(FolderViewKind.List) }
57+
var sortItemsOption by remember { mutableStateOf(FolderSortingOption.DEFAULT) }
58+
59+
LaunchedEffect(sortItemsOption) {
60+
viewModel.setSortingOption(sortItemsOption)
61+
}
62+
4263
Scaffold(topBar = { LinkhubToolbar(viewModel(), uiPreferences, navController) }) { padding ->
4364
Column(
4465
modifier = Modifier
@@ -56,13 +77,19 @@ fun FoldersScreen(
5677
.weight(1f)
5778
)
5879

59-
val showOptions = listOf(
60-
ShowOption(FolderViewKind.List.name, R.drawable.ic_list),
61-
ShowOption(FolderViewKind.Grid.name, R.drawable.ic_grid)
62-
)
80+
ShowItemsOptionsDropdownButton(sortingOptions) { index ->
81+
sortItemsOption = when (index) {
82+
0 -> FolderSortingOption.DEFAULT
83+
1 -> FolderSortingOption.NAME_ASC
84+
2 -> FolderSortingOption.NAME_DESC
85+
3 -> FolderSortingOption.CLICK_COUNT_ASC
86+
4 -> FolderSortingOption.CLICK_COUNT_DESC
87+
else -> FolderSortingOption.DEFAULT
88+
}
89+
}
6390

64-
ShowItemsOptionsDropdownButton(showOptions) { showOption ->
65-
showItemsOption = FolderViewKind.valueOf(showOption.literal)
91+
ShowItemsOptionsDropdownButton(showOptions) { index ->
92+
showItemsOption = FolderViewKind.valueOf(showOptions[index].literal)
6693
}
6794
}
6895

app/src/main/java/com/amrdeveloper/linkhub/ui/folders/FoldersViewModel.kt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
44
import androidx.lifecycle.viewModelScope
55
import com.amrdeveloper.linkhub.common.LazyValue
66
import com.amrdeveloper.linkhub.data.Folder
7+
import com.amrdeveloper.linkhub.data.FolderSortingOption
78
import com.amrdeveloper.linkhub.data.source.FolderRepository
89
import dagger.hilt.android.lifecycle.HiltViewModel
910
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -22,22 +23,22 @@ class FolderListViewModel @Inject constructor(
2223
private val folderRepository: FolderRepository
2324
) : ViewModel() {
2425

25-
private val searchQuery = MutableStateFlow(value = "")
26+
private val sortingOption = MutableStateFlow(value = FolderSortingOption.DEFAULT)
2627

2728
@OptIn(ExperimentalCoroutinesApi::class)
2829
val uiState: StateFlow<LazyValue<List<Folder>>> =
29-
combine(searchQuery) {
30-
searchQuery.value
31-
}.flatMapLatest { query ->
32-
folderRepository.getSortedFolders(keyword = query)
30+
combine(sortingOption) {
31+
sortingOption.value
32+
}.flatMapLatest { option ->
33+
folderRepository.getSortedFolders(sortingOption = option)
3334
}.map { LazyValue(data = it, isLoading = false) }.stateIn(
3435
scope = viewModelScope,
3536
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5000L),
3637
initialValue = LazyValue(data = listOf(), isLoading = true)
3738
)
3839

39-
fun updateSearchQuery(query: String) {
40-
searchQuery.value = query
40+
fun setSortingOption(option: FolderSortingOption) {
41+
sortingOption.value = option
4142
}
4243

4344
fun incrementFolderClickCount(folder: Folder) {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="24dp">
2+
3+
<path android:fillColor="#546E7A" android:pathData="M810.7,704V106.7h-85.3v597.3h-128l170.7,213.3 170.7,-213.3z"/>
4+
5+
<path android:fillColor="#2196F3" android:pathData="M349.9,426.7h-64V183.5L211.2,206.9V155.7L341.3,108.8h6.4V426.7zM413.9,917.3H196.3v-42.7l102.4,-108.8c8.5,-8.5 14.9,-17.1 19.2,-23.5 4.3,-6.4 10.7,-12.8 12.8,-19.2 4.3,-6.4 6.4,-10.7 6.4,-17.1 2.1,-4.3 2.1,-10.7 2.1,-14.9 0,-14.9 -4.3,-25.6 -10.7,-34.1 -6.4,-8.5 -17.1,-12.8 -29.9,-12.8 -6.4,0 -14.9,2.1 -19.2,4.3 -6.4,2.1 -10.7,6.4 -14.9,10.7 -4.3,4.3 -6.4,10.7 -8.5,17.1s-2.1,12.8 -2.1,21.3h-64c0,-14.9 2.1,-27.7 8.5,-40.5 4.3,-12.8 12.8,-23.5 21.3,-34.1 10.7,-8.5 21.3,-17.1 34.1,-23.5 12.8,-6.4 29.9,-8.5 46.9,-8.5 17.1,0 32,2.1 44.8,6.4 12.8,4.3 23.5,10.7 32,17.1s14.9,17.1 19.2,27.7 6.4,23.5 6.4,38.4c0,10.7 -2.1,21.3 -4.3,29.9s-8.5,25.6 -14.9,36.3 -12.8,19.2 -21.3,29.9c-8.5,10.7 -19.2,21.3 -29.9,32L277.3,866.1h136.5V917.3z"/>
6+
7+
</vector>
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:viewportHeight="1024" android:viewportWidth="1024" android:width="24dp">
2+
3+
<path android:fillColor="#546E7A" android:pathData="M810.7,704V106.7h-85.3v597.3h-128l170.7,213.3 170.7,-213.3z"/>
4+
5+
<path android:fillColor="#2196F3" android:pathData="M409.6,426.7H192v-42.7l102.4,-108.8c8.5,-8.5 14.9,-17.1 19.2,-23.5 4.3,-6.4 10.7,-12.8 12.8,-19.2 4.3,-6.4 6.4,-10.7 6.4,-17.1 2.1,-4.3 2.1,-10.7 2.1,-14.9 0,-14.9 -4.3,-25.6 -10.7,-34.1 -6.4,-8.5 -17.1,-12.8 -29.9,-12.8 -6.4,0 -14.9,2.1 -19.2,4.3 -6.4,2.1 -10.7,6.4 -14.9,10.7 -4.3,4.3 -6.4,10.7 -8.5,17.1s-2.1,12.8 -2.1,21.3h-64c0,-14.9 2.1,-27.7 8.5,-40.5 4.3,-12.8 12.8,-23.5 21.3,-34.1 10.7,-8.5 21.3,-17.1 34.1,-23.5 12.8,-6.4 29.9,-8.5 46.9,-8.5 17.1,0 32,2.1 44.8,6.4 12.8,4.3 23.5,10.7 32,17.1s14.9,17.1 19.2,27.7c4.3,10.7 6.4,23.5 6.4,38.4 0,10.7 -2.1,21.3 -4.3,29.9s-8.5,19.2 -14.9,29.9 -12.8,19.2 -21.3,29.9c-8.5,10.7 -19.2,21.3 -29.9,32l-55.5,59.7h136.5V426.7zM345.6,917.3h-64V674.1l-74.7,23.5v-51.2l132.3,-46.9h6.4V917.3z"/>
6+
7+
</vector>

0 commit comments

Comments
 (0)