Skip to content

Commit

Permalink
feat(maps): multi-selection
Browse files Browse the repository at this point in the history
  • Loading branch information
aliernfrog authored Jan 25, 2024
1 parent f01f307 commit 64a910e
Show file tree
Hide file tree
Showing 36 changed files with 1,093 additions and 542 deletions.
2 changes: 2 additions & 0 deletions app/src/main/java/com/aliernfrog/lactool/Constant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import android.os.Environment
import com.aliernfrog.lactool.data.PrefEditItem
import com.aliernfrog.lactool.data.Social

const val TAG = "LACToolLogs"

const val experimentalSettingsRequiredClicks = 10
const val githubRepoURL = "https://github.com/aliernfrog/lac-tool"

Expand Down
19 changes: 0 additions & 19 deletions app/src/main/java/com/aliernfrog/lactool/data/LACMap.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.aliernfrog.lactool.data

import com.aliernfrog.lactool.R

data class MapActionResult(
val successful: Boolean,
val messageId: Int? = if (successful) null else R.string.warning_error,
val newFile: Any? = null
)
233 changes: 233 additions & 0 deletions app/src/main/java/com/aliernfrog/lactool/enum/MapAction.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
package com.aliernfrog.lactool.enum

import android.content.Context
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.AddLocationAlt
import androidx.compose.material.icons.rounded.Delete
import androidx.compose.material.icons.rounded.Download
import androidx.compose.material.icons.rounded.Edit
import androidx.compose.material.icons.rounded.EditLocationAlt
import androidx.compose.material.icons.rounded.Share
import androidx.compose.material.icons.rounded.Upload
import androidx.compose.ui.graphics.vector.ImageVector
import com.aliernfrog.lactool.R
import com.aliernfrog.lactool.data.MapActionResult
import com.aliernfrog.lactool.impl.MapFile
import com.aliernfrog.lactool.util.Destination
import com.aliernfrog.lactool.util.extension.showErrorToast
import com.aliernfrog.lactool.util.staticutil.FileUtil

/**
* Contains map actions.
*/
@Suppress("unused")
enum class MapAction(
/**
* Resource ID to use as a short label. This can be used for one or multiple maps.
*/
val shortLabelId: Int,

/**
* Resource ID to use as a long label. This should be used for only one map.
*/
val longLabelId: Int = shortLabelId,

/**
* Resource ID to use as a description. This should be used for only one map.
*/
val descriptionId: Int? = null,

/**
* Icon of the action.
*/
val icon: ImageVector,

/**
* Whether the action is available for multi-selection.
*/
val availableForMultiSelection: Boolean = true,

/**
* Whether the action is destructive.
*/
val destructive: Boolean = false,

/**
* Whether the action is available for [map].
*/
val availableFor: (map: MapFile) -> Boolean
) {
RENAME(
shortLabelId = R.string.maps_rename,
icon = Icons.Rounded.Edit,
availableForMultiSelection = false,
availableFor = {
it.importedState != MapImportedState.NONE && it.resolveMapNameInput() != it.name
}
) {
override suspend fun execute(context: Context, vararg maps: MapFile) {
val first = maps.first()
val newName = first.resolveMapNameInput()
first.runInIOThreadSafe {
val result = first.rename(newName = newName)
if (!result.successful) return@runInIOThreadSafe first.topToastState.showErrorToast(
text = result.messageId ?: R.string.warning_error
)
result.newFile?.let {
first.mapsViewModel.chooseMap(it)
}
first.topToastState.showToast(
text = context.getString(result.messageId ?: R.string.maps_renamed)
.replace("{NAME}", newName),
icon = Icons.Rounded.Edit
)
}
}
},

IMPORT(
shortLabelId = R.string.maps_import_short,
longLabelId = R.string.maps_import,
icon = Icons.Rounded.Download,
availableFor = {
it.importedState != MapImportedState.IMPORTED
}
) {
override suspend fun execute(context: Context, vararg maps: MapFile) {
runIOAction(
*maps,
singleSuccessMessageId = R.string.maps_imported_single,
multipleSuccessMessageId = R.string.maps_imported_multiple,
successIcon = icon,
result = { it.import(context) },
context = context
)
}
},

EXPORT(
shortLabelId = R.string.maps_export_short,
longLabelId = R.string.maps_export,
icon = Icons.Rounded.Upload,
availableFor = {
it.importedState == MapImportedState.IMPORTED
}
) {
override suspend fun execute(context: Context, vararg maps: MapFile) {
runIOAction(
*maps,
singleSuccessMessageId = R.string.maps_exported_single,
multipleSuccessMessageId = R.string.maps_exported_multiple,
successIcon = icon,
result = { it.export(context) },
context = context
)
}
},

SHARE(
shortLabelId = R.string.maps_share_short,
longLabelId = R.string.maps_share,
icon = Icons.Rounded.Share,
availableFor = {
true
}
) {
override suspend fun execute(context: Context, vararg maps: MapFile) {
val files = maps.map { it.file }
maps.first().runInIOThreadSafe {
FileUtil.shareFiles(*files.toTypedArray(), context = context)
}
}
},

EDIT(
shortLabelId = R.string.maps_edit,
descriptionId = R.string.maps_edit_description,
icon = Icons.Rounded.EditLocationAlt,
availableForMultiSelection = false,
availableFor = {
true
}
) {
override suspend fun execute(context: Context, vararg maps: MapFile) {
maps.first().let {
it.mapsEditViewModel.openMap(it.file, context)
}
}
},

MERGE(
shortLabelId = R.string.maps_merge_short,
longLabelId = R.string.maps_merge,
icon = Icons.Rounded.AddLocationAlt,
availableFor = {
true
}
) {
override suspend fun execute(context: Context, vararg maps: MapFile) {
val mapsMergeViewModel = maps.first().mapsMergeViewModel
mapsMergeViewModel.addMaps(context, *maps)
mapsMergeViewModel.navController.navigate(Destination.MAPS_MERGE.route)
}
},

DELETE(
shortLabelId = R.string.maps_delete_short,
longLabelId = R.string.maps_delete,
icon = Icons.Rounded.Delete,
destructive = true,
availableFor = {
it.importedState != MapImportedState.NONE
}
) {
override suspend fun execute(context: Context, vararg maps: MapFile) {
maps.first().mapsViewModel.mapsPendingDelete = maps.toList()
}
};

/**
* Executes the action for [maps].
*/
abstract suspend fun execute(context: Context, vararg maps: MapFile)
}

