Skip to content

Commit c42b100

Browse files
SharonStratsGPT-5.6 Luna
andcommitted
Refreshing browse.html - do not redirect
Prompt: when on browse.html the user gets redirected to localhost:3100/storare Co-authored-by: GPT-5.6 Luna <gpt-5.6-luna@openai.com>
1 parent d839207 commit c42b100

8 files changed

Lines changed: 82 additions & 12 deletions

File tree

‎README.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ Volunteers are always welcome!
5252

5353
### Profile heading
5454

55+
Page shells can call `initializeProfileHeading(getPageUrl)` with a function
56+
returning the currently browsed resource URL. Without an argument, the heading
57+
uses `window.location.href`. The function is evaluated on each refresh, including
58+
authentication changes, so shell-specific URL/query parsing stays in the shell.
59+
Call `refreshProfileHeading(true)` after changing the shell's target.
60+
5561
The `solid-panes-profile-heading` component accepts presentation data through
5662
`profileData`. Its boolean `compact` property (also available as an attribute)
5763
halves the responsive avatar size and shows only the photo, name, and job title.

‎docs/navigation-design.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@ navigation controller, which always applies the same follow-up effects.
5353
mashlib. Those callers rely on it to push the history entry and the URL, so it is the one place that
5454
writes history, and it announces what it did.
5555

56+
Standalone shells such as `browse.html` can pass `preserveUrl: true` in the seventh argument when
57+
they display a subject while keeping their own document URL (the subject is carried in the shell's
58+
query parameters). The current history entry is still replaced with the displayed pane and
59+
account-menu metadata, and navigation is announced from that updated state. This is intentionally
60+
opt-in; ordinary `GotoSubject` callers continue to make the displayed subject the page URL.
61+
5662
Storage is an explicit browser view, not a resource's default pane. Its pane item
5763
renders the storage root with `solo` and `fullView` disabled, so the storage
5864
header, sidebar and listing remain visible even if the root has an index document.

‎src/index.ts‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ export {
7878
byName // from paneRegistry
7979
}
8080

