From 65ac3033082876a5cb7b6e77f8e25f860880a444 Mon Sep 17 00:00:00 2001 From: Simon Backx Date: Tue, 5 Jul 2022 14:24:29 +0200 Subject: [PATCH] Added basic pagination and API mocking --- apps/comments-ui/public/main.css | 5 +- apps/comments-ui/src/App.js | 24 +++++++-- apps/comments-ui/src/actions.js | 14 +++++ apps/comments-ui/src/components/Avatar.js | 2 +- apps/comments-ui/src/components/Comment.js | 10 ++-- .../comments-ui/src/components/CommentsBox.js | 17 +++++- apps/comments-ui/src/components/Form.js | 13 +---- apps/comments-ui/src/components/Pagination.js | 42 ++++++++++++--- apps/comments-ui/src/index.js | 7 +-- apps/comments-ui/src/utils/api.js | 54 ++++++++++++++++++- 10 files changed, 155 insertions(+), 33 deletions(-) diff --git a/apps/comments-ui/public/main.css b/apps/comments-ui/public/main.css index 5566a1f97f..b3be2cfc06 100644 --- a/apps/comments-ui/public/main.css +++ b/apps/comments-ui/public/main.css @@ -609,11 +609,11 @@ video { } .rounded-md { - border-radius: 0.6rem; + border-radius: 0.375rem; } .rounded { - border-radius: 0.4rem; + border-radius: 0.25rem; } .border { @@ -701,3 +701,4 @@ body { all: initial; } + diff --git a/apps/comments-ui/src/App.js b/apps/comments-ui/src/App.js index 1288fe37c2..d2de98630d 100644 --- a/apps/comments-ui/src/App.js +++ b/apps/comments-ui/src/App.js @@ -33,8 +33,10 @@ export default class App extends React.Component { initStatus: 'running', member: null, comments: null, + pagination: null, popupNotification: null, - customSiteUrl: props.customSiteUrl + customSiteUrl: props.customSiteUrl, + postCommentId: props.postCommentId }; } @@ -47,11 +49,15 @@ export default class App extends React.Component { try { // Fetch data from API, links, preview, dev sources const {site, member} = await this.fetchApiData(); + const {comments, pagination} = await this.fetchComments(); + const state = { site, member, action: 'init:success', - initStatus: 'success' + initStatus: 'success', + comments, + pagination }; this.setState(state); @@ -118,6 +124,16 @@ export default class App extends React.Component { } } + /** Fetch first few comments */ + async fetchComments() { + const data = this.GhostApi.comments.browse({page: 1}); + + return { + comments: data.comments, + pagination: data.meta.pagination + }; + } + /** Setup Sentry */ setupSentry({site}) { if (hasMode(['test'])) { @@ -146,12 +162,14 @@ export default class App extends React.Component { /**Get final App level context from App state*/ getContextFromState() { - const {action, popupNotification, customSiteUrl, member} = this.state; + const {action, popupNotification, customSiteUrl, member, comments, pagination} = this.state; return { action, popupNotification, customSiteUrl, member, + comments, + pagination, onAction: (_action, data) => this.dispatchAction(_action, data) }; } diff --git a/apps/comments-ui/src/actions.js b/apps/comments-ui/src/actions.js index 18fb211a9b..b786a3ad10 100644 --- a/apps/comments-ui/src/actions.js +++ b/apps/comments-ui/src/actions.js @@ -1,5 +1,19 @@ +function loadMoreComments({state, api}) { + let page = 1; + if (state.pagination && state.pagination.page) { + page = state.pagination.page + 1; + } + const data = api.comments.browse({page}); + + return { + comments: [...data.comments, ...state.comments], + pagination: data.meta.pagination + }; +} + const Actions = { // Put your actions here + loadMoreComments }; /** Handle actions in the App, returns updated state */ diff --git a/apps/comments-ui/src/components/Avatar.js b/apps/comments-ui/src/components/Avatar.js index 646b8cafd0..3006f668a9 100644 --- a/apps/comments-ui/src/components/Avatar.js +++ b/apps/comments-ui/src/components/Avatar.js @@ -1,6 +1,6 @@ function Avatar() { return ( -
+

JW

); diff --git a/apps/comments-ui/src/components/Comment.js b/apps/comments-ui/src/components/Comment.js index 4aa0371eab..e14fc53466 100644 --- a/apps/comments-ui/src/components/Comment.js +++ b/apps/comments-ui/src/components/Comment.js @@ -3,21 +3,23 @@ import Like from './Like'; import Reply from './Reply'; import More from './More'; -function Comment() { +function Comment(props) { + const comment = props.comment; + return (
-

Someone's Name

-
Someone's Bio
+

{comment.member.name}

+
{comment.member.bio}
2 mins ago
-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut mollis erat vitae diam gravida accumsan vitae quis nisl. Donec luctus laoreet mauris, nec posuere turpis accumsan in. Proin sagittis magna quis vulputate tempus. Duis sagittis purus mattis enim condimentum, quis tempus est tristique.

+

{comment.html}

diff --git a/apps/comments-ui/src/components/CommentsBox.js b/apps/comments-ui/src/components/CommentsBox.js index 765a3ec30d..784f5a7025 100644 --- a/apps/comments-ui/src/components/CommentsBox.js +++ b/apps/comments-ui/src/components/CommentsBox.js @@ -2,6 +2,9 @@ import React from 'react'; import AppContext from '../AppContext'; import NotSignedInBox from './NotSignedInBox'; import Form from './Form'; +import TotalComments from './TotalComments'; +import Comment from './Comment'; +import Pagination from './Pagination'; class CommentsBox extends React.Component { static contextType = AppContext; @@ -12,9 +15,21 @@ class CommentsBox extends React.Component { } render() { + const comments = !this.context.comments ? 'Loading...' : this.context.comments.map(comment => ); + return (
- { this.context.member ?
: } +
+

Members discussion

+ +
+ +
+ {comments} +
+
+ { this.context.member ? : } +
); } diff --git a/apps/comments-ui/src/components/Form.js b/apps/comments-ui/src/components/Form.js index 62fa0b0622..3ee8f8614c 100644 --- a/apps/comments-ui/src/components/Form.js +++ b/apps/comments-ui/src/components/Form.js @@ -1,8 +1,5 @@ import React from 'react'; import AppContext from '../AppContext'; -import TotalComments from './TotalComments'; -import Comment from './Comment'; -import Pagination from './Pagination'; class Form extends React.Component { static contextType = AppContext; @@ -61,15 +58,7 @@ class Form extends React.Component { render() { return ( -
-

Members discussion

- -
- -
- - -
+
diff --git a/apps/comments-ui/src/components/Pagination.js b/apps/comments-ui/src/components/Pagination.js index 02c7bd23a1..82e4a6dd0d 100644 --- a/apps/comments-ui/src/components/Pagination.js +++ b/apps/comments-ui/src/components/Pagination.js @@ -1,9 +1,39 @@ -function Pagination() { - return ( -
- Show 32 more comments -
- ); +import React from 'react'; +import AppContext from '../AppContext'; + +class Pagination extends React.Component { + static contextType = AppContext; + + constructor(props) { + super(props); + this.state = { + message: '' + }; + + this.loadMore = this.loadMore.bind(this); + } + + loadMore() { + this.context.onAction('loadMoreComments'); + } + + render() { + if (!this.context.pagination) { + return null; + } + + const left = this.context.pagination.total - this.context.pagination.page * this.context.pagination.limit; + + if (left <= 0) { + return null; + } + + return ( +
+ Show {left} more comments +
+ ); + } } export default Pagination; diff --git a/apps/comments-ui/src/index.js b/apps/comments-ui/src/index.js index 277fb910b0..ef9b1bdf1c 100644 --- a/apps/comments-ui/src/index.js +++ b/apps/comments-ui/src/index.js @@ -32,7 +32,8 @@ function getSiteData() { const apiKey = scriptTag.dataset.key; const apiUrl = scriptTag.dataset.api; const sentryDsn = scriptTag.dataset.sentryDsn; - return {siteUrl, apiKey, apiUrl, sentryDsn}; + const postCommentId = scriptTag.dataset.commentId; + return {siteUrl, apiKey, apiUrl, sentryDsn, postCommentId}; } return {}; } @@ -52,12 +53,12 @@ function setup({siteUrl}) { function init() { // const customSiteUrl = getSiteUrl(); - const {siteUrl: customSiteUrl, sentryDsn} = getSiteData(); + const {siteUrl: customSiteUrl, sentryDsn, postCommentId} = getSiteData(); const siteUrl = customSiteUrl || window.location.origin; setup({siteUrl}); ReactDOM.render( - {} + {} , document.getElementById(ROOT_DIV_ID) ); diff --git a/apps/comments-ui/src/utils/api.js b/apps/comments-ui/src/utils/api.js index 9e449709fe..8d357dfff0 100644 --- a/apps/comments-ui/src/utils/api.js +++ b/apps/comments-ui/src/utils/api.js @@ -2,11 +2,15 @@ import {transformApiSiteData} from './helpers'; function setupGhostApi({siteUrl = window.location.origin, apiUrl, apiKey}) { const apiPath = 'members/api'; + const commentsApiPath = 'comments/api'; - function endpointFor({type, resource}) { + function endpointFor({type, resource, params = ''}) { if (type === 'members') { return `${siteUrl.replace(/\/$/, '')}/${apiPath}/${resource}/`; } + if (type === 'comments') { + return `${siteUrl.replace(/\/$/, '')}/${commentsApiPath}/${resource}/${params}`; + } } function contentEndpointFor({resource, params = ''}) { @@ -75,6 +79,54 @@ function setupGhostApi({siteUrl = window.location.origin, apiUrl, apiKey}) { }; + api.comments = { + browse({page}) { + const limit = 15; + const comments = (new Array(limit)).fill().map(() => { + return { + id: 'comment-' + Math.random() * 10000 + Date.now(), + member: { + avatar: '', + bio: 'CEO', + name: 'Just a name' + }, + html: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut mollis erat vitae diam gravida accumsan vitae quis nisl. Donec luctus laoreet mauris, nec posuere turpis accumsan in. Proin sagittis magna quis vulputate tempus. Duis sagittis purus mattis enim condimentum, quis tempus est tristique.' + }; + }); + + // Temporary placeholder until we have a proper API + return { + comments, + meta: { + pagination: { + page: page, + limit, + pages: 3, + total: 15 * 3, + next: null, + prev: null + } + } + }; + /* + const url = endpointFor({type: 'comments', resource: 'comment', params: '?limit=15&page='+page}); + return makeRequest({ + url, + method: 'GET', + headers: { + 'Content-Type': 'application/json' + }, + credentials: 'same-origin' + }).then(function (res) { + if (res.ok) { + return res.json(); + } else { + throw new Error('Failed to fetch comments'); + } + });*/ + } + }; + api.init = async () => { let [member] = await Promise.all([ api.member.sessionData()