branchSwitch.js 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. import { isValidBranchParam } from "@/lib/frontend/params";
  2. import { branchPath } from "@/lib/frontend/routes";
  3. import {
  4. parseSearchUrlState,
  5. serializeSearchUrlState,
  6. SEARCH_SCOPE,
  7. } from "@/lib/frontend/search/urlState";
  8. export function readRouteBranchFromPathname(pathname) {
  9. if (typeof pathname !== "string" || !pathname.startsWith("/")) return null;
  10. const seg = pathname.split("/").filter(Boolean)[0] || null;
  11. return seg && isValidBranchParam(seg) ? seg : null;
  12. }
  13. export function isSearchRoutePathname(pathname) {
  14. if (typeof pathname !== "string" || !pathname.startsWith("/")) return false;
  15. const parts = pathname.split("/").filter(Boolean);
  16. return parts.length >= 2 && parts[1] === "search";
  17. }
  18. export function replaceBranchInPathnameOrFallback(pathname, nextBranch) {
  19. const current = readRouteBranchFromPathname(pathname);
  20. if (!current) return branchPath(nextBranch);
  21. const parts = pathname.split("/").filter(Boolean);
  22. if (parts.length === 0) return branchPath(nextBranch);
  23. parts[0] = nextBranch;
  24. return `/${parts.join("/")}`;
  25. }
  26. export function buildNextSearchQueryString({
  27. currentSearch,
  28. currentRouteBranch,
  29. nextBranch,
  30. }) {
  31. const sp = new URLSearchParams(currentSearch || "");
  32. const parsed = parseSearchUrlState(sp, { routeBranch: currentRouteBranch });
  33. const nextState =
  34. parsed.scope === SEARCH_SCOPE.SINGLE
  35. ? { ...parsed, branch: nextBranch }
  36. : parsed;
  37. const qs = serializeSearchUrlState(nextState);
  38. return qs ? `?${qs}` : "";
  39. }
  40. export function buildNextUrlForBranchSwitch({ pathname, search, nextBranch }) {
  41. if (!isValidBranchParam(nextBranch)) return null;
  42. const currentRouteBranch = readRouteBranchFromPathname(pathname);
  43. // Outside a branch route -> go to branch root
  44. if (!currentRouteBranch) return branchPath(nextBranch);
  45. const nextPathname = replaceBranchInPathnameOrFallback(pathname, nextBranch);
  46. const nextSearch = isSearchRoutePathname(pathname)
  47. ? buildNextSearchQueryString({
  48. currentSearch: search,
  49. currentRouteBranch,
  50. nextBranch,
  51. })
  52. : search || "";
  53. return `${nextPathname}${nextSearch}`;
  54. }
  55. export function safeReadLocalStorageBranch(storageKey) {
  56. if (typeof window === "undefined") return null;
  57. try {
  58. const raw = window.localStorage.getItem(String(storageKey || ""));
  59. return raw && isValidBranchParam(raw) ? raw : null;
  60. } catch {
  61. return null;
  62. }
  63. }
  64. export function safeWriteLocalStorageBranch(storageKey, branch) {
  65. if (typeof window === "undefined") return;
  66. try {
  67. window.localStorage.setItem(String(storageKey || ""), String(branch));
  68. } catch {
  69. // ignore
  70. }
  71. }