81+
export {
82+
initializeProfileHeading,
83+
refreshProfileHeading
84+
} from './mainPage/profileHeading'
85+
8186
// export for simpler access by non-node scripts
8287
if (typeof window !== 'undefined') {
8388
;(<any>window).panes = {

‎src/mainPage/profileHeading.ts‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ let authReady: Promise<void> | undefined
2323
let displayedPaneKey = ''
2424
let displayedSubjectUri = ''
2525
let loadingPaneKey = ''
26+
let headingPageUrl = () => window.location.href
2627
const hideAnimations = new WeakMap<HTMLElement, Animation>()
2728
const showAnimations = new WeakMap<HTMLElement, Animation>()
2829

@@ -150,7 +151,8 @@ function ensureMountPoint (): HTMLElement | null {
150151
export async function refreshProfileHeading (force = false): Promise<void> {
151152
await authReady
152153

153-
const pod = await getPodContext(window.location.href)
154+
const pageUrl = headingPageUrl()
155+
const pod = await getPodContext(pageUrl)
154156
const mount = ensureMountPoint()
155157
if (!mount) return
156158

@@ -165,7 +167,7 @@ export async function refreshProfileHeading (force = false): Promise<void> {
165167
view,
166168
viaAccountMenu,
167169
viewer: authn.currentUser()?.value,
168-
pageUrl: window.location.href,
170+
pageUrl,
169171
owner: pod.owner,
170172
storageUris: pod.storagePanes.map(pane => pane.subject.value)
171173
})
@@ -243,7 +245,9 @@ export function placeProfileHeading (): void {
243245
ensureMountPoint()
244246
}
245247

246-
export function initializeProfileHeading (): Promise<void> {
248+
/** Shells browsing another resource can supply its current URL instead of the page URL. */
249+
export function initializeProfileHeading (getPageUrl = () => window.location.href): Promise<void> {
250+
headingPageUrl = getPageUrl
247251
ensureMountPoint()
248252

249253
if (!profileSavedListenerRegistered) {

‎src/navigation/options.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export type FolderView = 'explorer' | 'storage' | 'container'
1717
export interface GotoSubjectOptions {
1818
source?: NavigationSource
1919
folderView?: FolderView
20+
/** Keep the caller's document URL while displaying a different subject. */
21+
preserveUrl?: boolean
2022
}
2123

2224
/** The folder view a `GotoSubject` caller asked for, if any. */

‎src/outline/manager.js‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1941,12 +1941,8 @@ export default function (context) {
19411941
createSubjectBlock()
19421942
}
19431943

1944-
if (
1945-
solo &&
1946-
dom &&
1947-
dom.defaultView &&
1948-
dom.defaultView.history
1949-
) {
1944+
if (solo && dom && dom.defaultView && dom.defaultView.history) {
1945+
const preserveUrl = options && typeof options === 'object' && options.preserveUrl === true
19501946
const currentState = dom.defaultView.history.state || {}
19511947
const leavingPage = document.location.href !== subject.uri
19521948
const stateObj = nextHistoryState(currentState, {
@@ -1959,7 +1955,9 @@ export default function (context) {
19591955
try {
19601956
const currentUrl = new URL(document.location.href)
19611957
const targetUrl = new URL(subject.uri, document.location.href)
1962-
if (currentUrl.origin === targetUrl.origin) {
1958+
if (preserveUrl) {
1959+
dom.defaultView.history.replaceState(stateObj, '', currentUrl.href)
1960+
} else if (currentUrl.origin === targetUrl.origin) {
19631961
if (leavingPage) {
19641962
dom.defaultView.history.pushState(stateObj, subject.uri, subject.uri)
19651963
} else if (JSON.stringify(currentState) !== JSON.stringify(stateObj)) {

‎test/unit/mainPage/profileHeading.test.ts‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,20 @@ import { lit, sym } from 'rdflib'
44
import { ns } from 'solid-ui'
55
import type { ProfileHeadingEditContext } from '../../../src/components/profile-heading/ProfileHeading'
66

7-
const { getViewerModeMock, createHeadingEditDialogMock, pod } = vi.hoisted(() => ({
7+
const { getViewerModeMock, createHeadingEditDialogMock, getPodContextMock, pod } = vi.hoisted(() => ({
88
getViewerModeMock: vi.fn(),
99
createHeadingEditDialogMock: vi.fn(),
10+
getPodContextMock: vi.fn(),
1011
// What the page knows about the pod it is in
1112
pod: { current: { owner: undefined as string | undefined, storagePanes: [] as Array<{ subject: { value: string } }>, viewerStorages: [] as string[] } }
1213
}))
1314

1415
vi.mock('../../../src/navigation/podContext', async (importOriginal) => ({
1516
...await importOriginal<typeof import('../../../src/navigation/podContext')>(),
16-
getPodContext: async () => pod.current
17+
getPodContext: async (url: string) => {
18+
getPodContextMock(url)
19+
return pod.current
20+
}
1721
}))
1822

1923
vi.mock('../../../src/components/profile-heading/editor/sections/heading/HeadingEditDialog', () => ({
@@ -220,6 +224,21 @@ describe('page-level profile heading', () => {
220224
checkUser.mockRestore()
221225
})
222226

227+
it('uses the shell resource URL on each refresh and defaults back to the page URL', async () => {
228+
let resourceUrl = 'https://heading-shell.example/profile/card#me'
229+
await initializeProfileHeading(() => resourceUrl)
230+
await refreshProfileHeading()
231+
expect(getPodContextMock).toHaveBeenLastCalledWith(resourceUrl)
232+
233+
resourceUrl = 'https://heading-shell.example/public/'
234+
await refreshProfileHeading(true)
235+
expect(getPodContextMock).toHaveBeenLastCalledWith(resourceUrl)
236+
237+
await initializeProfileHeading()
238+
await refreshProfileHeading()
239+
expect(getPodContextMock).toHaveBeenLastCalledWith(window.location.href)
240+
})
241+
223242
it('hides extra information and editing in compact mode, then restores the full heading', async () => {
224243
const heading = document.createElement('solid-panes-profile-heading') as HTMLElement & {
225244
profileData: {

‎test/unit/outline/manager.test.ts‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,36 @@ describe('manager', () => {
202202
expect(window.history.state).toMatchObject({ paneName: 'mock', paneUri: profile.uri, viaAccountMenu: true })
203203
})
204204

205+
it('updates navigation state while preserving the current URL', () => {
206+
const manager = new OutlineManager({ dom: document, session: { paneRegistry: mockPaneRegistry } })
207+
const shellUrl = `${window.location.origin}/browse.html?uri=${encodeURIComponent(`${window.location.origin}/other/`)}`
208+
const subject = sym(`${window.location.origin}/other/`)
209+
const pane = { ...MockPane, subject }
210+
window.history.replaceState({ existing: true }, '', shellUrl)
211+
212+
const seen: NavigatedDetail[] = []
213+
const stop = onNavigated(detail => seen.push(detail))
214+
manager.GotoSubject(subject, false, pane, true, undefined, undefined, {
215+
folderView: 'explorer',
216+
preserveUrl: true
217+
})
218+
stop()
219+
220+
expect(window.location.href).toBe(shellUrl)
221+
expect(window.history.state).toMatchObject({
222+
existing: true,
223+
paneName: 'mock',
224+
paneUri: subject.uri
225+
})
226+
expect(seen.at(-1)).toMatchObject({
227+
url: shellUrl,
228+
state: {
229+
view: { kind: 'pane', pane: 'mock', subject: subject.uri },
230+
viaAccountMenu: false
231+
}
232+
})
233+
})
234+
205235
it('displays a navbar pane\'s own subject, and the page subject for panes without one', () => {
206236
const manager = new OutlineManager({ dom: document, session: { paneRegistry: mockPaneRegistry } })
207237
const page = sym(`${window.location.origin}/other/container/sub/`)

0 commit comments

Comments
 (0)