Bump version to 1.3 (versionCode 4)
- Add CityListSheet for managing locations via page indicator tap - Add drag-to-reorder support in city list - Fix Polish translation: "Daylight w roku" → "Długość dnia w roku" - Fix PageIndicator position to avoid overlapping yearly chart - Remove "About" section from UI (simplified) - Add CHANGELOG.md
This commit is contained in:
parent
0512ee674d
commit
aede45cec2
9 changed files with 336 additions and 82 deletions
42
CHANGELOG.md
Normal file
42
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
## [1.3] - 2026-01-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- City management sheet - tap page indicator to manage, reorder and delete locations
|
||||||
|
- Drag-to-reorder support in city list
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Page indicator moved higher to avoid overlapping yearly daylight chart
|
||||||
|
- Simplified page indicator styling
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Polish translation: "Daylight w roku" → "Długość dnia w roku"
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- "About" section from cards and city list (simplified UI)
|
||||||
|
|
||||||
|
## [1.2] - 2025-01-XX
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Location reordering support
|
||||||
|
- Improved page transitions between locations
|
||||||
|
|
||||||
|
## [1.1] - 2025-01-XX
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Sun data refresh on app resume
|
||||||
|
- Periodic 60-second auto-refresh
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Location name text alignment for multi-line names
|
||||||
|
|
||||||
|
## [1.0] - 2025-01-XX
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Initial release
|
||||||
|
- Multi-location sunrise/sunset tracking
|
||||||
|
- Animated sun/moon arc
|
||||||
|
- Time-of-day gradient backgrounds
|
||||||
|
- Yearly daylight chart
|
||||||
|
- 20 language translations
|
||||||
|
|
@ -12,7 +12,7 @@ Track sunrise, sunset and daylight across multiple locations around the world. A
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Multi-location tracking** -- swipe between saved locations to see real-time sun and moon data
|
- **Multi-location tracking** -- swipe between saved locations to see real-time sun and moon data; tap page indicator to manage cities
|
||||||
- **Sunrise & sunset times** with day/night length and countdown to next sunrise
|
- **Sunrise & sunset times** with day/night length and countdown to next sunrise
|
||||||
- **Animated sun/moon arc** -- a visual progress indicator showing where the sun (or moon) is in its path across the sky
|
- **Animated sun/moon arc** -- a visual progress indicator showing where the sun (or moon) is in its path across the sky
|
||||||
- **Moon phase & illumination** -- current lunar phase emoji and illumination percentage
|
- **Moon phase & illumination** -- current lunar phase emoji and illumination percentage
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ android {
|
||||||
applicationId = "com.sunzones"
|
applicationId = "com.sunzones"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 3
|
versionCode = 4
|
||||||
versionName = "1.2"
|
versionName = "1.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import com.sunzones.R
|
||||||
import com.sunzones.ui.addlocation.AddLocationSheet
|
import com.sunzones.ui.addlocation.AddLocationSheet
|
||||||
import com.sunzones.ui.main.animation.getTimeGradient
|
import com.sunzones.ui.main.animation.getTimeGradient
|
||||||
import com.sunzones.ui.main.animation.lerpGradient
|
import com.sunzones.ui.main.animation.lerpGradient
|
||||||
|
import com.sunzones.ui.main.components.CityListSheet
|
||||||
import com.sunzones.ui.main.components.PageIndicator
|
import com.sunzones.ui.main.components.PageIndicator
|
||||||
import com.sunzones.ui.main.components.SunCard
|
import com.sunzones.ui.main.components.SunCard
|
||||||
import com.sunzones.ui.theme.NightBottom
|
import com.sunzones.ui.theme.NightBottom
|
||||||
|
|
@ -50,6 +51,7 @@ fun MainScreen(
|
||||||
) {
|
) {
|
||||||
val uiState by viewModel.uiState.collectAsState()
|
val uiState by viewModel.uiState.collectAsState()
|
||||||
var showAddSheet by remember { mutableStateOf(false) }
|
var showAddSheet by remember { mutableStateOf(false) }
|
||||||
|
var showCityListSheet by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
// Entrance animation — runs once on screen load, shared by all cards
|
// Entrance animation — runs once on screen load, shared by all cards
|
||||||
val entranceProgress = remember { Animatable(0f) }
|
val entranceProgress = remember { Animatable(0f) }
|
||||||
|
|
@ -132,10 +134,11 @@ fun MainScreen(
|
||||||
PageIndicator(
|
PageIndicator(
|
||||||
pageCount = uiState.locations.size,
|
pageCount = uiState.locations.size,
|
||||||
currentPage = pagerState.currentPage,
|
currentPage = pagerState.currentPage,
|
||||||
|
onClick = { showCityListSheet = true },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.BottomCenter)
|
.align(Alignment.BottomCenter)
|
||||||
.navigationBarsPadding()
|
.navigationBarsPadding()
|
||||||
.padding(bottom = 80.dp)
|
.padding(bottom = 120.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,4 +165,16 @@ fun MainScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showCityListSheet) {
|
||||||
|
CityListSheet(
|
||||||
|
locations = uiState.locations,
|
||||||
|
onDismiss = { showCityListSheet = false },
|
||||||
|
onDelete = { viewModel.deleteLocation(it) },
|
||||||
|
onReorder = { from, to -> viewModel.reorderLocations(from, to) },
|
||||||
|
onAddClick = {
|
||||||
|
showCityListSheet = false
|
||||||
|
showAddSheet = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,269 @@
|
||||||
|
package com.sunzones.ui.main.components
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateColorAsState
|
||||||
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.gestures.detectDragGesturesAfterLongPress
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.Add
|
||||||
|
import androidx.compose.material.icons.rounded.Delete
|
||||||
|
import androidx.compose.material.icons.rounded.DragHandle
|
||||||
|
import androidx.compose.material.icons.rounded.MyLocation
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableFloatStateOf
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
|
import androidx.compose.runtime.mutableStateListOf
|
||||||
|
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.draw.clip
|
||||||
|
import androidx.compose.ui.draw.shadow
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.IntOffset
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.zIndex
|
||||||
|
import com.sunzones.R
|
||||||
|
import com.sunzones.domain.model.SunLocation
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun CityListSheet(
|
||||||
|
locations: List<SunLocation>,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onDelete: (Long) -> Unit,
|
||||||
|
onReorder: (fromIndex: Int, toIndex: Int) -> Unit,
|
||||||
|
onAddClick: () -> Unit
|
||||||
|
) {
|
||||||
|
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
|
val haptic = LocalHapticFeedback.current
|
||||||
|
val density = LocalDensity.current
|
||||||
|
|
||||||
|
val localList = remember { mutableStateListOf<SunLocation>() }
|
||||||
|
LaunchedEffect(locations) {
|
||||||
|
localList.clear()
|
||||||
|
localList.addAll(locations)
|
||||||
|
}
|
||||||
|
|
||||||
|
var draggedItemIndex by remember { mutableIntStateOf(-1) }
|
||||||
|
var dragOffsetY by remember { mutableFloatStateOf(0f) }
|
||||||
|
val itemHeight = with(density) { 56.dp.toPx() }
|
||||||
|
|
||||||
|
var locationToDelete by remember { mutableStateOf<SunLocation?>(null) }
|
||||||
|
|
||||||
|
if (locationToDelete != null) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { locationToDelete = null },
|
||||||
|
title = { Text(stringResource(R.string.delete_location_title)) },
|
||||||
|
text = { Text(stringResource(R.string.delete_location_message, locationToDelete!!.name)) },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
locationToDelete?.let { location ->
|
||||||
|
val index = localList.indexOfFirst { it.id == location.id }
|
||||||
|
if (index >= 0) {
|
||||||
|
localList.removeAt(index)
|
||||||
|
}
|
||||||
|
onDelete(location.id)
|
||||||
|
}
|
||||||
|
locationToDelete = null
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.delete),
|
||||||
|
color = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { locationToDelete = null }) {
|
||||||
|
Text(stringResource(R.string.cancel))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ModalBottomSheet(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
sheetState = sheetState
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 24.dp)
|
||||||
|
.padding(bottom = 32.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.manage_cities),
|
||||||
|
style = MaterialTheme.typography.titleLarge
|
||||||
|
)
|
||||||
|
IconButton(onClick = onAddClick) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.Add,
|
||||||
|
contentDescription = stringResource(R.string.add_location),
|
||||||
|
tint = MaterialTheme.colorScheme.primary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
if (localList.isEmpty()) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.empty_state),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.padding(vertical = 24.dp)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
|
||||||
|
LazyColumn(
|
||||||
|
state = listState,
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
itemsIndexed(
|
||||||
|
items = localList,
|
||||||
|
key = { _, item -> item.id }
|
||||||
|
) { index, location ->
|
||||||
|
val isDragging = draggedItemIndex == index
|
||||||
|
val elevation by animateDpAsState(
|
||||||
|
targetValue = if (isDragging) 8.dp else 0.dp,
|
||||||
|
label = "elevation"
|
||||||
|
)
|
||||||
|
val backgroundColor by animateColorAsState(
|
||||||
|
targetValue = if (isDragging) {
|
||||||
|
MaterialTheme.colorScheme.surfaceContainerHighest
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.surface
|
||||||
|
},
|
||||||
|
label = "backgroundColor"
|
||||||
|
)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.zIndex(if (isDragging) 1f else 0f)
|
||||||
|
.offset {
|
||||||
|
IntOffset(
|
||||||
|
x = 0,
|
||||||
|
y = if (isDragging) dragOffsetY.roundToInt() else 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.shadow(elevation, RoundedCornerShape(8.dp))
|
||||||
|
.clip(RoundedCornerShape(8.dp))
|
||||||
|
.background(backgroundColor)
|
||||||
|
.height(56.dp)
|
||||||
|
.padding(start = 8.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.DragHandle,
|
||||||
|
contentDescription = stringResource(R.string.drag_to_reorder),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(24.dp)
|
||||||
|
.pointerInput(Unit) {
|
||||||
|
detectDragGesturesAfterLongPress(
|
||||||
|
onDragStart = {
|
||||||
|
draggedItemIndex = index
|
||||||
|
dragOffsetY = 0f
|
||||||
|
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||||
|
},
|
||||||
|
onDrag = { change, dragAmount ->
|
||||||
|
change.consume()
|
||||||
|
dragOffsetY += dragAmount.y
|
||||||
|
val targetIndex = (index + (dragOffsetY / itemHeight).roundToInt())
|
||||||
|
.coerceIn(0, localList.size - 1)
|
||||||
|
if (targetIndex != index && targetIndex != draggedItemIndex) {
|
||||||
|
val item = localList.removeAt(draggedItemIndex)
|
||||||
|
localList.add(targetIndex, item)
|
||||||
|
dragOffsetY -= (targetIndex - draggedItemIndex) * itemHeight
|
||||||
|
draggedItemIndex = targetIndex
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDragEnd = {
|
||||||
|
if (draggedItemIndex != index) {
|
||||||
|
onReorder(index, draggedItemIndex)
|
||||||
|
}
|
||||||
|
draggedItemIndex = -1
|
||||||
|
dragOffsetY = 0f
|
||||||
|
},
|
||||||
|
onDragCancel = {
|
||||||
|
draggedItemIndex = -1
|
||||||
|
dragOffsetY = 0f
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(12.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = location.name,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (location.isCurrentLocation) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.MyLocation,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton(
|
||||||
|
onClick = { locationToDelete = location }
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Rounded.Delete,
|
||||||
|
contentDescription = stringResource(R.string.delete),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,6 @@ import androidx.compose.animation.core.animateDpAsState
|
||||||
import androidx.compose.animation.core.spring
|
import androidx.compose.animation.core.spring
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
|
|
@ -14,10 +13,8 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.ripple.rememberRipple
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
|
@ -36,11 +33,7 @@ fun PageIndicator(
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.clip(RoundedCornerShape(16.dp))
|
.clip(RoundedCornerShape(16.dp))
|
||||||
.clickable(
|
.clickable(onClick = onClick)
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
|
||||||
indication = rememberRipple(color = Color.White.copy(alpha = 0.3f)),
|
|
||||||
onClick = onClick
|
|
||||||
)
|
|
||||||
.padding(horizontal = 12.dp, vertical = 8.dp)
|
.padding(horizontal = 12.dp, vertical = 8.dp)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,11 @@
|
||||||
package com.sunzones.ui.main.components
|
package com.sunzones.ui.main.components
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import androidx.compose.animation.core.LinearEasing
|
import androidx.compose.animation.core.LinearEasing
|
||||||
import androidx.compose.animation.core.RepeatMode
|
import androidx.compose.animation.core.RepeatMode
|
||||||
import androidx.compose.animation.core.animateFloat
|
import androidx.compose.animation.core.animateFloat
|
||||||
import androidx.compose.animation.core.infiniteRepeatable
|
import androidx.compose.animation.core.infiniteRepeatable
|
||||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
|
|
@ -40,8 +37,6 @@ import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.ui.window.Dialog
|
|
||||||
import androidx.compose.ui.window.DialogProperties
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
|
@ -54,7 +49,6 @@ import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
|
@ -87,7 +81,6 @@ fun SunCard(
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
var showDeleteDialog by remember { mutableStateOf(false) }
|
var showDeleteDialog by remember { mutableStateOf(false) }
|
||||||
var showAboutDialog by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
val scrollState = rememberScrollState()
|
val scrollState = rememberScrollState()
|
||||||
|
|
||||||
|
|
@ -393,23 +386,6 @@ fun SunCard(
|
||||||
modifier = Modifier.padding(top = 8.dp)
|
modifier = Modifier.padding(top = 8.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(24.dp))
|
|
||||||
|
|
||||||
// Subtle about button
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.about_title),
|
|
||||||
style = MaterialTheme.typography.bodySmall,
|
|
||||||
color = TextOnDarkSecondary.copy(alpha = 0.35f),
|
|
||||||
modifier = Modifier
|
|
||||||
.clickable { showAboutDialog = true }
|
|
||||||
.padding(vertical = 12.dp, horizontal = 24.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Space for navigation bar + page indicator + FAB
|
// Space for navigation bar + page indicator + FAB
|
||||||
Spacer(
|
Spacer(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -450,51 +426,6 @@ fun SunCard(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (showAboutDialog) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
val email = stringResource(R.string.about_email)
|
|
||||||
Dialog(
|
|
||||||
onDismissRequest = { showAboutDialog = false },
|
|
||||||
properties = DialogProperties(usePlatformDefaultWidth = false)
|
|
||||||
) {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.padding(horizontal = 48.dp)
|
|
||||||
.background(
|
|
||||||
color = Color.Black.copy(alpha = 0.7f),
|
|
||||||
shape = RoundedCornerShape(28.dp)
|
|
||||||
)
|
|
||||||
.padding(vertical = 32.dp, horizontal = 24.dp),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "\u2600\uFE0F",
|
|
||||||
style = MaterialTheme.typography.displayLarge
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.about_made_in),
|
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
|
||||||
color = Color.White
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
|
||||||
TextButton(onClick = {
|
|
||||||
context.startActivity(
|
|
||||||
Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:$email"))
|
|
||||||
)
|
|
||||||
}) {
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.about_contact),
|
|
||||||
color = SunGold
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,12 @@
|
||||||
<string name="delete_location_message">Usunąć %1$s z Twoich lokalizacji?</string>
|
<string name="delete_location_message">Usunąć %1$s z Twoich lokalizacji?</string>
|
||||||
<string name="delete">Usuń</string>
|
<string name="delete">Usuń</string>
|
||||||
<string name="cancel">Anuluj</string>
|
<string name="cancel">Anuluj</string>
|
||||||
<string name="yearly_daylight">Daylight w roku</string>
|
<string name="yearly_daylight">Długość dnia w roku</string>
|
||||||
<string name="about_title">O aplikacji</string>
|
<string name="about_title">O aplikacji</string>
|
||||||
<string name="about_contact">Napisz do mnie</string>
|
<string name="about_contact">Napisz do mnie</string>
|
||||||
<string name="ok">OK</string>
|
<string name="ok">OK</string>
|
||||||
<string name="night_length">Długość nocy</string>
|
<string name="night_length">Długość nocy</string>
|
||||||
<string name="sunrise_in">Wschód za %1$s</string>
|
<string name="sunrise_in">Wschód za %1$s</string>
|
||||||
|
<string name="manage_cities">Zarządzaj miastami</string>
|
||||||
|
<string name="drag_to_reorder">Przeciągnij by zmienić kolejność</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -20,4 +20,6 @@
|
||||||
<string name="ok">OK</string>
|
<string name="ok">OK</string>
|
||||||
<string name="night_length">Night length</string>
|
<string name="night_length">Night length</string>
|
||||||
<string name="sunrise_in">Sunrise in %1$s</string>
|
<string name="sunrise_in">Sunrise in %1$s</string>
|
||||||
|
<string name="manage_cities">Manage Cities</string>
|
||||||
|
<string name="drag_to_reorder">Drag to reorder</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue