File size: 3,709 Bytes
a417977
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import {createContext} from "@/utils/context"
import {Topic} from "@/contexts/topics";

type Route = {
    name: keyof typeof routes,
    args: any[],
    location: string,
}

const matchers: {[key: string]: (location: string) => any[] | null} = {};

function constructRoute<A>(
    name: keyof typeof routes,
    generateLocation: (...args: A) => string,
    matcher: (location: string) => A | null,
): (...args: A) => Route {
    matchers[name] = matcher;
    return (...args) => ({
        name,
        args,
        location: generateLocation(...args),
    } satisfies Route);
}

export const routes = {
    home: constructRoute(
        "home",
        (page: number) => {
            // console.log(page)
            if(page < 0) {
                throw new Error("Page not found");
            }
            return page ? `/${page + 1}` : "/"
        },
        (location) => {
            // console.log(location)
            if(location === "/") {
                return [0];
            }
            const match = location.match(/^\/(\d+)/);
            // console.log([Math.max(parseInt(match[1]) - 1, 0)]);
            return match ? [Math.max(parseInt(match[1]) - 1, 0)] : null;
        },
        // (location) => location === "/" ? [] : null,
    ),
    topic: constructRoute(
        "topic",
        (id: string, page: number) => `/topic/${id}/${page}`,
        (location) => {
            const match = location.match(/^\/topic\/(.+)\/(\d+)/);
            return match ? [match[1], Math.max(parseInt(match[2]) - 1, 1)] : null;
        },
    ),
    settings: constructRoute(
        "settings",
        () => "/settings",
        (location) => location === "/settings" ? [] : null,
    ),
} as const satisfies {
    [key: string]: (...args: any[]) => Route
};

function matchBrowserLocation(): Route {
    for (const [name, matcher] of Object.entries(matchers)) {
        const location = window.location.pathname + window.location.search;
        const args = matcher(location);
        if (args !== null) {
            return routes[name as keyof typeof routes](...args);
        }
    }
    return routes.home(0);
}

const history: Route[] = [];
let historyIndex = 0;

export const routeCtx = createContext({
    initialValue: matchBrowserLocation,
    controllers: (route: Route, setRoute) => ({
        onMount: () => {
            // console.log("add listener")
            // Add initial route to history
            history.push(route);
            historyIndex = history.length - 1;
            window.addEventListener('popstate', () => {
                setRoute(matchBrowserLocation());
            });
        },
        effect: () => {
            const location = route.location;
            if (location !== `${window.location.pathname}${window.location.search}`) {
                history.push(route);
                historyIndex = history.length - 1;
                window.history.pushState({}, "", location);
            }
        },
        actions: {
            goBack: () => {
                // console.log(history);
                // console.log(historyIndex);
                if (historyIndex > 0) {
                    historyIndex--;
                    setRoute(history[historyIndex]);
                } else {
                    setRoute(routes.home(0));
                }
            },
            getHistoryIndex: () => historyIndex,
            // goBack: (route: Route, setRoute) => {
            //     if (historyIndex > 0) {
            //         historyIndex--;
            //
            //     }
            // }
            // goForward: () => {
            //     if (historyIndex < history.length - 1) {}
            // }
        }
    })
})