Lane 3: Settings' last sections, D22's leave prompt, and a regression fix for players (#729, CAMP-15, CAMP-17, SH-7) #783
Loading…
Reference in a new issue
No description provided.
Delete branch "build/lane3-settings-c"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The last PR of lane 3's surfaces. It builds Content packs (CAMP-15) and Archive, export & delete (CAMP-17), replaces the leave prompt (D22, SH-7), and fixes a regression lane 3 itself put on main. Two sections stay deliberately unbuilt.
Read first: a regression, found reviewing this PR
From #769 until this PR, a player could not reach the quest log, the loot ledger, or an ownership offer.
The old campaign page (
/manage) still holds all three, and all three are for every member. Its only door was "Everything else", a row I added inside Settings, which is the GM's. The sharpest case is an ownership offer. It can go to any member, and the bell's "has offered you this campaign" links to This week. This week didn't show the offer, and the player had no route to the page that could accept it.Fixed here:
/manage. "Everything else" is gone from Settings, whose sub-list is now only its built sections.gmbanner ("a decision addressed to you"). Answer leads to where it can be accepted or declined. That costs one request, and a failure draws nothing and doesn't cost the page.?open=any more. The old page still honours it for old links.Tests cover the banner for its recipient, and its absence for anyone else and when the read fails. Both absence tests wait on the offer request itself, so neither can pass before it has answered. There's also a test for a player's Contents carrying the new door.
D22: the leave prompt, finally
SH-036andIS-371draw it: "Leave without saving? — Your changes to General are not saved." Stay (first, focused) / Leave without saving. It replaceswindow.confirm, which had two buttons and generic words.ui/LeavePromptis mounted once inRootProviders. The hook asks it through context; with no host above it (component tests) it falls back towindow.confirmwith the same words.IS-296, tested).?tab=change, or a hash that isn't an element on the page. A literal "hash change" would prompt on Profile's own Contents jumps (/profile#p-when), where nothing is being left.SH-035is behaviour:beforeunloadis unchanged and tested, and the browser's own "Leave site?" is not ours to style.The hook's calling contract is kept. The one addition is an optional second argument, the name.
hooks/leaveGuardCallers.test.jspins every caller and the name it passes:beforeunloadonly, unchangedOne behaviour change, and it's a bug fix. If the edit was saved between the click and the guard's answer, the navigation now goes ahead. It used to call
reset()and drop the click, despite its own comment saying "let it through".useLeaveGuardalso no longer registersbeforeunloadtwice.CAMP-15: Content packs, at campaign scope
Built fresh as
pages/CampaignSettingsPacks.jsx.components/ContentPacks.jsxis untouched, because Admin (lane 9) still renders it and reskinning it would change Admin mid-lane. The two shareapi/contentPacks.jsand the affirmation rules, and Admin's tests pass unchanged.Every drawn state is built: nothing loaded, loading, would not load; Export and Remove (Remove asks first, names the record count, and a failure stays inside the dialog); the load form in each of its states; the state chips in words; an administrator turning one off, with a required reason recorded in the audit log; from a web address;
CA-117as replaced (two choices, default GM only); the affirmation's loading state, and its re-arm after a 422; importing / loaded with warnings / loading in the background; and a 4xx showing the server's sentence while a 5xx shows a plain one.Not built: giving a pack a licence (
IS-315's second half). No endpoint sets one (D36). "No licence given — nothing in this pack can be exported" is shown.CAMP-17: Archive, export & delete
Who may do each part follows the endpoints, and each part is absent to anyone it isn't for. Export is
require_gm; archive and delete arerequire_owner; restore isrequire_owner_or_admin. A GM who isn't the owner reads "Archiving and deleting the campaign are Dana Whitlock's, as its owner" (IS-199).Delete is
IS-012:Modal tier="typed". Tests cover near-misses (a partial name, the wrong case, a trailing dot, a doubled space), the exact name, and a failed delete that leaves the page usable.What the page says is recoverable comes from the server.
DELETEsoft-deletes. The bin keeps a campaign for_DEFAULT_TRASH_RETENTION_DAYS= 30 days (floor 7, and nothing writes another value). Restore is the owner's or an admin's, from the bin on Home.Not built: clone (
IS-224–IS-226). That isn't the settings-copy the New campaign page already does. It's a clone with an optional member list and a wiki-entry checkbox tree, it has no endpoint, and it belongs to #125 (templates and cloning, v4.6.0).Deliberately not built
_can_edit_lore_entry, and reputation levels are stored nowhere. A form that saves neither is a dead control. #779.Both stay
built: falsewith a comment saying why, and neither appears in the index or the Contents.SH-7: checked, and what was wrong
CA-003/IS-370failed and are fixed. Members (6 sites), the session form, Discord and Recording showederr.messageverbatim, e.g. "Internal server error" or "Failed to fetch". The newutils/plainError.jsshows a 4xx sentence, which is written for people, and otherwise the page's own plain words. Three tests had modelled a refusal as a 500; they now use realistic 400s and 409s.SH-034,IS-295(chips carry words),IS-369(Storyline's Undo; Invites deliberately has none, for the reason its comment gives).IS-292), and focus doesn't move on navigation (IS-296's other half). Both belong to the shell. #780.Where the drawing is wrong
Checks
lane2b.pyandlane2c.pyprint no unclassified rows, and no classification changed.scripts/journeys.shrun by the build, 26 passed, which matters because D22 changes a hook the journeys' pages use.[clientWidth, scrollWidth]measured inside a 390 px iframe was [390, 390] on every page, and focus on the leave dialog's open landed on Stay.🤖 Generated with Claude Code