Skip to content

Commit

Permalink
feat: added the pagination for user query (#198)
Browse files Browse the repository at this point in the history
  • Loading branch information
nimish-gupta authored and matthiaskern committed Nov 4, 2018
1 parent 864796c commit f7d74a7
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 84 deletions.
8 changes: 8 additions & 0 deletions client/shared/Utils.re
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,11 @@ let joinWithComma =
acc ++ ", " ++ str;
}
);

module Object = {
[@bs.val]
external assign3: (Js.t({..}), Js.t({..}), Js.t({..})) => Js.t({..}) =
"Object.assign";
let merge = (o1: Js.t({..}), o2: Js.t({..})) =>
assign3(Js.Obj.empty(), o1, o2);
};
2 changes: 2 additions & 0 deletions client/src/Config.re
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ let graphqlEndpoint =
let anonymousUserId = "anonymous";

let cmTheme = "rtop";

let sketchListLimit = 30;
93 changes: 80 additions & 13 deletions client/src/User.re
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,111 @@ open Utils;

module GetNotes = [%graphql
{|
query getNotes($userName: String!) {
note(
where: {owner: {username: {_eq: $userName}}}
order_by: updated_at_desc
query getNotes($userName: String!, $limit: Int, $offset: Int) {
note(
where: {owner: {username: {_eq: $userName}}}
order_by: updated_at_desc
limit: $limit,
offset: $offset
) {
id
title
date: updated_at
}
}
|}
|}
];

module GetNotesComponent = ReasonApollo.CreateQuery(GetNotes);

let component = ReasonReact.statelessComponent("User");
let getNotesQuery = (~userName, ~offset=0, ()) =>
GetNotes.make(~userName, ~offset, ~limit=Config.sketchListLimit, ());

external unsafeFromJson: Js.Json.t => GetNotes.t = "%identity";
external unsafeToJson: GetNotes.t => Js.Json.t = "%identity";

let updateQuery = (prev, next) => {
let prev = unsafeFromJson(prev);
let fetchMoreResult = ReasonApolloQuery.fetchMoreResultGet(next);

(
switch (fetchMoreResult) {
| None => prev
| Some(moreNotes) =>
let moreNotes = unsafeFromJson(moreNotes);
let newNotes = {
"note": Belt.Array.concat(prev##note, moreNotes##note),
};
Utils.Object.merge(prev, newNotes);
}
)
|> unsafeToJson;
};

let shouldFetchMore = (note, count) =>
Array.length(note) >= Config.sketchListLimit * count;

type state = {count: int};
type action =
| ChangeCount;

let initialState = () => {count: 1};

let reducer = (action, state) =>
switch (action) {
| ChangeCount => ReasonReact.Update({count: 1 + state.count})
};

let component = ReasonReact.reducerComponent("User");
let make = (~userName, _children) => {
...component,
render: _self => {
let notesQuery = GetNotes.make(~userName, ());
initialState,
reducer,
render: self => {
let notesQuery = getNotesQuery(~userName, ());
<section className="Layout__center User">
<h1> {j|$(userName)'s sketches|j}->str </h1>
<GetNotesComponent
fetchPolicy="network-only" variables=notesQuery##variables>
...{
({result}) =>
({result, fetchMore}) =>
switch (result) {
| Loading =>
<div style={ReactDOMRe.Style.make(~width="500px", ())}>
<UI_SketchList.Placeholder />
</div>
| Error(error) => error##message->str
| Data(response) =>
<UI_SketchList
sketches=response##note
noSketches={<UI_NoSketches />}
/>
let notesQuery =
getNotesQuery(
~userName,
~offset=Array.length(response##note),
(),
);
let fetchMore = _e =>
Js.Promise.(
fetchMore(
~variables=notesQuery##variables,
~updateQuery,
(),
)
|> then_(_ => {
self.send(ChangeCount);
resolve();
})
)
|> ignore;

shouldFetchMore(response##note, self.state.count) ?
<UI_SketchList
sketches=response##note
noSketches={<UI_NoSketches />}
fetchMore
/> :
<UI_SketchList
sketches=response##note
noSketches={<UI_NoSketches />}
/>;
}
}
</GetNotesComponent>
Expand Down
163 changes: 92 additions & 71 deletions client/src/components/UI_SketchList.re
Original file line number Diff line number Diff line change
Expand Up @@ -15,85 +15,106 @@ module DeleteNoteComponent = ReasonApollo.CreateMutation(DeleteNote);
let component = ReasonReact.statelessComponent("UI_SketchList");

let make =
(~sketches, ~className=?, ~noSketches="No sketches"->str, _children) => {
(
~sketches,
~className=?,
~noSketches="No sketches"->str,
~fetchMore=?,
_children,
) => {
...component,
render: _self =>
switch (sketches) {
| [||] => <div className={Cn.unwrap(className)}> noSketches </div>
| sketches =>
<ul className={Cn.make(["UI_SketchList", Cn.unwrap(className)])}>
...sketches
->(
Belt.Array.mapU((. sketch) =>
<li className="UI_SketchList__sketch">
<Router.Link
className="UI_SketchList__sketch--link"
route={Route.Note({noteId: sketch##id, data: None})}>
<span className="UI_SketchList__sketch--title">
{
switch (sketch##title) {
| None
| Some("") => "untitled sketch"->str
| Some(title) => title->str
<>
<ul className={Cn.make(["UI_SketchList", Cn.unwrap(className)])}>
...sketches
->(
Belt.Array.mapU((. sketch) =>
<li className="UI_SketchList__sketch">
<Router.Link
className="UI_SketchList__sketch--link"
route={Route.Note({noteId: sketch##id, data: None})}>
<span className="UI_SketchList__sketch--title">
{
switch (sketch##title) {
| None
| Some("") => "untitled sketch"->str
| Some(title) => title->str
}
}
}
</span>
</Router.Link>
<div className="UI_SketchList__sketch--lastEdited">
"last edited"->str
<UI_DateTime
date=sketch##date
className="UI_SketchList__sketch--time"
/>
</div>
<DeleteNoteComponent>
...(
(mutation, _) =>
<button
className="btn UI_SketchList__sketch--delete"
onClick={
_event => {
let continue =
Webapi.Dom.(
Window.confirm(
"Are you sure you want to delete this sketch?",
window,
)
);
</span>
</Router.Link>
<div className="UI_SketchList__sketch--lastEdited">
"last edited"->str
<UI_DateTime
date=sketch##date
className="UI_SketchList__sketch--time"
/>
</div>
<DeleteNoteComponent>
...(
(mutation, _) =>
<button
className="btn UI_SketchList__sketch--delete"
onClick={
_event => {
let continue =
Webapi.Dom.(
Window.confirm(
"Are you sure you want to delete this sketch?",
window,
)
);

if (continue) {
let deleteNoteQuery =
DeleteNote.make(~noteId=sketch##id, ());
if (continue) {
let deleteNoteQuery =
DeleteNote.make(
~noteId=sketch##id,
(),
);

Js.Promise.(
mutation(
~variables=deleteNoteQuery##variables,
~refetchQueries=[|"getNotes"|],
(),
)
|> then_(_response => {
Notify.info("Note was deleted.");
resolve();
})
|> catch(err => {
Notify.error(
"Note failed to delete.",
);
logError(err)->resolve;
})
|> ignore
);
};
}
}>
"Delete"->str
</button>
)
</DeleteNoteComponent>
</li>
Js.Promise.(
mutation(
~variables=
deleteNoteQuery##variables,
~refetchQueries=[|"getNotes"|],
(),
)
|> then_(_response => {
Notify.info("Note was deleted.");
resolve();
})
|> catch(err => {
Notify.error(
"Note failed to delete.",
);
logError(err)->resolve;
})
|> ignore
);
};
}
}>
"Delete"->str
</button>
)
</DeleteNoteComponent>
</li>
)
)
)
</ul>
</ul>
{
switch (fetchMore) {
| None => ReasonReact.null
| Some(fetchMore) =>
<button className="btn btn-primary" onClick=fetchMore>
"Load more sketches"->str
</button>
}
}
</>
},
};

Expand Down Expand Up @@ -162,4 +183,4 @@ module Placeholder = {
<circle cx="12" cy="177" r="12" />
</ReactContentLoader>,
};
};
};

0 comments on commit f7d74a7

Please sign in to comment.