0.2.93
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
## 0.2.92 - 2026-08-21
|
## 0.2.92 - 2026-08-21
|
||||||
- Improved: App design / layout tweaks.
|
- Improved: App design / layout tweaks.
|
||||||
- Improved: Many behind the scenes tweaks, bug fixes to make the app run smoother.
|
- Improved: Many behind the scenes tweaks, bug fixes to make the app run smoother.
|
||||||
|
- Improved: Gateway updated to version 0.1.63.
|
||||||
|
|
||||||
## 0.2.84 - 2026-08-21
|
## 0.2.84 - 2026-08-21
|
||||||
- Improved: Loading and performance of the continue watching row.
|
- Improved: Loading and performance of the continue watching row.
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ val projectNoticeText =
|
|||||||
|
|
||||||
// A release workflow can derive the app version from its Git tag without editing the
|
// A release workflow can derive the app version from its Git tag without editing the
|
||||||
// source tree. Local builds keep using the checked-in default.
|
// source tree. Local builds keep using the checked-in default.
|
||||||
val defaultVersionName = "0.2.92"
|
val defaultVersionName = "0.2.93"
|
||||||
val membyVersionName: String =
|
val membyVersionName: String =
|
||||||
(project.findProperty("memby.versionName") as String?)
|
(project.findProperty("memby.versionName") as String?)
|
||||||
?.trim()
|
?.trim()
|
||||||
|
|||||||
@@ -83,6 +83,11 @@ import java.util.Date
|
|||||||
import java.util.Calendar
|
import java.util.Calendar
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
|
// The focused metadata hero now owns year/runtime/certificate for browse destinations.
|
||||||
|
// Search and Genres keep their own card context through PosterGridCard instead.
|
||||||
|
private const val BROWSE_ROW_SECONDARY_METADATA = false
|
||||||
|
private const val CONTINUE_ROW_SECONDARY_METADATA = true
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun PlaybackLaunchOverlay(item: BaseItem, modifier: Modifier = Modifier) {
|
internal fun PlaybackLaunchOverlay(item: BaseItem, modifier: Modifier = Modifier) {
|
||||||
val rotation by rememberInfiniteTransition(label = "playback-launch").animateFloat(
|
val rotation by rememberInfiniteTransition(label = "playback-launch").animateFloat(
|
||||||
@@ -277,7 +282,6 @@ internal fun FocusedHomeBackdrop(homeViewModel: HomeViewModel) {
|
|||||||
@Composable
|
@Composable
|
||||||
internal fun FocusedHomeMetadata(
|
internal fun FocusedHomeMetadata(
|
||||||
homeViewModel: HomeViewModel,
|
homeViewModel: HomeViewModel,
|
||||||
sectionLabel: String,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val focusedItem by homeViewModel.focusedItem.collectAsStateWithLifecycle()
|
val focusedItem by homeViewModel.focusedItem.collectAsStateWithLifecycle()
|
||||||
@@ -288,7 +292,6 @@ internal fun FocusedHomeMetadata(
|
|||||||
MetadataHero(
|
MetadataHero(
|
||||||
item = focusedItem,
|
item = focusedItem,
|
||||||
loading = homeContent.loading.isNotEmpty(),
|
loading = homeContent.loading.isNotEmpty(),
|
||||||
sectionLabel = sectionLabel,
|
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -480,7 +483,10 @@ internal fun serverHomeRows(state: HomeUiState, settings: Settings): List<HomeBr
|
|||||||
"movie-schedule" -> "No monitored movies have a digital release in the next 5 days"
|
"movie-schedule" -> "No monitored movies have a digital release in the next 5 days"
|
||||||
else -> "Nothing to show here yet"
|
else -> "Nothing to show here yet"
|
||||||
},
|
},
|
||||||
showSecondaryMetadata = settings.showHomeCardMetadata,
|
showSecondaryMetadata = when (row.kind) {
|
||||||
|
"continue" -> CONTINUE_ROW_SECONDARY_METADATA
|
||||||
|
else -> BROWSE_ROW_SECONDARY_METADATA
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return applyHomeRowPreferences(mapped, settings)
|
return applyHomeRowPreferences(mapped, settings)
|
||||||
@@ -558,7 +564,7 @@ internal fun homeRowsFor(
|
|||||||
kind = MediaRowKind.CONTINUE,
|
kind = MediaRowKind.CONTINUE,
|
||||||
loading = HomeSection.CONTINUE in state.loading,
|
loading = HomeSection.CONTINUE in state.loading,
|
||||||
emptyMessage = "Nothing in progress",
|
emptyMessage = "Nothing in progress",
|
||||||
showSecondaryMetadata = settings.showHomeCardMetadata,
|
showSecondaryMetadata = CONTINUE_ROW_SECONDARY_METADATA,
|
||||||
)
|
)
|
||||||
val latestMovies = HomeBrowseRow(
|
val latestMovies = HomeBrowseRow(
|
||||||
id = "latest-movies",
|
id = "latest-movies",
|
||||||
@@ -567,7 +573,7 @@ internal fun homeRowsFor(
|
|||||||
kind = MediaRowKind.MOVIES,
|
kind = MediaRowKind.MOVIES,
|
||||||
loading = HomeSection.LATEST in state.loading,
|
loading = HomeSection.LATEST in state.loading,
|
||||||
emptyMessage = "No recent movies found",
|
emptyMessage = "No recent movies found",
|
||||||
showSecondaryMetadata = settings.showHomeCardMetadata,
|
showSecondaryMetadata = BROWSE_ROW_SECONDARY_METADATA,
|
||||||
)
|
)
|
||||||
val favorites = HomeBrowseRow(
|
val favorites = HomeBrowseRow(
|
||||||
id = "favorites",
|
id = "favorites",
|
||||||
@@ -576,7 +582,7 @@ internal fun homeRowsFor(
|
|||||||
kind = MediaRowKind.FAVORITES,
|
kind = MediaRowKind.FAVORITES,
|
||||||
loading = HomeSection.FAVORITES in state.loading,
|
loading = HomeSection.FAVORITES in state.loading,
|
||||||
emptyMessage = "Your favourites will appear here",
|
emptyMessage = "Your favourites will appear here",
|
||||||
showSecondaryMetadata = settings.showHomeCardMetadata,
|
showSecondaryMetadata = BROWSE_ROW_SECONDARY_METADATA,
|
||||||
)
|
)
|
||||||
val destinationRows = when (destination) {
|
val destinationRows = when (destination) {
|
||||||
// The gateway composes the home screen — including rows this app has no concept
|
// The gateway composes the home screen — including rows this app has no concept
|
||||||
@@ -609,6 +615,11 @@ internal fun homeRowsFor(
|
|||||||
row.id.startsWith("curated:movies:")
|
row.id.startsWith("curated:movies:")
|
||||||
}
|
}
|
||||||
.distinctBy(HomeBrowseRow::id) + listOf(
|
.distinctBy(HomeBrowseRow::id) + listOf(
|
||||||
|
continueRow.copy(
|
||||||
|
id = "continue-movies",
|
||||||
|
title = "Continue Watching",
|
||||||
|
items = state.continueWatching.filter(BaseItem::isMovie),
|
||||||
|
),
|
||||||
favorites.copy(
|
favorites.copy(
|
||||||
id = "favourite-movies",
|
id = "favourite-movies",
|
||||||
title = "Favourite Movies",
|
title = "Favourite Movies",
|
||||||
@@ -726,7 +737,7 @@ internal fun forYouBrowseRows(
|
|||||||
kind = MediaRowKind.MOVIES,
|
kind = MediaRowKind.MOVIES,
|
||||||
loading = state.loading,
|
loading = state.loading,
|
||||||
emptyMessage = state.error ?: "Watch and browse a little more to shape your picks",
|
emptyMessage = state.error ?: "Watch and browse a little more to shape your picks",
|
||||||
showSecondaryMetadata = true,
|
showSecondaryMetadata = BROWSE_ROW_SECONDARY_METADATA,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -738,8 +749,7 @@ internal fun forYouBrowseRows(
|
|||||||
kind = MediaRowKind.MOVIES,
|
kind = MediaRowKind.MOVIES,
|
||||||
loading = state.loading && row.items.isEmpty(),
|
loading = state.loading && row.items.isEmpty(),
|
||||||
emptyMessage = state.error ?: "No picks fit this time window yet",
|
emptyMessage = state.error ?: "No picks fit this time window yet",
|
||||||
// Reasons are the primary secondary metadata in this destination.
|
showSecondaryMetadata = BROWSE_ROW_SECONDARY_METADATA,
|
||||||
showSecondaryMetadata = true,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -739,7 +739,6 @@ internal fun HomeScreen(
|
|||||||
forYouState,
|
forYouState,
|
||||||
selectedDestination,
|
selectedDestination,
|
||||||
settings.homeSections,
|
settings.homeSections,
|
||||||
settings.showHomeCardMetadata,
|
|
||||||
settings.hideWatchedMovies,
|
settings.hideWatchedMovies,
|
||||||
) {
|
) {
|
||||||
val destinationRows = if (selectedDestination == BrowseDestination.FOR_YOU) {
|
val destinationRows = if (selectedDestination == BrowseDestination.FOR_YOU) {
|
||||||
@@ -1377,7 +1376,6 @@ internal fun HomeScreen(
|
|||||||
} else {
|
} else {
|
||||||
FocusedHomeMetadata(
|
FocusedHomeMetadata(
|
||||||
homeViewModel = homeViewModel,
|
homeViewModel = homeViewModel,
|
||||||
sectionLabel = selectedDestination.label,
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(metadataHeight)
|
.height(metadataHeight)
|
||||||
// LazyColumn is drawn later as a sibling. Keep the hero
|
// LazyColumn is drawn later as a sibling. Keep the hero
|
||||||
@@ -1393,7 +1391,7 @@ internal fun HomeScreen(
|
|||||||
Text(
|
Text(
|
||||||
// The gateway's maintenance notice when it sent one, and the
|
// The gateway's maintenance notice when it sent one, and the
|
||||||
// generic wording otherwise.
|
// generic wording otherwise.
|
||||||
homeStatus.statusMessage ?: "Connection is slow — showing available content",
|
homeStatus.statusMessage ?: "Memby Server is updating/busy.... slow. Please wait.",
|
||||||
color = MembyMutedText,
|
color = MembyMutedText,
|
||||||
fontSize = 14.sp,
|
fontSize = 14.sp,
|
||||||
modifier = Modifier.padding(
|
modifier = Modifier.padding(
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import com.ponzischeme89.memby.ui.theme.MembySurface
|
|||||||
internal fun MetadataHero(
|
internal fun MetadataHero(
|
||||||
item: BaseItem?,
|
item: BaseItem?,
|
||||||
loading: Boolean,
|
loading: Boolean,
|
||||||
sectionLabel: String,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
@@ -70,7 +69,6 @@ internal fun MetadataHero(
|
|||||||
MediaMetadataPanel(
|
MediaMetadataPanel(
|
||||||
item = item,
|
item = item,
|
||||||
loading = loading,
|
loading = loading,
|
||||||
sectionLabel = sectionLabel,
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(
|
.padding(
|
||||||
|
|||||||
@@ -135,8 +135,8 @@ private val RailSurface: Color get() = MembySurface.copy(alpha = 0.95f)
|
|||||||
private val MutedText: Color get() = MembyMutedText
|
private val MutedText: Color get() = MembyMutedText
|
||||||
private val QuietText: Color get() = MembyQuietText
|
private val QuietText: Color get() = MembyQuietText
|
||||||
private val BACKDROP_SETTLE_DELAY = 240.milliseconds
|
private val BACKDROP_SETTLE_DELAY = 240.milliseconds
|
||||||
private val MetadataHeroLogoMaxWidth = 420.dp
|
private val MetadataHeroLogoMaxWidth = 360.dp
|
||||||
private val MetadataHeroLogoMaxHeight = 92.dp
|
private val MetadataHeroLogoMaxHeight = 72.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BackdropLayer(item: BaseItem?, modifier: Modifier = Modifier) {
|
fun BackdropLayer(item: BaseItem?, modifier: Modifier = Modifier) {
|
||||||
@@ -196,7 +196,6 @@ fun BackdropLayer(item: BaseItem?, modifier: Modifier = Modifier) {
|
|||||||
fun MediaMetadataPanel(
|
fun MediaMetadataPanel(
|
||||||
item: BaseItem?,
|
item: BaseItem?,
|
||||||
loading: Boolean,
|
loading: Boolean,
|
||||||
sectionLabel: String,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
BoxWithConstraints(modifier) {
|
BoxWithConstraints(modifier) {
|
||||||
@@ -211,7 +210,6 @@ fun MediaMetadataPanel(
|
|||||||
modifier = Modifier.width(contentWidth),
|
modifier = Modifier.width(contentWidth),
|
||||||
verticalArrangement = Arrangement.spacedBy(if (compactLayout) 7.dp else 10.dp),
|
verticalArrangement = Arrangement.spacedBy(if (compactLayout) 7.dp else 10.dp),
|
||||||
) {
|
) {
|
||||||
Text(sectionLabel, color = QuietText, fontSize = 12.sp, fontWeight = FontWeight.SemiBold)
|
|
||||||
Text(
|
Text(
|
||||||
"Your library",
|
"Your library",
|
||||||
color = Color.White,
|
color = Color.White,
|
||||||
@@ -221,7 +219,7 @@ fun MediaMetadataPanel(
|
|||||||
Text("Choose something to watch.", color = MutedText, fontSize = 14.sp)
|
Text("Choose something to watch.", color = MutedText, fontSize = 14.sp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> MetadataContent(item, sectionLabel, contentWidth, compactLayout)
|
else -> MetadataContent(item, contentWidth, compactLayout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,12 +260,11 @@ private fun SkeletonBlock(width: Dp, height: Dp) {
|
|||||||
@Composable
|
@Composable
|
||||||
private fun MetadataContent(
|
private fun MetadataContent(
|
||||||
item: BaseItem,
|
item: BaseItem,
|
||||||
sectionLabel: String,
|
|
||||||
contentWidth: Dp,
|
contentWidth: Dp,
|
||||||
compact: Boolean,
|
compact: Boolean,
|
||||||
) {
|
) {
|
||||||
if (item.isSchedule) {
|
if (item.isSchedule) {
|
||||||
ScheduleMetadataContent(item, sectionLabel, contentWidth, compact)
|
ScheduleMetadataContent(item, contentWidth, compact)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val repository = ServiceLocator.repository
|
val repository = ServiceLocator.repository
|
||||||
@@ -285,7 +282,6 @@ private fun MetadataContent(
|
|||||||
modifier = Modifier.width(contentWidth).fillMaxHeight(),
|
modifier = Modifier.width(contentWidth).fillMaxHeight(),
|
||||||
verticalArrangement = Arrangement.spacedBy(if (compact) 5.dp else 8.dp),
|
verticalArrangement = Arrangement.spacedBy(if (compact) 5.dp else 8.dp),
|
||||||
) {
|
) {
|
||||||
Text(sectionLabel.uppercase(), color = EmbyGreen, fontSize = 11.sp, fontWeight = FontWeight.Bold, letterSpacing = 1.1.sp)
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -296,13 +292,13 @@ private fun MetadataContent(
|
|||||||
Modifier.heightIn(max = MetadataHeroLogoMaxHeight)
|
Modifier.heightIn(max = MetadataHeroLogoMaxHeight)
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
contentAlignment = Alignment.BottomStart,
|
contentAlignment = Alignment.TopStart,
|
||||||
) {
|
) {
|
||||||
if (logo != null) {
|
if (logo != null) {
|
||||||
TitleLogoImage(
|
TitleLogoImage(
|
||||||
logoUrl = logo,
|
logoUrl = logo,
|
||||||
contentDescription = item.seriesName?.takeIf { item.isEpisode } ?: item.name,
|
contentDescription = item.seriesName?.takeIf { item.isEpisode } ?: item.name,
|
||||||
alignment = Alignment.BottomStart,
|
alignment = Alignment.TopStart,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(MetadataHeroLogoMaxWidth)
|
.width(MetadataHeroLogoMaxWidth)
|
||||||
.height(MetadataHeroLogoMaxHeight),
|
.height(MetadataHeroLogoMaxHeight),
|
||||||
@@ -383,7 +379,6 @@ private fun MetadataContent(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun ScheduleMetadataContent(
|
private fun ScheduleMetadataContent(
|
||||||
item: BaseItem,
|
item: BaseItem,
|
||||||
sectionLabel: String,
|
|
||||||
contentWidth: Dp,
|
contentWidth: Dp,
|
||||||
compact: Boolean,
|
compact: Boolean,
|
||||||
) {
|
) {
|
||||||
@@ -391,13 +386,6 @@ private fun ScheduleMetadataContent(
|
|||||||
modifier = Modifier.width(contentWidth).fillMaxHeight(),
|
modifier = Modifier.width(contentWidth).fillMaxHeight(),
|
||||||
verticalArrangement = Arrangement.spacedBy(if (compact) 5.dp else 8.dp),
|
verticalArrangement = Arrangement.spacedBy(if (compact) 5.dp else 8.dp),
|
||||||
) {
|
) {
|
||||||
Text(
|
|
||||||
sectionLabel.uppercase(),
|
|
||||||
color = EmbyGreen,
|
|
||||||
fontSize = 11.sp,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
letterSpacing = 1.2.sp,
|
|
||||||
)
|
|
||||||
Text(
|
Text(
|
||||||
item.name,
|
item.name,
|
||||||
color = Color.White,
|
color = Color.White,
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
0.1.61
|
0.1.63
|
||||||
|
|||||||
Reference in New Issue
Block a user