The way to Discover and Settings, from a conversation
The chat screen never carried the tab bar. On a narrow window that was survivable, because `← Chats` goes back to a screen that has one — but a wide window has no back button, the room list being a strip beside the conversation instead, so from a room there was no way to either of the other two screens at all. That is new as of the strip, and it is what the strip took away. So the bar is at the foot of this screen too, and Chats is lit while a conversation is open: a conversation is what that tab leads to, and a bar with nothing lit reads as one that has lost its place. Wide only, and not for want of symmetry. Three tabs go to two lines at 300 points and this screen has no 120 points to spare — it overflowed the moment they were added unconditionally, which is the same corner the jump button found. On a narrow window `← Chats` is still the way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
e20f5ab parent: 0e7cd32 modified
nim/src/frq/screens/chat.nim +15 -1 | @@ -17,6 +17,7 @@ import std/options | ||
| 17 | 17 | import frq/[ui, cells, model, clock, reactions, textruns, members, |
| 18 | 18 | glyphs, emoji, rooms, profile] |
| 19 | 19 | from frq/screens/connect import errorNote |
| 20 | +from frq/screens/frame import tabBar | |
| 20 | 21 | |
| 21 | 22 | const |
| 22 | 23 | faceSize = 32 |
| @@ -484,6 +485,18 @@ proc chatScreen*(s: State, connected: bool): Node = | ||
| 484 | 485 | returnRow.children.add button("← back to " & s.overviewReturn, |
| 485 | 486 | "overview.back") |
| 486 | 487 | |
| 488 | + # The same bar the other three screens carry, which this one did not have. | |
| 489 | + # On a narrow window that was survivable: `← Chats` goes back to a screen | |
| 490 | + # that has one. On a wide window there is no back button — the room list is | |
| 491 | + # a strip instead — so Discover and Settings had no way in at all. | |
| 492 | + # | |
| 493 | + # Wide only, and not for symmetry's sake: a 300-point window puts these | |
| 494 | + # three on two lines, and the chat screen has no 120 points to spare. It | |
| 495 | + # overflowed the moment they were added unconditionally. | |
| 496 | + var tabs = vbox(%*{"key": "tabs"}) | |
| 497 | + if s.wide: | |
| 498 | + tabs.children.add tabBar(s) | |
| 499 | + | |
| 487 | 500 | var jump = vbox(%*{"key": "jump"}) |
| 488 | 501 | if not s.atPresent: |
| 489 | 502 | jump.children.add button("↓ Jump to present", "jump.present") |
| @@ -544,4 +557,5 @@ proc chatScreen*(s: State, connected: bool): Node = | ||
| 544 | 557 | returnRow, |
| 545 | 558 | banners, |
| 546 | 559 | separator(), |
| 547 | - compose) | |
| 560 | + compose, | |
| 561 | + tabs) | |
| @@ -17,6 +17,7 @@ import std/options | |||
| 17 | import frq/[ui, cells, model, clock, reactions, textruns, members, | 17 | import frq/[ui, cells, model, clock, reactions, textruns, members, |
| 18 | glyphs, emoji, rooms, profile] | 18 | glyphs, emoji, rooms, profile] |
| 19 | from frq/screens/connect import errorNote | 19 | from frq/screens/connect import errorNote |
| 20 | +from frq/screens/frame import tabBar | ||
| 20 | 21 | ||
| 21 | const | 22 | const |
| 22 | faceSize = 32 | 23 | faceSize = 32 |
| @@ -484,6 +485,18 @@ proc chatScreen*(s: State, connected: bool): Node = | |||
| 484 | returnRow.children.add button("← back to " & s.overviewReturn, | 485 | returnRow.children.add button("← back to " & s.overviewReturn, |
| 485 | "overview.back") | 486 | "overview.back") |
| 486 | 487 | ||
| 488 | + # The same bar the other three screens carry, which this one did not have. | ||
| 489 | + # On a narrow window that was survivable: `← Chats` goes back to a screen | ||
| 490 | + # that has one. On a wide window there is no back button — the room list is | ||
| 491 | + # a strip instead — so Discover and Settings had no way in at all. | ||
| 492 | + # | ||
| 493 | + # Wide only, and not for symmetry's sake: a 300-point window puts these | ||
| 494 | + # three on two lines, and the chat screen has no 120 points to spare. It | ||
| 495 | + # overflowed the moment they were added unconditionally. | ||
| 496 | + var tabs = vbox(%*{"key": "tabs"}) | ||
| 497 | + if s.wide: | ||
| 498 | + tabs.children.add tabBar(s) | ||
| 499 | + | ||
| 487 | var jump = vbox(%*{"key": "jump"}) | 500 | var jump = vbox(%*{"key": "jump"}) |
| 488 | if not s.atPresent: | 501 | if not s.atPresent: |
| 489 | jump.children.add button("↓ Jump to present", "jump.present") | 502 | jump.children.add button("↓ Jump to present", "jump.present") |
| @@ -544,4 +557,5 @@ proc chatScreen*(s: State, connected: bool): Node = | |||
| 544 | returnRow, | 557 | returnRow, |
| 545 | banners, | 558 | banners, |
| 546 | separator(), | 559 | separator(), |
| 547 | - compose) | 560 | + compose, |
| 561 | + tabs) | ||
modified
nim/src/frq/screens/frame.nim +6 -1 | @@ -8,9 +8,14 @@ import std/json | ||
| 8 | 8 | import frq/[ui, cells] |
| 9 | 9 | |
| 10 | 10 | func tabBar*(s: State): Node = |
| 11 | + ## The three screens, and the way between them. | |
| 12 | + ## | |
| 13 | + ## Lit for a conversation as well as for the list, because a conversation is | |
| 14 | + ## what the Chats tab leads to — a bar with nothing lit on it reads as a bar | |
| 15 | + ## that has lost its place. | |
| 11 | 16 | hbox(%*{"spacing": 8}, |
| 12 | 17 | button("Chats", "screen.chats", |
| 13 | - if s.screen == scChats: "primary" else: "default"), | |
| 18 | + if s.screen in {scChats, scChat}: "primary" else: "default"), | |
| 14 | 19 | button("Discover", "screen.discover", |
| 15 | 20 | if s.screen == scDiscover: "primary" else: "default"), |
| 16 | 21 | button("Settings", "screen.settings", |
| @@ -8,9 +8,14 @@ import std/json | |||
| 8 | import frq/[ui, cells] | 8 | import frq/[ui, cells] |
| 9 | 9 | ||
| 10 | func tabBar*(s: State): Node = | 10 | func tabBar*(s: State): Node = |
| 11 | + ## The three screens, and the way between them. | ||
| 12 | + ## | ||
| 13 | + ## Lit for a conversation as well as for the list, because a conversation is | ||
| 14 | + ## what the Chats tab leads to — a bar with nothing lit on it reads as a bar | ||
| 15 | + ## that has lost its place. | ||
| 11 | hbox(%*{"spacing": 8}, | 16 | hbox(%*{"spacing": 8}, |
| 12 | button("Chats", "screen.chats", | 17 | button("Chats", "screen.chats", |
| 13 | - if s.screen == scChats: "primary" else: "default"), | 18 | + if s.screen in {scChats, scChat}: "primary" else: "default"), |
| 14 | button("Discover", "screen.discover", | 19 | button("Discover", "screen.discover", |
| 15 | if s.screen == scDiscover: "primary" else: "default"), | 20 | if s.screen == scDiscover: "primary" else: "default"), |
| 16 | button("Settings", "screen.settings", | 21 | button("Settings", "screen.settings", |
modified
nim/tests/tscreens.nim +25 -0 | @@ -258,6 +258,8 @@ suite "the chat screen's panes": | ||
| 258 | 258 | at: 1_700_000_000_000'i64)] |
| 259 | 259 | s.rooms["#test"] = r |
| 260 | 260 | s.current = "#test" |
| 261 | + # What `room.open` does, and what the tab bar reads to light Chats. | |
| 262 | + s.screen = scChat | |
| 261 | 263 | |
| 262 | 264 | test "the backlog and the rooms sit side by side when the list is up": |
| 263 | 265 | # The toggle had a button and rendered nothing at all: `hideChatList` was |
| @@ -286,6 +288,29 @@ suite "the chat screen's panes": | ||
| 286 | 288 | check t.find("scroll").anyIt( |
| 287 | 289 | it.props{"scrollKey"}.getStr() == "messages-#test") |
| 288 | 290 | |
| 291 | + test "the way to Discover and Settings is on the screen": | |
| 292 | + # It was not: the chat screen never carried the tab bar, and on a wide | |
| 293 | + # window there is no back button either — the room list is a strip. So | |
| 294 | + # from a conversation there was no way to either of them at all. | |
| 295 | + let t = cht.chatScreen(s, true) | |
| 296 | + check "Discover" in t.labels("button") | |
| 297 | + check "Settings" in t.labels("button") | |
| 298 | + | |
| 299 | + test "and Chats is lit, because a conversation is what it leads to": | |
| 300 | + let t = cht.chatScreen(s, true) | |
| 301 | + let chats = t.find("button").filterIt( | |
| 302 | + it.props{"label"}.getStr() == "Chats") | |
| 303 | + check chats.len == 1 | |
| 304 | + check chats[0].props{"kind"}.getStr() == "primary" | |
| 305 | + | |
| 306 | + test "but not on a narrow window, which has no room for them": | |
| 307 | + # Three tabs go to two lines at 300 points, and this screen has no 120 | |
| 308 | + # points to spare — `← Chats` is the way back there instead. | |
| 309 | + s.windowWidth = wideWidth - 1 | |
| 310 | + let t = cht.chatScreen(s, true) | |
| 311 | + check "Discover" notin t.labels("button") | |
| 312 | + check "← Chats" in t.labels("button") | |
| 313 | + | |
| 289 | 314 | test "a room in the strip opens it, and the current one is lit": |
| 290 | 315 | s.hideChatList = false |
| 291 | 316 | let t = cht.chatScreen(s, true) |
| @@ -258,6 +258,8 @@ suite "the chat screen's panes": | |||
| 258 | at: 1_700_000_000_000'i64)] | 258 | at: 1_700_000_000_000'i64)] |
| 259 | s.rooms["#test"] = r | 259 | s.rooms["#test"] = r |
| 260 | s.current = "#test" | 260 | s.current = "#test" |
| 261 | + # What `room.open` does, and what the tab bar reads to light Chats. | ||
| 262 | + s.screen = scChat | ||
| 261 | 263 | ||
| 262 | test "the backlog and the rooms sit side by side when the list is up": | 264 | test "the backlog and the rooms sit side by side when the list is up": |
| 263 | # The toggle had a button and rendered nothing at all: `hideChatList` was | 265 | # The toggle had a button and rendered nothing at all: `hideChatList` was |
| @@ -286,6 +288,29 @@ suite "the chat screen's panes": | |||
| 286 | check t.find("scroll").anyIt( | 288 | check t.find("scroll").anyIt( |
| 287 | it.props{"scrollKey"}.getStr() == "messages-#test") | 289 | it.props{"scrollKey"}.getStr() == "messages-#test") |
| 288 | 290 | ||
| 291 | + test "the way to Discover and Settings is on the screen": | ||
| 292 | + # It was not: the chat screen never carried the tab bar, and on a wide | ||
| 293 | + # window there is no back button either — the room list is a strip. So | ||
| 294 | + # from a conversation there was no way to either of them at all. | ||
| 295 | + let t = cht.chatScreen(s, true) | ||
| 296 | + check "Discover" in t.labels("button") | ||
| 297 | + check "Settings" in t.labels("button") | ||
| 298 | + | ||
| 299 | + test "and Chats is lit, because a conversation is what it leads to": | ||
| 300 | + let t = cht.chatScreen(s, true) | ||
| 301 | + let chats = t.find("button").filterIt( | ||
| 302 | + it.props{"label"}.getStr() == "Chats") | ||
| 303 | + check chats.len == 1 | ||
| 304 | + check chats[0].props{"kind"}.getStr() == "primary" | ||
| 305 | + | ||
| 306 | + test "but not on a narrow window, which has no room for them": | ||
| 307 | + # Three tabs go to two lines at 300 points, and this screen has no 120 | ||
| 308 | + # points to spare — `← Chats` is the way back there instead. | ||
| 309 | + s.windowWidth = wideWidth - 1 | ||
| 310 | + let t = cht.chatScreen(s, true) | ||
| 311 | + check "Discover" notin t.labels("button") | ||
| 312 | + check "← Chats" in t.labels("button") | ||
| 313 | + | ||
| 289 | test "a room in the strip opens it, and the current one is lit": | 314 | test "a room in the strip opens it, and the current one is lit": |
| 290 | s.hideChatList = false | 315 | s.hideChatList = false |
| 291 | let t = cht.chatScreen(s, true) | 316 | let t = cht.chatScreen(s, true) |