diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/AppFilesManager.kt b/src/main/kotlin/com/jetpackduba/gitnuro/AppFilesManager.kt new file mode 100644 index 0000000..9a518e3 --- /dev/null +++ b/src/main/kotlin/com/jetpackduba/gitnuro/AppFilesManager.kt @@ -0,0 +1,43 @@ +package com.jetpackduba.gitnuro + +import com.jetpackduba.gitnuro.extensions.OS +import com.jetpackduba.gitnuro.extensions.getCurrentOs +import com.jetpackduba.gitnuro.logging.printLog +import java.io.File +import javax.inject.Inject + +private const val TAG = "AppFilesManager" + +class AppFilesManager @Inject constructor() { + fun getAppFolder(): File { + val baseFolderPath = when (getCurrentOs()) { + OS.LINUX -> { + var configFolder: String? = System.getenv("XDG_CONFIG_HOME") + + if (configFolder.isNullOrEmpty()) + configFolder = "${System.getenv("HOME")}/.config" + + + configFolder + } + + OS.WINDOWS -> System.getenv("APPDATA").orEmpty() + OS.MAC -> System.getProperty("user.home") + "/Library/Application" + else -> { + printLog(TAG, "Unknown OS") + throw Exception("Invalid OS") + } + } + + val baseFolder = File(baseFolderPath) + + baseFolder.mkdirs() + + val appFolder = File(baseFolder, "gitnuro") + // TODO test if mkdir fails for some reason + if(!appFolder.exists() || !appFolder.isDirectory) + appFolder.mkdir() + + return appFolder + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/TempFilesManager.kt b/src/main/kotlin/com/jetpackduba/gitnuro/TempFilesManager.kt index 8791a22..435eb34 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/TempFilesManager.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/TempFilesManager.kt @@ -1,20 +1,23 @@ package com.jetpackduba.gitnuro import com.jetpackduba.gitnuro.di.TabScope +import com.jetpackduba.gitnuro.extensions.openDirectory +import java.io.File +import java.nio.file.Files import javax.inject.Inject -import kotlin.io.path.createTempDirectory -import kotlin.io.path.deleteIfExists @TabScope -class TempFilesManager @Inject constructor() { - val tempDir by lazy { - val tempDirPath = createTempDirectory("gitnuro_") - tempDirPath.toFile().deleteOnExit() +class TempFilesManager @Inject constructor( + private val appFilesManager: AppFilesManager, +) { + fun tempDir(): File { + val appDataDir = appFilesManager.getAppFolder() + val tempDir = appDataDir.openDirectory("tmp") - tempDirPath - } + if(!tempDir.exists() || !tempDir.isDirectory) { + tempDir.mkdir() + } - fun removeTempDir() { - tempDir.deleteIfExists() + return tempDir } } \ No newline at end of file diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/extensions/FileExtensions.kt b/src/main/kotlin/com/jetpackduba/gitnuro/extensions/FileExtensions.kt new file mode 100644 index 0000000..747b108 --- /dev/null +++ b/src/main/kotlin/com/jetpackduba/gitnuro/extensions/FileExtensions.kt @@ -0,0 +1,13 @@ +package com.jetpackduba.gitnuro.extensions + +import java.io.File + +fun File.openDirectory(dirName: String): File { + val newDir = File(this, dirName) + + if (!newDir.exists() || !newDir.isDirectory) { + newDir.mkdir() + } + + return newDir +} \ No newline at end of file diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/extensions/Shell.kt b/src/main/kotlin/com/jetpackduba/gitnuro/extensions/Shell.kt index 82fe5c2..bc76e68 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/extensions/Shell.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/extensions/Shell.kt @@ -28,7 +28,7 @@ fun runCommand(command: String): String? { fun runCommandWithoutResult(command: String, args: String, file: String): Boolean { val parts: Array = prepareCommand(command, args, file) - printLog(TAG, "Running command $parts") + printLog(TAG, "Running command ${parts.joinToString( )}") return try { val p = Runtime.getRuntime().exec(parts) ?: return false diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/git/RawFileManager.kt b/src/main/kotlin/com/jetpackduba/gitnuro/git/RawFileManager.kt index eafb96d..1f144a5 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/git/RawFileManager.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/git/RawFileManager.kt @@ -1,7 +1,7 @@ package com.jetpackduba.gitnuro.git import com.jetpackduba.gitnuro.TempFilesManager -import com.jetpackduba.gitnuro.extensions.systemSeparator +import com.jetpackduba.gitnuro.extensions.fileName import org.eclipse.jgit.diff.ContentSource import org.eclipse.jgit.diff.DiffEntry import org.eclipse.jgit.diff.RawText @@ -11,16 +11,21 @@ import org.eclipse.jgit.storage.pack.PackConfig import org.eclipse.jgit.treewalk.AbstractTreeIterator import org.eclipse.jgit.treewalk.WorkingTreeIterator import org.eclipse.jgit.util.LfsFactory +import java.io.File import java.io.FileOutputStream import java.nio.file.Files import java.nio.file.Path import javax.inject.Inject -import kotlin.io.path.createTempFile private const val DEFAULT_BINARY_FILE_THRESHOLD = PackConfig.DEFAULT_BIG_FILE_THRESHOLD private const val IMAGE_CONTENT_TYPE = "image/" +/** + * Max of chars that a file name can have. Used to avoid issues with OS chars limit in file names. + */ +private const val FILE_NAME_MAX_LENGTH = 250 + val animatedImages = arrayOf( "image/gif", "image/webp" @@ -76,17 +81,21 @@ class RawFileManager @Inject constructor( entry: DiffEntry, side: DiffEntry.Side ): EntryContent.ImageBinary { - val tempDir = tempFilesManager.tempDir + val tempDir = tempFilesManager.tempDir() - val tempFile = createTempFile(tempDir, prefix = "${entry.newPath.replace(systemSeparator, "_")}_${side.name}") - tempFile.toFile().deleteOnExit() + val prefix = "${entry.getId(side).name().take(20)}_${side.name}_" + val tempFileName = prefix + entry.fileName.take(FILE_NAME_MAX_LENGTH - prefix.length) - val out = FileOutputStream(tempFile.toFile()) + val tempFile = File(tempDir, tempFileName) + + tempFile.deleteOnExit() + + val out = FileOutputStream(tempFile) out.use { ldr.copyTo(out) } - return EntryContent.ImageBinary(tempFile, Files.probeContentType(Path.of(entry.newPath)).orEmpty()) + return EntryContent.ImageBinary(tempFile.absolutePath, Files.probeContentType(Path.of(entry.newPath)).orEmpty()) } private fun isImage(entry: DiffEntry): Boolean { @@ -102,7 +111,7 @@ sealed class EntryContent { object InvalidObjectBlob : EntryContent() data class Text(val rawText: RawText) : EntryContent() sealed class BinaryContent : EntryContent() - data class ImageBinary(val tempFilePath: Path, val contentType: String) : BinaryContent() + data class ImageBinary(val imagePath: String, val contentType: String) : BinaryContent() object Binary : BinaryContent() object TooLargeEntry : EntryContent() } \ No newline at end of file diff --git a/src/main/kotlin/com/jetpackduba/gitnuro/ui/diff/Diff.kt b/src/main/kotlin/com/jetpackduba/gitnuro/ui/diff/Diff.kt index bfb7633..e52e43a 100644 --- a/src/main/kotlin/com/jetpackduba/gitnuro/ui/diff/Diff.kt +++ b/src/main/kotlin/com/jetpackduba/gitnuro/ui/diff/Diff.kt @@ -27,7 +27,6 @@ import androidx.compose.ui.input.pointer.PointerEventType import androidx.compose.ui.input.pointer.onPointerEvent import androidx.compose.ui.res.loadImageBitmap import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -58,8 +57,6 @@ import org.jetbrains.compose.animatedimage.animate import org.jetbrains.compose.animatedimage.loadAnimatedImage import org.jetbrains.compose.resources.loadOrNull import java.io.FileInputStream -import java.nio.file.Path -import kotlin.io.path.absolutePathString import kotlin.math.max private const val MAX_MOVES_COUNT = 5 @@ -253,7 +250,7 @@ fun SideTitle(text: String) { fun SideDiff(entryContent: EntryContent) { when (entryContent) { EntryContent.Binary -> BinaryDiff() - is EntryContent.ImageBinary -> ImageDiff(entryContent.tempFilePath, entryContent.contentType) + is EntryContent.ImageBinary -> ImageDiff(entryContent.imagePath, entryContent.contentType) else -> { } // is EntryContent.Text -> //TODO maybe have a text view if the file was a binary before? @@ -262,9 +259,7 @@ fun SideDiff(entryContent: EntryContent) { } @Composable -private fun ImageDiff(tempImagePath: Path, contentType: String) { - val imagePath = tempImagePath.absolutePathString() - +private fun ImageDiff(imagePath: String, contentType: String) { if (animatedImages.contains(contentType)) { AnimatedImage(imagePath) } else { @@ -305,13 +300,13 @@ private fun StaticImage(tempImagePath: String) { } @Composable -private fun AnimatedImage(tempImagePath: String) { +private fun AnimatedImage(iamgePath: String) { Image( - bitmap = loadOrNull(tempImagePath) { loadAnimatedImage(tempImagePath) }?.animate() ?: ImageBitmap.Blank, + bitmap = loadOrNull(iamgePath) { loadAnimatedImage(iamgePath) }?.animate() ?: ImageBitmap.Blank, contentDescription = null, modifier = Modifier.fillMaxSize() .handMouseClickable { - openFileWithExternalApp(tempImagePath) + openFileWithExternalApp(iamgePath) } ) }