mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 18:39:55 +02:00
feat(send): build durable transfer details and sharing flow
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
package com.vnidrop.app.feature.send
|
||||
|
||||
import java.io.File
|
||||
|
||||
actual fun createPlatformPreviewStore(appDataDir: String): PlatformPreviewStore = JvmPreviewStore(File(appDataDir, "ui/previews"))
|
||||
|
||||
private class JvmPreviewStore(private val directory: File) : PlatformPreviewStore {
|
||||
override fun list(): List<PreviewFileInfo> = directory.listFiles().orEmpty().mapNotNull { file ->
|
||||
file.name.removeSuffix(".preview").toULongOrNull()?.let { PreviewFileInfo(it, file.length(), file.lastModified()) }
|
||||
}
|
||||
override fun read(transferId: ULong): ByteArray? = runCatching { file(transferId).takeIf(File::isFile)?.readBytes() }.getOrNull()
|
||||
override fun writeAtomically(transferId: ULong, bytes: ByteArray): Boolean = runCatching {
|
||||
directory.mkdirs()
|
||||
val target = file(transferId)
|
||||
if (target.isFile) return@runCatching true
|
||||
val temporary = File(directory, ".${target.name}.tmp")
|
||||
temporary.writeBytes(bytes)
|
||||
temporary.renameTo(target).also { if (!it) temporary.delete() }
|
||||
}.getOrDefault(false)
|
||||
override fun delete(transferId: ULong) { file(transferId).delete() }
|
||||
private fun file(transferId: ULong) = File(directory, "$transferId.preview")
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.vnidrop.app.feature.send
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import java.awt.EventQueue
|
||||
import java.awt.FileDialog
|
||||
import java.awt.Frame
|
||||
import java.awt.KeyboardFocusManager
|
||||
import java.io.File
|
||||
|
||||
@Composable
|
||||
actual fun rememberTransferShareActions(): TransferShareActions = remember {
|
||||
object : TransferShareActions {
|
||||
override val canUseNativeShare = DesktopShareBridge.shareFile != null
|
||||
override val nfcAvailability = NfcShareAvailability.Hidden
|
||||
|
||||
override fun exportInvitation(ticket: String, transferName: String, onResult: (Result<Unit>) -> Unit) {
|
||||
EventQueue.invokeLater {
|
||||
onResult(runCatching {
|
||||
val dialog = FileDialog(activeFrame(), "Save VniDrop invitation", FileDialog.SAVE).apply {
|
||||
file = invitationFileName(transferName)
|
||||
}
|
||||
try {
|
||||
dialog.isVisible = true
|
||||
val directory = dialog.directory
|
||||
val name = dialog.file
|
||||
if (directory != null && name != null) File(directory, name).writeText(ticket)
|
||||
} finally { dialog.dispose() }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun shareInvitation(ticket: String, transferName: String, onResult: (Result<Unit>) -> Unit) {
|
||||
EventQueue.invokeLater {
|
||||
val share = DesktopShareBridge.shareFile
|
||||
if (share == null) {
|
||||
onResult(Result.failure(UnsupportedOperationException("System sharing is unavailable on this desktop")))
|
||||
return@invokeLater
|
||||
}
|
||||
onResult(runCatching {
|
||||
val directory = File(System.getProperty("java.io.tmpdir"), "vnidrop-share").apply { mkdirs() }
|
||||
val file = File(directory, invitationFileName(transferName)).apply { writeText(ticket) }
|
||||
share(file).getOrThrow()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeInvitationToNfc(ticket: String, onResult: (Result<Unit>) -> Unit) {
|
||||
onResult(Result.failure(UnsupportedOperationException("NFC is unavailable on desktop")))
|
||||
}
|
||||
override fun cancelNfcWrite() = Unit
|
||||
}
|
||||
}
|
||||
|
||||
private fun activeFrame(): Frame? =
|
||||
(KeyboardFocusManager.getCurrentKeyboardFocusManager().activeWindow as? Frame)
|
||||
?: Frame.getFrames().firstOrNull { it.isActive || it.isFocused }
|
||||
|
||||
object DesktopShareBridge {
|
||||
@Volatile
|
||||
var shareFile: ((File) -> Result<Unit>)? = null
|
||||
}
|
||||
Reference in New Issue
Block a user