|
6 | 6 | import Youtube from 'svelte-youtube-embed'; |
7 | 7 | |
8 | 8 | import type { PageProps } from './$types'; |
9 | | - import { goto } from '$app/navigation'; |
| 9 | + import { goto, pushState } from '$app/navigation'; |
| 10 | + import { page } from '$app/state'; |
10 | 11 |
|
11 | 12 | let { data }: PageProps = $props(); |
12 | 13 |
|
|
65 | 66 | } else { |
66 | 67 | filters[type].push(key); |
67 | 68 | } |
| 69 | + syncHistory(); |
68 | 70 | } |
69 | 71 |
|
70 | 72 | function clearFilters() { |
71 | 73 | query = ''; |
72 | 74 | filters.themes = []; |
73 | 75 | filters.tags = []; |
| 76 | + syncHistory(); |
74 | 77 | } |
75 | 78 |
|
76 | 79 | function gotoPub(pub: Publication) { |
|
81 | 84 | } |
82 | 85 | } |
83 | 86 |
|
84 | | - let initializedFromHash = $state(false); |
| 87 | + function syncHistory() { |
| 88 | + const parts = [ |
| 89 | + ...filters.themes.map((t) => `theme=${t}`), |
| 90 | + ...filters.tags.map((t) => `tag=${t}`), |
| 91 | + ]; |
| 92 | + const hash = parts.length ? `#${parts.join('&')}` : ''; |
| 93 | + const url = page.url.pathname + page.url.search + hash; |
| 94 | + pushState(url, { |
| 95 | + researchFilters: { themes: [...filters.themes], tags: [...filters.tags] }, |
| 96 | + }); |
| 97 | + } |
85 | 98 |
|
86 | | - function parseHash() { |
87 | | - initializedFromHash = true; |
88 | | - const hash = window.location.hash.substring(1); |
| 99 | + function parseHash() { |
| 100 | + filters.themes = []; |
| 101 | + filters.tags = []; |
| 102 | + const hash = location.hash.slice(1); |
89 | 103 | if (!hash) return; |
90 | 104 |
|
91 | | - const params = new URLSearchParams(hash); |
92 | | - for (const [key, value] of params.entries()) { |
| 105 | + for (const [key, value] of new URLSearchParams(hash)) { |
93 | 106 | filters[`${key}s`].push(value); |
94 | 107 | } |
95 | 108 | } |
96 | 109 |
|
97 | | - function updateHash() { |
98 | | - if (filters.themes.length || filters.tags.length) { |
99 | | - window.history.replaceState(null, '', `#${[...filters.themes.map(t => `theme=${t}`), ...filters.tags.map(t => `tag=${t}`)].join('&')}`); |
100 | | - } else { |
101 | | - // Remove hash completely |
102 | | - window.history.replaceState(null, '', window.location.pathname + window.location.search); |
103 | | - } |
104 | | - } |
105 | | -
|
| 110 | + // Restore filters on back/forward (syncHistory is not called here) |
106 | 111 | $effect(() => { |
107 | | - if (initializedFromHash) updateHash(); |
| 112 | + const rf = page.state.researchFilters; |
| 113 | + if (!rf) return; |
| 114 | + if (_.isEqual(rf.themes, filters.themes) && _.isEqual(rf.tags, filters.tags)) return; |
| 115 | + filters.themes = rf.themes; |
| 116 | + filters.tags = rf.tags; |
108 | 117 | }); |
109 | 118 |
|
110 | 119 | onMount(() => { |
|
118 | 127 | }); |
119 | 128 | } |
120 | 129 |
|
121 | | - parseHash(); |
| 130 | + if (page.state.researchFilters) { |
| 131 | + filters.themes = page.state.researchFilters.themes; |
| 132 | + filters.tags = page.state.researchFilters.tags; |
| 133 | + } else { |
| 134 | + parseHash(); |
| 135 | + } |
| 136 | +
|
| 137 | + // Initial landing has no researchFilters in history; restore from hash on back |
| 138 | + const onPopState = () => { |
| 139 | + if (!page.state.researchFilters) parseHash(); |
| 140 | + }; |
| 141 | + addEventListener('popstate', onPopState); |
| 142 | + return () => removeEventListener('popstate', onPopState); |
122 | 143 | }); |
123 | 144 | </script> |
124 | 145 |
|
|
214 | 235 | </div> |
215 | 236 | {/if} |
216 | 237 | <div class="group" role="button" tabindex="0" onclick={() => gotoPub(pub)} onkeydown={(e) => e.key === 'Enter' || e.key === ' ' ? gotoPub(pub) : null}> |
217 | | - <a href={pub.type === 'video' ? `https://youtu.be/${pub.youtube}` : `/pubs/${pub.slug}`} class="block"> |
| 238 | + <a href={pub.type === 'video' ? `https://youtu.be/${pub.youtube}` : `/pubs/${pub.slug}`} onclick={(e) => e.stopPropagation()} class="block"> |
218 | 239 | <h4 class="text-md font-bold {pub.award ? 'text-violet-800' : 'text-amber-700'}"> |
219 | 240 | {#if pub.award} |
220 | 241 | <i class="fas fa-award mr-1"></i> |
|
0 commit comments