The screens nobody had rendered get their margins back
Going flush to the screen edges was right for chrome, bands and rows, and wrong for every screen I had not looked at. Calls, the contact detail and the story feed were all laid out assuming the 20dp frame that is gone, so their content sat hard against both sides of the phone and their trailing controls ran off the right edge entirely: the call-back button on a call row was half a button, and the contact rail's three actions were clipped. Calls rows and alert rows are `PortalRow` now, like the conversation list. `DetailRow` carries its own gutter. The story feed and its empty state do too. Two bands on the contact screen were unlabelled strips. They say what they are over now, which is what a band is for. `SectionBand` in ChatScreens duplicated `PortalBand` from the day PortalChrome was added; I wrote the second one and left the first. There is one band again, and the 19 call sites point at it. 1474 unit tests green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
5bbc328076
commit
79aefbe707
@@ -143,15 +143,11 @@ fun ColumnScope.CallsScreen(
|
||||
.firstOrNull { it.userId == entry.peerId }
|
||||
?.let { it.displayName.ifBlank { it.handle } }
|
||||
?: "user ${entry.peerId}"
|
||||
Row(
|
||||
PortalRow(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { model.requestCall(entry.peerId) }
|
||||
.padding(vertical = Kit.rowGap / 2)
|
||||
.testTag("call-${entry.peerId}-${entry.at}"),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Kit.gutter),
|
||||
) {
|
||||
Avatar(
|
||||
name = name,
|
||||
@@ -229,7 +225,6 @@ fun ColumnScope.CallsScreen(
|
||||
)
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,31 +681,31 @@ fun ColumnScope.AccountScreen(
|
||||
|
||||
// Directly under the picture: status is the other thing people expect to
|
||||
// find first on an account screen, and it was last.
|
||||
SectionBand("Your status")
|
||||
PortalBand("Your status")
|
||||
SettingsSection { PresencePicker(model, phase.presence) }
|
||||
|
||||
SectionBand("Stories")
|
||||
PortalBand("Stories")
|
||||
SettingsSection { StoryAudienceSection(model, phase) }
|
||||
|
||||
// Wallet and Files are bar destinations, in the same place iOS puts them.
|
||||
|
||||
SectionBand("App lock")
|
||||
PortalBand("App lock")
|
||||
SettingsSection { AppLockSection() }
|
||||
|
||||
SectionBand("Media")
|
||||
PortalBand("Media")
|
||||
SettingsSection { MediaQualitySection() }
|
||||
|
||||
SectionBand("Your devices")
|
||||
PortalBand("Your devices")
|
||||
SettingsSection { RegisteredDevicesSection(model, phase) }
|
||||
|
||||
SectionBand("History backup")
|
||||
PortalBand("History backup")
|
||||
SettingsSection { HistoryBackupSection(model) }
|
||||
|
||||
// Sits with the other settings and not under anything privacy-shaped.
|
||||
SectionBand("Display")
|
||||
PortalBand("Display")
|
||||
SettingsSection { DisplaySection() }
|
||||
|
||||
SectionBand()
|
||||
PortalBand()
|
||||
// The door to the alerts inbox. Alerts left the bar on 2026-08-28 with the
|
||||
// note that the screen "survives for the badge tap-through from Account",
|
||||
// and this is that tap-through; without it the screen had no way in.
|
||||
@@ -724,22 +724,22 @@ fun ColumnScope.AccountScreen(
|
||||
}
|
||||
SettingsSection { NotificationSection() }
|
||||
|
||||
SectionBand("Network")
|
||||
PortalBand("Network")
|
||||
SettingsSection { NetworkSection(model, phase.buddies) }
|
||||
|
||||
SectionBand("Parental controls")
|
||||
PortalBand("Parental controls")
|
||||
SettingsSection { ParentalControlsSection() }
|
||||
|
||||
SectionBand("Contact list")
|
||||
PortalBand("Contact list")
|
||||
SettingsSection { ContactListPrivacySection(model) }
|
||||
|
||||
SectionBand()
|
||||
PortalBand()
|
||||
SettingsSection { AddressCard(fnid = phase.fnid, relay = phase.relay, handle = phase.handle) }
|
||||
|
||||
SectionBand()
|
||||
PortalBand()
|
||||
SettingsSection { MailboxesSection(model, phase) }
|
||||
|
||||
SectionBand()
|
||||
PortalBand()
|
||||
SettingsSection {
|
||||
// The row is the label; the field must not repeat it.
|
||||
SettingsRow(label = "Display name") {}
|
||||
@@ -790,7 +790,7 @@ fun ColumnScope.AccountScreen(
|
||||
|
||||
// -- Security (§19) ------------------------------------------------------.
|
||||
|
||||
SectionBand()
|
||||
PortalBand()
|
||||
SettingsSection {
|
||||
if (phase.forkDetected) {
|
||||
// Above the toggle, not below: this is a statement about the server
|
||||
@@ -850,17 +850,17 @@ fun ColumnScope.AccountScreen(
|
||||
}
|
||||
}
|
||||
|
||||
SectionBand()
|
||||
PortalBand()
|
||||
SettingsSection { AppVersionSection(model) }
|
||||
|
||||
SectionBand()
|
||||
PortalBand()
|
||||
SettingsSection { DiagnosticsSection() }
|
||||
|
||||
SectionBand()
|
||||
PortalBand()
|
||||
SettingsSection { DonateSection() }
|
||||
|
||||
// The destructive pair, in their own section.
|
||||
SectionBand()
|
||||
PortalBand()
|
||||
SettingsSection {
|
||||
SettingsRow(
|
||||
label = "Sign out",
|
||||
@@ -1316,39 +1316,6 @@ private fun SettingsSection(content: @Composable ColumnScope.() -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The 16pt band the kit puts between sections.
|
||||
*/
|
||||
|
||||
/** Internal, not private: the contact detail uses the same band. */
|
||||
@Composable
|
||||
internal fun SectionBandPublic() = SectionBand()
|
||||
|
||||
@Composable
|
||||
private fun SectionBand(label: String? = null) {
|
||||
// The portal's own section furniture: a blue band ruled top and bottom.
|
||||
// A bare gap reads as an app with nothing between its sections; this
|
||||
// reads as a page with headings.
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Box(Modifier.fillMaxWidth().height(Kit.hairline).background(Portal.rule()))
|
||||
Box(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.background(Portal.band())
|
||||
.padding(horizontal = Space.md, vertical = if (label == null) Space.sm else 7.dp),
|
||||
) {
|
||||
if (label != null) {
|
||||
Text(
|
||||
label.uppercase(),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onBackground,
|
||||
)
|
||||
}
|
||||
}
|
||||
Box(Modifier.fillMaxWidth().height(Kit.hairline).background(Portal.rule()))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* One settings row: label, an optional sentence under it, and one control on
|
||||
|
||||
@@ -62,7 +62,7 @@ fun ColumnScope.ContactDetail(
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = Space.sm, vertical = Space.xs),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Box(
|
||||
@@ -182,14 +182,14 @@ fun ColumnScope.ContactDetail(
|
||||
|
||||
Spacer(Modifier.height(Space.xl))
|
||||
|
||||
SectionBandPublic()
|
||||
PortalBand("Their details")
|
||||
Column(Modifier.padding(vertical = Space.sm)) {
|
||||
DetailRow("Username", buddy.handle.ifBlank { ", " })
|
||||
DetailRow("User ID", buddy.userId.toString())
|
||||
DetailRow("Status", buddy.presence.ifBlank { "unknown" })
|
||||
}
|
||||
|
||||
SectionBandPublic()
|
||||
PortalBand("Managing this contact")
|
||||
Column(Modifier.padding(vertical = Space.sm)) {
|
||||
DetailRow(
|
||||
"Clear this conversation",
|
||||
@@ -264,7 +264,10 @@ private fun DetailRow(
|
||||
.fillMaxWidth()
|
||||
.then(if (onClick != null) Modifier.clickable(onClick = onClick) else Modifier)
|
||||
.then(if (tag != null) Modifier.testTag(tag) else Modifier)
|
||||
.padding(vertical = Kit.bubblePadding),
|
||||
// The screen is flush to its edges now, so the row carries its
|
||||
// own gutter. Without it the label and the value sit hard
|
||||
// against both sides of the phone.
|
||||
.padding(horizontal = Space.md, vertical = Kit.bubblePadding),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(Space.md),
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user