Skip to main content

max / goingson

3.3 KB · 108 lines History Blame Raw
1 /**
2 * GoingsOn - Router Module
3 * URL routing with History API for bookmarking, back button, and deep links.
4 *
5 * URL Structure:
6 * /projects -> Projects list
7 * /project/{id} -> Project dashboard
8 * /tasks -> Tasks list
9 * /events -> Events list
10 * /emails -> Emails list
11 * /day-plan -> Day planning
12 */
13
14 (function() {
15 'use strict';
16
17 // Flag to prevent recursive URL pushes when handling popstate
18 let suppressPush = false;
19
20 // Standard view mappings
21 const VIEW_ROUTES = {
22 'work': 'tasks', // tab alias
23 'time': 'day-plan', // tab alias
24 'messages': 'emails', // tab alias
25 'projects': 'projects',
26 'tasks': 'tasks',
27 'events': 'events',
28 'emails': 'emails',
29 'contacts': 'contacts',
30 'day-plan': 'day-plan',
31 'weekly-review': 'weekly-review',
32 'monthly-review': 'monthly-review',
33 };
34
35 /**
36 * Navigate to a URL, optionally replacing current history entry.
37 * @param {string} path - The path to navigate to (e.g., '/tasks')
38 * @param {boolean} replace - If true, replace current entry instead of push
39 */
40 function navigate(path, replace = false) {
41 if (suppressPush) return;
42
43 const method = replace ? 'replaceState' : 'pushState';
44 history[method]({ path }, '', path);
45 }
46
47 /**
48 * Handle a route change (from URL or popstate event).
49 * @param {string} pathname - The URL pathname
50 * @param {string} search - The URL search string (query params)
51 */
52 function handleRoute(pathname, search) {
53 suppressPush = true;
54 try {
55 // Remove leading slash and get base path
56 const path = pathname.replace(/^\//, '') || 'tasks';
57 const query = Object.fromEntries(new URLSearchParams(search));
58
59 // Project dashboard: /project/{id}
60 if (path.startsWith('project/')) {
61 const id = path.split('/')[1];
62 if (id && typeof GoingsOn.projects?.open === 'function') {
63 GoingsOn.projects.open(id);
64 }
65 return;
66 }
67
68 // Standard views
69 const viewName = VIEW_ROUTES[path];
70 if (viewName) {
71 if (typeof GoingsOn.navigation?.switchView === 'function') {
72 GoingsOn.navigation.switchView(viewName);
73 }
74 return;
75 }
76
77 // Unknown route - redirect to tasks
78 navigate('/tasks', true);
79 if (typeof GoingsOn.navigation?.switchView === 'function') {
80 GoingsOn.navigation.switchView('tasks');
81 }
82 } finally {
83 suppressPush = false;
84 }
85 }
86
87 /**
88 * Initialize the router - handle current URL on page load.
89 */
90 function init() {
91 handleRoute(location.pathname, location.search);
92 }
93
94 // Listen for browser back/forward navigation
95 window.addEventListener('popstate', (e) => {
96 handleRoute(location.pathname, location.search);
97 });
98
99 // ============ Populate GoingsOn.router Namespace ============
100
101 GoingsOn.router = {
102 navigate,
103 init,
104 get suppressPush() { return suppressPush; },
105 };
106
107 })();
108