private suspend fun runIOAction(
vararg maps: MapFile,
singleSuccessMessageId: Int,
multipleSuccessMessageId: Int,
successIcon: ImageVector,
result: (MapFile) -> MapActionResult,
context: Context
) {
val first = maps.first()
val total = maps.size
val isSingle = total == 1

first.runInIOThreadSafe {
val results = maps.map {
val executionResult = result(it)
it.resolveMapNameInput() to executionResult
}
if (isSingle) results.first().let { (mapName, result) ->
if (result.successful) first.topToastState.showToast(
text = context.getString(singleSuccessMessageId).replace("{NAME}", mapName),
icon = successIcon
) else first.topToastState.showErrorToast(
text = context.getString(result.messageId ?: R.string.warning_error)
)
result.newFile?.let { first.mapsViewModel.chooseMap(it) }
} else {
val successes = results.filter { it.second.successful }
val fails = results.filter { !it.second.successful }
if (fails.isEmpty()) first.topToastState.showToast(
text = context.getString(multipleSuccessMessageId).replace("{COUNT}", successes.size.toString()),
icon = successIcon
) else first.mapsViewModel.showActionFailedDialog(
successes = successes,
fails = fails
)
}
}
first.mapsViewModel.loadMaps(context)
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.aliernfrog.lactool.enum

import com.aliernfrog.lactool.R
import com.aliernfrog.lactool.data.LACMap
import com.aliernfrog.lactool.impl.MapFile
import com.aliernfrog.lactool.ui.viewmodel.MapsViewModel

enum class MapsListSegment(
val labelId: Int,
val noMapsTextId: Int,
val getMaps: (MapsViewModel) -> List<LACMap>
val getMaps: (MapsViewModel) -> List<MapFile>
) {
IMPORTED(
labelId = R.string.mapsList_imported,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,29 @@ import androidx.compose.material.icons.filled.CalendarMonth
import androidx.compose.material.icons.filled.SortByAlpha
import androidx.compose.ui.graphics.vector.ImageVector
import com.aliernfrog.lactool.R
import com.aliernfrog.lactool.data.LACMap
import com.aliernfrog.lactool.impl.MapFile

@Suppress("unused")
enum class MapsListSortingType(
val labelId: Int,
val iconVector: ImageVector,
val comparator: Comparator<LACMap>
val comparator: Comparator<MapFile>
) {
ALPHABETICAL(
labelId = R.string.mapsList_sorting_name,
iconVector = Icons.Default.SortByAlpha,
comparator = compareBy(LACMap::name)
comparator = compareBy(MapFile::name)
),

DATE(
labelId = R.string.mapsList_sorting_date,
iconVector = Icons.Default.CalendarMonth,
comparator = compareByDescending(LACMap::lastModified)
comparator = compareByDescending(MapFile::lastModified)
),

SIZE(
labelId = R.string.mapsList_sorting_size,
iconVector = Icons.AutoMirrored.Filled.Note,
comparator = compareByDescending(LACMap::fileSize)
comparator = compareByDescending(MapFile::size)
)
}
Loading

0 comments on commit 64a910e

Please sign in to comment.