Withhold a component's freshness while it is being built
Nothing re-reads the manifest during a run, so the "out of date" note beside a running progress bar is the answer from before the button was pressed -- shown for the length of a build, next to the work that is making it wrong. The card now reads a busy component's freshness as unknown, in the one place the list of components is built for the cards, so the row's note and the sibling-mismatch warning both go quiet. Same condition as the one that disables the Update button. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
aa8e2b97a5
commit
c35f84a373
2 files changed
+116
-81
No files matched your search
@@ -494,6 +494,21 @@ mutable at runtime from the phone.
|
|||||||
the Retry that acts on it, in the one place that reports that
|
the Retry that acts on it, in the one place that reports that
|
||||||
component's failures whether they came from a build, a download or a
|
component's failures whether they came from a build, a download or a
|
||||||
service action.
|
service action.
|
||||||
|
- **A card being worked on has no freshness, rather than the one from
|
||||||
|
before the press.** Nothing re-reads the manifest during a run -- the
|
||||||
|
entry was fetched before the button was pressed and read again only
|
||||||
|
once the run is over -- so "out of date" drawn beside the bar that is
|
||||||
|
making it current is last minute's answer wearing this minute's
|
||||||
|
clothes. `UpdaterScreen` therefore maps a busy component's `freshness`
|
||||||
|
to `unknown` in the one place the component list is built for the
|
||||||
|
cards, which is what makes both readers of it -- the row's own note and
|
||||||
|
`MismatchedPairNote` -- go quiet without either having to know why. The
|
||||||
|
condition is deliberately the same pair that disables the Update button
|
||||||
|
(`projectState.busy || componentState.busy`): what cannot be acted on
|
||||||
|
is exactly what cannot be measured just now. It comes back the instant
|
||||||
|
the run ends, still saying "out of date" if the build failed, because
|
||||||
|
by then the entry has been read again -- withheld is not the same as
|
||||||
|
cleared.
|
||||||
- **A project's own `.dev-updater.ron` is a request, never an
|
- **A project's own `.dev-updater.ron` is a request, never an
|
||||||
instruction.** It only runs once accepted from the phone, which copies
|
instruction.** It only runs once accepted from the phone, which copies
|
||||||
it into `config.ron`; `AppEntry::pending_declaration` is the whole gate.
|
it into `config.ron`; `AppEntry::pending_declaration` is the whole gate.
|
||||||
|
|||||||
@@ -1580,87 +1580,107 @@ private fun AppCard(
|
|||||||
// order is the *build* order -- dev-updater builds its
|
// order is the *build* order -- dev-updater builds its
|
||||||
// server before its APK on purpose -- and that stays as
|
// server before its APK on purpose -- and that stays as
|
||||||
// it is; this sort is stable, so anything else keeps it.
|
// it is; this sort is stable, so anything else keeps it.
|
||||||
entry.components
|
//
|
||||||
.sortedBy { it.isServer }
|
// Freshness is withheld from a component being worked
|
||||||
.forEach { component ->
|
// on, because it is the one thing on the card that the
|
||||||
val installed = installedTimes[component.name]
|
// work is *about* and nothing re-reads it while the
|
||||||
val installedSize = installedSizes[component.name]
|
// work runs: the entry was fetched before the button
|
||||||
val chosenVariantPath = chosenVariants[component.name]
|
// was pressed and is read again only once the run is
|
||||||
// This component's own, so nothing below can
|
// over. So "out of date" beside the bar that is making
|
||||||
// reach for a sibling's by accident.
|
// it current is not a measurement, it is the answer
|
||||||
val componentState = componentStates[component.name]
|
// from before the press -- and unknown is what this
|
||||||
val upToDate =
|
// card already says when it has no reading, so both
|
||||||
component.apk?.let {
|
// readers of it (the row's own note, and the sibling
|
||||||
isUpToDate(it, installed, chosenVariantPath)
|
// warning) go quiet without either having to know why.
|
||||||
} == true
|
// Deliberately the same pair of conditions that
|
||||||
ComponentCard(
|
// disables the Update button: what cannot be acted on
|
||||||
entryKey = entry.key,
|
// is exactly what cannot be measured just now.
|
||||||
component = component,
|
val components =
|
||||||
packageName = component.apk?.packageName,
|
entry.components
|
||||||
// What the download would cost, and what it
|
.sortedBy { it.isServer }
|
||||||
// replaces. An APK's size sits where a
|
.map { component ->
|
||||||
// server's state does: the one thing worth
|
if (projectState.busy || componentStates[component.name].busy)
|
||||||
// knowing about it besides its name.
|
component.copy(freshness = "unknown")
|
||||||
sizeText =
|
else component
|
||||||
component.apk
|
}
|
||||||
?.takeIf { it.built }
|
components.forEach { component ->
|
||||||
?.let { apk ->
|
val installed = installedTimes[component.name]
|
||||||
when {
|
val installedSize = installedSizes[component.name]
|
||||||
!upToDate && installedSize != null ->
|
val chosenVariantPath = chosenVariants[component.name]
|
||||||
"${formatSize(installedSize)} \u2192 " +
|
// This component's own, so nothing below can
|
||||||
formatSize(apk.size)
|
// reach for a sibling's by accident.
|
||||||
else -> formatSize(apk.size)
|
val componentState = componentStates[component.name]
|
||||||
}
|
val upToDate =
|
||||||
},
|
component.apk?.let { isUpToDate(it, installed, chosenVariantPath) } ==
|
||||||
chosenVariantPath = chosenVariantPath,
|
true
|
||||||
onSelectVariant = { onSelectVariant(component.name, it) },
|
ComponentCard(
|
||||||
// This app reaches the server through this server.
|
entryKey = entry.key,
|
||||||
// Stopping or uninstalling it is the one action
|
component = component,
|
||||||
// here that cannot be undone from the phone.
|
packageName = component.apk?.packageName,
|
||||||
isOwnServer = entry.builtIn,
|
// What the download would cost, and what it
|
||||||
build =
|
// replaces. An APK's size sits where a
|
||||||
componentBuild(
|
// server's state does: the one thing worth
|
||||||
projectState,
|
// knowing about it besides its name.
|
||||||
componentState,
|
sizeText =
|
||||||
component.name,
|
component.apk
|
||||||
),
|
?.takeIf { it.built }
|
||||||
// Being worked on right now, which the
|
?.let { apk ->
|
||||||
// component's own slice of the build says
|
when {
|
||||||
// directly rather than being inferred from a
|
!upToDate && installedSize != null ->
|
||||||
// project-wide phase name.
|
"${formatSize(installedSize)} \u2192 " +
|
||||||
working =
|
formatSize(apk.size)
|
||||||
componentBuild(projectState, componentState, component.name)
|
else -> formatSize(apk.size)
|
||||||
?.running == true,
|
}
|
||||||
busy = serviceBusy == component.name,
|
},
|
||||||
state = componentState,
|
chosenVariantPath = chosenVariantPath,
|
||||||
onAction = { action, purge ->
|
onSelectVariant = { onSelectVariant(component.name, it) },
|
||||||
onServiceAction(component.name, action, purge)
|
// This app reaches the server through this server.
|
||||||
},
|
// Stopping or uninstalling it is the one action
|
||||||
controls = {
|
// here that cannot be undone from the phone.
|
||||||
if (!component.isServer) {
|
isOwnServer = entry.builtIn,
|
||||||
// Said before the button, because it
|
build =
|
||||||
// qualifies what pressing it gets you.
|
componentBuild(
|
||||||
MismatchedPairNote(
|
projectState,
|
||||||
self = component,
|
componentState,
|
||||||
others = entry.components,
|
component.name,
|
||||||
)
|
),
|
||||||
// The button that asked for it, then
|
// Being worked on right now, which the
|
||||||
// how far along it is: a bar reports on
|
// component's own slice of the build says
|
||||||
// the control above it.
|
// directly rather than being inferred from a
|
||||||
UpdateButton(
|
// project-wide phase name.
|
||||||
built = component.apk?.built == true,
|
working =
|
||||||
needsBuild = entry.needsBuild,
|
componentBuild(projectState, componentState, component.name)
|
||||||
installed = installed != null,
|
?.running == true,
|
||||||
upToDate = upToDate,
|
busy = serviceBusy == component.name,
|
||||||
state = componentState,
|
state = componentState,
|
||||||
projectState = projectState,
|
onAction = { action, purge ->
|
||||||
onUpdate = { onUpdate(entry, component.name) },
|
onServiceAction(component.name, action, purge)
|
||||||
)
|
},
|
||||||
ApkProgress(componentState)
|
controls = {
|
||||||
}
|
if (!component.isServer) {
|
||||||
},
|
// Said before the button, because it
|
||||||
)
|
// qualifies what pressing it gets you.
|
||||||
}
|
MismatchedPairNote(
|
||||||
|
self = component,
|
||||||
|
others = components,
|
||||||
|
)
|
||||||
|
// The button that asked for it, then
|
||||||
|
// how far along it is: a bar reports on
|
||||||
|
// the control above it.
|
||||||
|
UpdateButton(
|
||||||
|
built = component.apk?.built == true,
|
||||||
|
needsBuild = entry.needsBuild,
|
||||||
|
installed = installed != null,
|
||||||
|
upToDate = upToDate,
|
||||||
|
state = componentState,
|
||||||
|
projectState = projectState,
|
||||||
|
onUpdate = { onUpdate(entry, component.name) },
|
||||||
|
)
|
||||||
|
ApkProgress(componentState)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// What there is to read before pressing anything, directly
|
// What there is to read before pressing anything, directly
|
||||||
|
|||||||
Reference in new issue
Block a user