Created
October 11, 2018 21:46
-
-
Save mithereal/2b145e1fe5136abb945472844b7cc363 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type webData('a) = RemoteData.t('a, 'a, string); | |
type language = { | |
name: option(string), | |
count:int | |
} | |
type repo = { | |
id: int, | |
owner: string, | |
name: string, | |
full_name: string, | |
stars: int, | |
language: option(string), | |
html_url: string, | |
description: option(string), | |
fork: bool, | |
}; | |
type action = | |
| Init | |
| Loading | |
| ReposLoaded(list(repo)) | |
| ReposError(string); | |
type state = { | |
username: string, | |
user: string, | |
languages: list(option(language)), | |
repos: webData(list(repo)), | |
}; | |
let forkPath = "M8 1a1.993 1.993 0 0 0-1 3.72V6L5 8 3 6V4.72A1.993 1.993 0 0 0 2 1a1.993 1.993 0 0 0-1 3.72V6.5l3 3v1.78A1.993 1.993 0 0 0 5 15a1.993 1.993 0 0 0 1-3.72V9.5l3-3V4.72A1.993 1.993 0 0 0 8 1zM2 4.2C1.34 4.2.8 3.65.8 3c0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zm3 10c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2zm3-10c-.66 0-1.2-.55-1.2-1.2 0-.65.55-1.2 1.2-1.2.65 0 1.2.55 1.2 1.2 0 .65-.55 1.2-1.2 1.2z"; | |
let svgPath = "M15.2 40.6c-.2 0-.4-.1-.6-.2-.4-.3-.5-.7-.4-1.1l3.9-12-10.2-7.5c-.4-.3-.5-.7-.4-1.1s.5-.7 1-.7h12.7L25 5.9c.1-.4.5-.7 1-.7s.8.3 1 .7L30.9 18h12.7c.4 0 .8.2 1 .6s0 .9-.4 1.1L34 27.1l3.9 12c.1.4 0 .9-.4 1.1s-.8.3-1.2 0L26 33l-10.2 7.4c-.2.1-.4.2-.6.2zM26 30.7c.2 0 .4.1.6.2l8.3 6.1-3.2-9.8c-.1-.4 0-.9.4-1.1l8.3-6.1H30.1c-.4 0-.8-.3-1-.7L26 9.5l-3.2 9.8c-.1.4-.5.7-1 .7H11.5l8.3 6.1c.4.3.5.7.4 1.1L17.1 37l8.3-6.1c.2-.1.4-.2.6-.2z"; | |
let component = ReasonReact.reducerComponent("App"); | |
let renderDesc = desc => | |
switch (desc) { | |
| Some(str) => str | |
| None => "No desciption :(" | |
}; | |
let repoItems = (user, repos) => | |
repos | |
|> List.map(repo => | |
<li | |
key=(string_of_int(repo.id)) | |
className="flex flex-row justify-between w-full border-b"> | |
<div className="px-6 py-4"> | |
<div | |
className="flex flex-row items-center font-mono text-base mb-2"> | |
<a | |
href=repo.html_url | |
target="_blank" | |
className="no-underline text-grey-dark mr-2"> | |
( | |
ReasonReact.string( | |
repo.owner == user ? repo.name : repo.full_name, | |
) | |
) | |
</a> | |
( | |
repo.fork ? | |
<svg | |
className="fill-current text-green-dark h-4 w-4" | |
viewBox="0 0 10 16"> | |
<path fillRule="evenodd" d=forkPath /> | |
</svg> : | |
ReasonReact.null | |
) | |
</div> | |
<p className="font-mono text-grey text-xs"> | |
(repo.description |> renderDesc |> ReasonReact.string) | |
</p> | |
</div> | |
<div className="flex flex-row items-center px-6 py-4"> | |
<span | |
className="font-mono inline-block rounded-full px-1 py-1 text-sm text-grey-dark mr-1"> | |
(ReasonReact.string(string_of_int(repo.stars))) | |
</span> | |
<svg | |
className="fill-current text-orange-light h-4 w-4 inline-block" | |
viewBox="0 0 50 50"> | |
<path d=svgPath /> | |
</svg> | |
</div> | |
</li> | |
); | |
let fetchRepos = ({ReasonReact.state, send}) => { | |
Js.Promise.( | |
Fetch.fetch( | |
"https://api.github.com/users/" | |
++ state.username | |
++ "/repos?type=all&sort=updated&per_page=500", | |
) | |
|> then_(Fetch.Response.json) | |
|> then_(json => | |
json | |
|> Json.Decode.array(json => | |
Json.Decode.{ | |
id: json |> field("id", int), | |
language: json |> optional(field("language", string)), | |
owner: | |
json | |
|> field("owner", owner => owner |> field("login", string)), | |
name: json |> field("name", string), | |
full_name: json |> field("full_name", string), | |
stars: json |> field("stargazers_count", int), | |
html_url: json |> field("html_url", string), | |
description: json |> optional(field("description", string)), | |
fork: json |> field("fork", bool), | |
} | |
) | |
|> Array.to_list | |
|> ( | |
repos => { | |
send(ReposLoaded(repos)); | |
resolve(); | |
} | |
) | |
) | |
|> ignore | |
); | |
send(Loading); | |
}; | |
let countOf = (data, value) => { | |
let rec helper = (acc, data) => { | |
switch (data) { | |
| [] => acc | |
| [x, ...xs] => if (x == value) { | |
helper(acc + 1, xs); | |
} else { helper(acc, xs);} | |
} | |
}; | |
helper(0, data); | |
}; | |
let rec removeEmpties = (xs) => | |
switch (xs) { | |
| [Some(x), ...xs] => [x, ...removeEmpties(xs)] | |
| [None, ...xs] => removeEmpties(xs) | |
| [] => [] | |
}; | |
let removeEmptiesAndSort = (xs) => | |
xs |> removeEmpties |> List.sort_uniq(compare); | |
let removeEmpties = (xs) => | |
xs |> removeEmpties |> List.sort(compare); | |
let make = _children => { | |
...component, | |
initialState: () => {username: "mithereal", user: "", repos: RemoteData.NotAsked, languages: []}, | |
reducer: (action, state) => | |
switch (action) { | |
| Init => | |
ReasonReact.SideEffects((self => fetchRepos(self))) | |
| Loading => | |
let existingData = | |
switch (state.repos) { | |
| NotAsked | |
| Loading(_) | |
| Failure(_) => [] | |
| Success(s) => s | |
}; | |
ReasonReact.Update({ | |
...state, | |
user: state.username, | |
repos: RemoteData.Loading(existingData), | |
}); | |
| ReposLoaded(repos) => | |
let langlist = List.map((repo: repo) => | |
repo.language | |
, repos); | |
let uniqlist = removeEmptiesAndSort(langlist); | |
let modified_list = removeEmpties(langlist); | |
Js.log(uniqlist); | |
let languages = List.map((ul: option(string)) => | |
Some({ | |
name: ul, | |
count: countOf(modified_list, ul) | |
}), | |
uniqlist, | |
); | |
ReasonReact.Update({...state, repos: RemoteData.Success(repos), languages: languages}) | |
| ReposError(err) => | |
ReasonReact.Update({...state, repos: RemoteData.Failure(err)}) | |
}, | |
didMount: self => { | |
self.send(Init) | |
}, | |
render: ({state, send}) => | |
<div | |
className="h-screen w-full bg-grey-darker flex flex-col justify-start items-center overflow-scroll"> | |
( | |
switch (state.repos) { | |
| NotAsked => ReasonReact.null | |
| Failure(e) => <p> (ReasonReact.string(e)) </p> | |
| Loading(repos) | |
| Success(repos) => | |
let isLoading = RemoteData.isLoading(state.repos); | |
<> | |
( | |
if (isLoading) { | |
<p className="mt-8 font-mono text-grey text-lg"> | |
(ReasonReact.string("Loading...")) | |
</p>; | |
} else { | |
ReasonReact.null; | |
} | |
) | |
( | |
if (List.length(repos) > 0) { | |
<div | |
className="bg-white shadow rounded flex overflow-scroll w-2/5 mb-8 mt-8"> | |
<ul | |
className="appearance-none p-0 w-full text-grey-darker border rounded"> | |
( | |
ReasonReact.array( | |
repos |> repoItems(state.user) |> Array.of_list, | |
) | |
) | |
</ul> | |
</div>; | |
} else if (! isLoading) { | |
<p className="mt-8 font-mono text-grey text-lg"> | |
( | |
ReasonReact.string( | |
state.user ++ " " ++ "does not have any public repos", | |
) | |
) | |
</p>; | |
} else { | |
ReasonReact.null; | |
} | |
) | |
</>; | |
} | |
) | |
</div>, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment