Compare commits
2 Commits
022e309873
...
9257da046a
| Author | SHA1 | Date | |
|---|---|---|---|
| 9257da046a | |||
| ff955b35fe |
@@ -24,7 +24,7 @@ struct ComponentLibraryItem: Identifiable, Equatable {
|
||||
let iconURL: URL?
|
||||
|
||||
var displayVoltage: Double? {
|
||||
voltageIn ?? voltageOut
|
||||
[voltageIn, voltageOut].compactMap({ $0 }).first(where: { $0 > 0 })
|
||||
}
|
||||
|
||||
var current: Double? {
|
||||
|
||||
@@ -29,7 +29,8 @@ struct LoadsView: View {
|
||||
@State private var overviewExportRequested = false
|
||||
@State private var diagramExportRequested = false
|
||||
@State private var isExportingOverview = false
|
||||
@State private var overviewShareItem: OverviewShareItem?
|
||||
@State private var previewURL: URL?
|
||||
@State private var previewTempURL: URL?
|
||||
@State private var overviewExportError: OverviewExportError?
|
||||
|
||||
let system: ElectricalSystem
|
||||
@@ -219,11 +220,12 @@ struct LoadsView: View {
|
||||
}
|
||||
)
|
||||
}
|
||||
.sheet(item: $overviewShareItem, onDismiss: {
|
||||
cleanupOverviewShareItem()
|
||||
.quickLookPreview($previewURL)
|
||||
.onChange(of: previewURL) { _, newValue in
|
||||
if newValue == nil {
|
||||
cleanupPreview()
|
||||
ReviewPrompt.registerSuccessfulExport()
|
||||
}) { item in
|
||||
ShareSheet(items: item.shareItems)
|
||||
}
|
||||
}
|
||||
.alert(
|
||||
String(localized: "overview.share.error.title", defaultValue: "Export Failed"),
|
||||
@@ -1106,12 +1108,6 @@ struct LoadsView: View {
|
||||
|
||||
// MARK: - PDF Export
|
||||
|
||||
private struct OverviewShareItem: Identifiable {
|
||||
let id = UUID()
|
||||
let shareItems: [Any]
|
||||
let tempURL: URL?
|
||||
}
|
||||
|
||||
private struct OverviewExportError: Identifiable {
|
||||
let message: String
|
||||
var id: String { message }
|
||||
@@ -1134,7 +1130,8 @@ struct LoadsView: View {
|
||||
"system": snapshot.systemName,
|
||||
])
|
||||
await MainActor.run {
|
||||
overviewShareItem = OverviewShareItem(shareItems: [url], tempURL: url)
|
||||
previewURL = url
|
||||
previewTempURL = url
|
||||
isExportingOverview = false
|
||||
}
|
||||
} catch {
|
||||
@@ -1164,7 +1161,8 @@ struct LoadsView: View {
|
||||
AnalyticsTracker.log("Diagram Image Shared", properties: [
|
||||
"system": snapshot.systemName,
|
||||
])
|
||||
overviewShareItem = OverviewShareItem(shareItems: [url], tempURL: url)
|
||||
previewURL = url
|
||||
previewTempURL = url
|
||||
} else {
|
||||
overviewExportError = OverviewExportError(
|
||||
message: String(localized: "overview.share.diagram.error", defaultValue: "Could not generate diagram. Check your internet connection.")
|
||||
@@ -1284,11 +1282,10 @@ struct LoadsView: View {
|
||||
)
|
||||
}
|
||||
|
||||
private func cleanupOverviewShareItem() {
|
||||
guard let item = overviewShareItem else { return }
|
||||
overviewShareItem = nil
|
||||
if let url = item.tempURL {
|
||||
private func cleanupPreview() {
|
||||
if let url = previewTempURL {
|
||||
try? FileManager.default.removeItem(at: url)
|
||||
previewTempURL = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ data class ComponentLibraryItem(
|
||||
val iconURL: String?,
|
||||
val affiliateLinks: List<AffiliateLink>,
|
||||
) {
|
||||
val displayVoltage: Double? get() = voltageIn ?: voltageOut
|
||||
val displayVoltage: Double? get() = voltageIn?.takeIf { it > 0 } ?: voltageOut?.takeIf { it > 0 }
|
||||
|
||||
val current: Double?
|
||||
get() {
|
||||
|
||||
@@ -87,16 +87,20 @@ object SystemDiagram {
|
||||
onError: () -> Unit,
|
||||
) {
|
||||
val bitmap = fetchOrFallback(context, state, unit)
|
||||
share(context, bitmap, state.system?.name ?: "System")
|
||||
}
|
||||
|
||||
suspend fun share(context: Context, bitmap: Bitmap, systemName: String) {
|
||||
val file = withContext(Dispatchers.IO) {
|
||||
val opaque = flattenOnWhite(bitmap)
|
||||
val name = state.system?.name?.takeIf { it.isNotBlank() } ?: "System"
|
||||
val name = systemName.takeIf { it.isNotBlank() } ?: "System"
|
||||
val dir = File(context.cacheDir, "exports").apply { mkdirs() }
|
||||
val out = File(dir, "${name.replace(Regex("[^A-Za-z0-9-_]"), "_")}-Diagram.png")
|
||||
out.outputStream().use { opaque.compress(Bitmap.CompressFormat.PNG, 100, it) }
|
||||
out
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
Analytics.log("Diagram Image Shared", mapOf("system" to (state.system?.name ?: "")))
|
||||
Analytics.log("Diagram Image Shared", mapOf("system" to systemName))
|
||||
PdfShare.shareFile(context, file, "image/png")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package app.voltplan.cable.ui.system
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.gestures.rememberTransformableState
|
||||
import androidx.compose.foundation.gestures.transformable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
|
||||
import androidx.compose.material.icons.outlined.IosShare
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import app.voltplan.cable.R
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun DiagramPreviewDialog(
|
||||
bitmap: Bitmap,
|
||||
onShare: () -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
Dialog(
|
||||
onDismissRequest = onDismiss,
|
||||
properties = DialogProperties(usePlatformDefaultWidth = false),
|
||||
) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = MaterialTheme.colorScheme.background,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.overview_share_diagram)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onDismiss) {
|
||||
Icon(Icons.AutoMirrored.Outlined.ArrowBack, contentDescription = stringResource(R.string.action_back))
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = onShare) {
|
||||
Icon(Icons.Outlined.IosShare, contentDescription = stringResource(R.string.overview_share_diagram))
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
) { padding ->
|
||||
var scale by remember { mutableFloatStateOf(1f) }
|
||||
var offset by remember { mutableStateOf(Offset.Zero) }
|
||||
val transformState = rememberTransformableState { zoomChange, panChange, _ ->
|
||||
scale = (scale * zoomChange).coerceIn(1f, 8f)
|
||||
offset = if (scale == 1f) Offset.Zero else offset + panChange
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding)
|
||||
.transformable(transformState),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Image(
|
||||
bitmap = bitmap.asImageBitmap(),
|
||||
contentDescription = stringResource(R.string.overview_share_diagram),
|
||||
contentScale = ContentScale.Fit,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(16.dp)
|
||||
.graphicsLayer(
|
||||
scaleX = scale,
|
||||
scaleY = scale,
|
||||
translationX = offset.x,
|
||||
translationY = offset.y,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,7 @@ import app.voltplan.cable.ui.theme.componentColor
|
||||
import app.voltplan.cable.data.ReviewPrompt
|
||||
import app.voltplan.cable.pdf.SystemDiagram
|
||||
import app.voltplan.cable.pdf.SystemOverviewPdf
|
||||
import android.graphics.Bitmap
|
||||
import android.widget.Toast
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
@@ -106,6 +107,7 @@ fun SystemDetailScreen(
|
||||
var showSystemEditor by remember { mutableStateOf(false) }
|
||||
var showOverviewMenu by remember { mutableStateOf(false) }
|
||||
var exporting by remember { mutableStateOf(false) }
|
||||
var diagramBitmapPreview by remember { mutableStateOf<Bitmap?>(null) }
|
||||
val system = state.system
|
||||
|
||||
// Switch to the matching tab before opening an editor, so returning from the
|
||||
@@ -167,13 +169,9 @@ fun SystemDetailScreen(
|
||||
showOverviewMenu = false
|
||||
scope.launch {
|
||||
exporting = true
|
||||
var failed = false
|
||||
SystemDiagram.exportAndShare(context, state, unitSystem) {
|
||||
failed = true
|
||||
Toast.makeText(context, R.string.overview_share_diagram_error, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
val bitmap = SystemDiagram.fetchOrFallback(context, state, unitSystem)
|
||||
exporting = false
|
||||
if (!failed) ReviewPrompt.registerSuccessfulExport(context)
|
||||
diagramBitmapPreview = bitmap
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -256,6 +254,20 @@ fun SystemDetailScreen(
|
||||
}
|
||||
}
|
||||
|
||||
diagramBitmapPreview?.let { bmp ->
|
||||
DiagramPreviewDialog(
|
||||
bitmap = bmp,
|
||||
onShare = {
|
||||
scope.launch {
|
||||
SystemDiagram.share(context, bmp, state.system?.name ?: "System")
|
||||
diagramBitmapPreview = null
|
||||
ReviewPrompt.registerSuccessfulExport(context)
|
||||
}
|
||||
},
|
||||
onDismiss = { diagramBitmapPreview = null },
|
||||
)
|
||||
}
|
||||
|
||||
if (showSystemEditor && system != null) {
|
||||
var location by remember { mutableStateOf(system.location) }
|
||||
AppearanceEditorSheet(
|
||||
|
||||
Reference in New Issue
Block a user