Compare commits
1
Commits
7638fa4a9a
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
600949e475 |
+55
-1
@@ -73,6 +73,17 @@ export default function App() {
|
|||||||
*/
|
*/
|
||||||
const initialRouteRef = useRef(parseRoute(window.location.pathname, window.location.search));
|
const initialRouteRef = useRef(parseRoute(window.location.pathname, window.location.search));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Back-button support for the post view. Every other URL change
|
||||||
|
* (`replaceState`s the tab/archive) is intentionally NOT pushed — only
|
||||||
|
* opening a post gets its own history entry, matching Instagram's own
|
||||||
|
* back-button behaviour: Back closes the post instead of leaving the app.
|
||||||
|
*/
|
||||||
|
const pushedPostRef = useRef(false);
|
||||||
|
/** Set while reacting to a popstate, so the URL-sync effect below does not
|
||||||
|
* try to push/replace/back() again for a change the browser already made. */
|
||||||
|
const suppressNextSyncRef = useRef(false);
|
||||||
|
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
const profilePicInputRef = useRef<HTMLInputElement>(null);
|
const profilePicInputRef = useRef<HTMLInputElement>(null);
|
||||||
@@ -367,6 +378,13 @@ export default function App() {
|
|||||||
// loader below is waiting to read.
|
// loader below is waiting to read.
|
||||||
if (!hasInitialLoaded) return;
|
if (!hasInitialLoaded) return;
|
||||||
|
|
||||||
|
// Consumed exactly once per popstate, regardless of what happens below:
|
||||||
|
// a popstate-driven change often already matches the URL (the browser
|
||||||
|
// already moved the pointer), which used to leave this flag stuck true
|
||||||
|
// and silently no-op the NEXT real close/open until something reset it.
|
||||||
|
const wasPopState = suppressNextSyncRef.current;
|
||||||
|
suppressNextSyncRef.current = false;
|
||||||
|
|
||||||
const archive = currentArchive?.name ?? (allPosts.length > 0 ? username : null) ?? null;
|
const archive = currentArchive?.name ?? (allPosts.length > 0 ? username : null) ?? null;
|
||||||
const nextPath = buildPath({
|
const nextPath = buildPath({
|
||||||
archive,
|
archive,
|
||||||
@@ -374,12 +392,48 @@ export default function App() {
|
|||||||
post: selectedPost ? postSlug(selectedPost) : null,
|
post: selectedPost ? postSlug(selectedPost) : null,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (nextPath !== window.location.pathname + window.location.search) {
|
if (nextPath === window.location.pathname + window.location.search) return;
|
||||||
|
if (wasPopState) return; // the browser already navigated; nothing to add
|
||||||
|
|
||||||
console.log(`[Permalink] Updating URL to: ${nextPath}`);
|
console.log(`[Permalink] Updating URL to: ${nextPath}`);
|
||||||
|
|
||||||
|
if (selectedPost && !pushedPostRef.current) {
|
||||||
|
// Opening a post: push, so Back closes it instead of leaving the app.
|
||||||
|
window.history.pushState(null, '', nextPath);
|
||||||
|
pushedPostRef.current = true;
|
||||||
|
} else if (!selectedPost && pushedPostRef.current) {
|
||||||
|
// Closing a post that was pushed for: consume that entry rather than
|
||||||
|
// piling a new one on top of it, so Back still means "one step".
|
||||||
|
pushedPostRef.current = false;
|
||||||
|
window.history.back();
|
||||||
|
} else {
|
||||||
window.history.replaceState(null, '', nextPath);
|
window.history.replaceState(null, '', nextPath);
|
||||||
}
|
}
|
||||||
}, [hasInitialLoaded, currentArchive?.name, username, allPosts.length, activeTab, selectedPost?.id]);
|
}, [hasInitialLoaded, currentArchive?.name, username, allPosts.length, activeTab, selectedPost?.id]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Back/forward support for the post view. Only a post push (above) ever
|
||||||
|
* creates an entry, so this only ever needs to open or close a post —
|
||||||
|
* never re-derive the tab or archive, which stayed on replaceState.
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
const onPopState = () => {
|
||||||
|
suppressNextSyncRef.current = true;
|
||||||
|
const route = parseRoute(window.location.pathname, window.location.search);
|
||||||
|
const post = route.post ? findPostBySlug(allPosts, route.post) : null;
|
||||||
|
if (post) {
|
||||||
|
setActiveTab(tabForSource(post.source));
|
||||||
|
setSelectedPost(post);
|
||||||
|
pushedPostRef.current = true; // forward navigation can land back here
|
||||||
|
} else {
|
||||||
|
setSelectedPost(null);
|
||||||
|
pushedPostRef.current = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener('popstate', onPopState);
|
||||||
|
return () => window.removeEventListener('popstate', onPopState);
|
||||||
|
}, [allPosts]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (hasInitialLoaded) return;
|
if (hasInitialLoaded) return;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user