summaryrefslogtreecommitdiff
path: root/bs5/universal/native/shared/Router.re
blob: 7b814a6f43af2b1b2b68d7ffa464dbfe48120c1e (plain)
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
let home = "/";
let demoRenderToStaticMarkup = "/demo/render-to-static-markup";
let demoRenderToString = "/demo/render-to-string";
let demoRenderToStream = "/demo/render-to-stream";
let demoServerOnlyRSC = "/demo/server-only-rsc";
let demoSinglePageRSC = "/demo/single-page-rsc";
let demoRouterRSC = "/demo/router-rsc";

let links = [|
  ("Server side render to string (renderToString)", demoRenderToString),
  (
    "Server side render to static markup (renderToStaticMarkup)",
    demoRenderToStaticMarkup,
  ),
  ("Server side render to stream (renderToStream)", demoRenderToStream),
  (
    "React Server components without client (createFromFetch)",
    demoServerOnlyRSC,
  ),
  (
    "React Server components with createFromReadableStream (RSC + SSR)",
    demoSinglePageRSC,
  ),
  (
    "React Server components with single page router (createFromFetch + createFromReadableStream)",
    demoRouterRSC,
  ),
|];

module Menu = {
  [@react.component]
  let make = () => {
    <ul className="flex flex-col gap-4">
      {links
       |> Array.map(((title, href)) =>
            <li> <Link.WithArrow href> title </Link.WithArrow> </li>
          )
       |> React.array}
    </ul>;
  };
};
type location = {
  selectedId: option(int),
  isEditing: bool,
  searchText: option(string),
};

let locationToString = location =>
  [
    switch (location.selectedId) {
    | Some(id) => "selectedId=" ++ Int.to_string(id)
    | None => ""
    },
    "isEditing=" ++ (location.isEditing ? "true" : "false"),
    switch (location.searchText) {
    | Some(text) => "searchText=" ++ text
    | None => ""
    },
  ]
  |> List.filter(s => s != "")
  |> String.concat("&");

let initialLocation = {
  selectedId: None,
  isEditing: false,
  searchText: None,
};

let locationFromString = str => {
  switch (URL.make(str)) {
  | Some(url) =>
    let searchParams = URL.searchParams(url);
    let selectedId =
      URL.SearchParams.get(searchParams, "selectedId")
      |> Option.map(id => int_of_string(id));
    let searchText = URL.SearchParams.get(searchParams, "searchText");

    let isEditing =
      URL.SearchParams.get(searchParams, "isEditing")
      |> Option.map(v =>
           switch (v) {
           | "true" => true
           | "false" => false
           | _ => false
           }
         )
      |> Option.value(~default=false);

    {
      selectedId,
      isEditing,
      searchText,
    };

  | None => initialLocation
  };
};

type payload = {
  body: string,
  title: string,
};

/* 'a is melange-fetch's response in melange */
type t('a) = {
  location,
  navigate: location => unit,
  useAction: (string, string) => ((payload, location, unit) => unit, bool),
  refresh: option('a) => unit,
};

let useRouter = () => {
  location: initialLocation,
  navigate: _ => (),
  useAction: (_, _) => ((_, _, _) => (), false),
  refresh: _ => (),
};