Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
scratch-www
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
xpstem
scratch-www
Commits
7eb583a8
Unverified
Commit
7eb583a8
authored
Apr 01, 2021
by
Karishma Chadha
Committed by
GitHub
Apr 01, 2021
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #5217 from kchadha/studio-comments-fetching
Studio Comments Fetching & Initial UI
parents
23027f0b
6026eded
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
274 additions
and
16 deletions
+274
-16
src/redux/studio-comment-actions.js
src/redux/studio-comment-actions.js
+180
-0
src/views/preview/comment/comment.jsx
src/views/preview/comment/comment.jsx
+3
-3
src/views/preview/comment/compose-comment.jsx
src/views/preview/comment/compose-comment.jsx
+2
-2
src/views/preview/comment/top-level-comment.jsx
src/views/preview/comment/top-level-comment.jsx
+4
-4
src/views/preview/presentation.jsx
src/views/preview/presentation.jsx
+2
-2
src/views/studio/studio-comments.jsx
src/views/studio/studio-comments.jsx
+80
-4
src/views/studio/studio.jsx
src/views/studio/studio.jsx
+3
-1
No files found.
src/redux/studio-comment-actions.js
0 → 100644
View file @
7eb583a8
const
eachLimit
=
require
(
'
async/eachLimit
'
);
const
api
=
require
(
'
../lib/api
'
);
const
log
=
require
(
'
../lib/log
'
);
const
COMMENT_LIMIT
=
20
;
const
{
addNewComment
,
resetComments
,
Status
,
setFetchStatus
,
setCommentDeleted
,
setCommentReported
,
setCommentRestored
,
setMoreCommentsToLoad
,
setComments
,
setError
,
setReplies
,
setRepliesDeleted
,
setRepliesRestored
}
=
require
(
'
../redux/comments.js
'
);
const
getReplies
=
(
studioId
,
commentIds
,
offset
,
isAdmin
,
token
)
=>
(
dispatch
=>
{
dispatch
(
setFetchStatus
(
'
replies
'
,
Status
.
FETCHING
));
const
fetchedReplies
=
{};
eachLimit
(
commentIds
,
10
,
(
parentId
,
callback
)
=>
{
api
({
uri
:
`
${
isAdmin
?
'
/admin
'
:
''
}
/studios/
${
studioId
}
/comments/
${
parentId
}
/replies`
,
authentication
:
token
?
token
:
null
,
params
:
{
offset
:
offset
||
0
,
limit
:
COMMENT_LIMIT
}
},
(
err
,
body
,
res
)
=>
{
if
(
err
)
{
return
callback
(
`Error fetching comment replies:
${
err
}
`
);
}
if
(
typeof
body
===
'
undefined
'
||
res
.
statusCode
>=
400
)
{
// NotFound
return
callback
(
'
No comment reply information
'
);
}
fetchedReplies
[
parentId
]
=
body
;
callback
(
null
,
body
);
});
},
err
=>
{
if
(
err
)
{
dispatch
(
setFetchStatus
(
'
replies
'
,
Status
.
ERROR
));
dispatch
(
setError
(
err
));
return
;
}
dispatch
(
setFetchStatus
(
'
replies
'
,
Status
.
FETCHED
));
dispatch
(
setReplies
(
fetchedReplies
));
});
});
const
getTopLevelComments
=
(
id
,
offset
,
isAdmin
,
token
)
=>
(
dispatch
=>
{
dispatch
(
setFetchStatus
(
'
comments
'
,
Status
.
FETCHING
));
api
({
uri
:
`
${
isAdmin
?
'
/admin
'
:
''
}
/studios/
${
id
}
/comments`
,
authentication
:
token
?
token
:
null
,
params
:
{
offset
:
offset
||
0
,
limit
:
COMMENT_LIMIT
}
},
(
err
,
body
,
res
)
=>
{
if
(
err
)
{
dispatch
(
setFetchStatus
(
'
comments
'
,
Status
.
ERROR
));
dispatch
(
setError
(
err
));
return
;
}
if
(
typeof
body
===
'
undefined
'
||
res
.
statusCode
>=
400
)
{
// NotFound
dispatch
(
setFetchStatus
(
'
comments
'
,
Status
.
ERROR
));
dispatch
(
setError
(
'
No comment info
'
));
return
;
}
dispatch
(
setFetchStatus
(
'
comments
'
,
Status
.
FETCHED
));
dispatch
(
setComments
(
body
));
dispatch
(
getReplies
(
id
,
body
.
map
(
comment
=>
comment
.
id
),
0
,
isAdmin
,
token
));
// If we loaded a full page of comments, assume there are more to load.
// This will be wrong (1 / COMMENT_LIMIT) of the time, but does not require
// any more server query complexity, so seems worth it. In the case of a project with
// number of comments divisible by the COMMENT_LIMIT, the load more button will be
// clickable, but upon clicking it will go away.
dispatch
(
setMoreCommentsToLoad
(
body
.
length
===
COMMENT_LIMIT
));
});
});
const
getCommentById
=
(
studioId
,
commentId
,
isAdmin
,
token
)
=>
(
dispatch
=>
{
dispatch
(
setFetchStatus
(
'
comments
'
,
Status
.
FETCHING
));
api
({
uri
:
`
${
isAdmin
?
'
/admin
'
:
''
}
/studios/
${
studioId
}
/comments/
${
commentId
}
`
,
authentication
:
token
?
token
:
null
},
(
err
,
body
,
res
)
=>
{
if
(
err
)
{
dispatch
(
setFetchStatus
(
'
comments
'
,
Status
.
ERROR
));
dispatch
(
setError
(
err
));
return
;
}
if
(
!
body
||
res
.
statusCode
>=
400
)
{
// NotFound
dispatch
(
setFetchStatus
(
'
comments
'
,
Status
.
ERROR
));
dispatch
(
setError
(
'
No comment info
'
));
return
;
}
if
(
body
.
parent_id
)
{
// If the comment is a reply, load the parent
return
dispatch
(
getCommentById
(
studioId
,
body
.
parent_id
,
isAdmin
,
token
));
}
// If the comment is not a reply, show it as top level and load replies
dispatch
(
setFetchStatus
(
'
comments
'
,
Status
.
FETCHED
));
dispatch
(
setComments
([
body
]));
dispatch
(
getReplies
(
studioId
,
[
body
.
id
],
0
,
isAdmin
,
token
));
});
});
const
deleteComment
=
(
studioId
,
commentId
,
topLevelCommentId
,
token
)
=>
(
dispatch
=>
{
/* TODO fetching/fetched/error states updates for comment deleting */
api
({
uri
:
`/proxy/comments/studio/
${
studioId
}
/comment/
${
commentId
}
`
,
authentication
:
token
,
withCredentials
:
true
,
method
:
'
DELETE
'
,
useCsrf
:
true
},
(
err
,
body
,
res
)
=>
{
if
(
err
||
res
.
statusCode
!==
200
)
{
log
.
error
(
err
||
res
.
body
);
return
;
}
dispatch
(
setCommentDeleted
(
commentId
,
topLevelCommentId
));
if
(
!
topLevelCommentId
)
{
dispatch
(
setRepliesDeleted
(
commentId
));
}
});
});
const
reportComment
=
(
studioId
,
commentId
,
topLevelCommentId
,
token
)
=>
(
dispatch
=>
{
api
({
uri
:
`/proxy/studio/
${
studioId
}
/comment/
${
commentId
}
/report`
,
authentication
:
token
,
withCredentials
:
true
,
method
:
'
POST
'
,
useCsrf
:
true
},
(
err
,
body
,
res
)
=>
{
if
(
err
||
res
.
statusCode
!==
200
)
{
log
.
error
(
err
||
res
.
body
);
return
;
}
// TODO use the reportId in the response for unreporting functionality
dispatch
(
setCommentReported
(
commentId
,
topLevelCommentId
));
});
});
const
restoreComment
=
(
studioId
,
commentId
,
topLevelCommentId
,
token
)
=>
(
dispatch
=>
{
api
({
uri
:
`/proxy/admin/studio/
${
studioId
}
/comment/
${
commentId
}
/undelete`
,
authentication
:
token
,
withCredentials
:
true
,
method
:
'
PUT
'
,
useCsrf
:
true
},
(
err
,
body
,
res
)
=>
{
if
(
err
||
res
.
statusCode
!==
200
)
{
log
.
error
(
err
||
res
.
body
);
return
;
}
dispatch
(
setCommentRestored
(
commentId
,
topLevelCommentId
));
if
(
!
topLevelCommentId
)
{
dispatch
(
setRepliesRestored
(
commentId
));
}
});
});
module
.
exports
=
{
getTopLevelComments
,
getCommentById
,
getReplies
,
deleteComment
,
reportComment
,
restoreComment
,
// Re-export these specific action creators directly so the implementer
// does not need to go to two places for comment actions
addNewComment
,
resetComments
};
src/views/preview/comment/comment.jsx
View file @
7eb583a8
...
...
@@ -110,7 +110,7 @@ class Comment extends React.Component {
highlighted
,
id
,
parentId
,
p
rojectId
,
p
ostURI
,
replyUsername
,
visibility
}
=
this
.
props
;
...
...
@@ -234,7 +234,7 @@ class Comment extends React.Component {
isReply
commenteeId=
{
author
.
id
}
parentId=
{
parentId
||
id
}
p
rojectId=
{
projectId
}
p
ostURI=
{
postURI
}
onAddComment=
{
this
.
handlePostReply
}
onCancel=
{
this
.
handleToggleReplying
}
/>
...
...
@@ -285,7 +285,7 @@ Comment.propTypes = {
onReport
:
PropTypes
.
func
,
onRestore
:
PropTypes
.
func
,
parentId
:
PropTypes
.
number
,
p
rojectId
:
PropTypes
.
string
,
p
ostURI
:
PropTypes
.
string
,
replyUsername
:
PropTypes
.
string
,
visibility
:
PropTypes
.
string
};
...
...
src/views/preview/comment/compose-comment.jsx
View file @
7eb583a8
...
...
@@ -79,7 +79,7 @@ class ComposeComment extends React.Component {
handlePost
()
{
this
.
setState
({
status
:
ComposeStatus
.
SUBMITTING
});
api
({
uri
:
`/proxy/comments/project/
${
this
.
props
.
projectId
}
`
,
uri
:
this
.
props
.
postURI
,
authentication
:
this
.
props
.
user
.
token
,
withCredentials
:
true
,
method
:
'
POST
'
,
...
...
@@ -434,7 +434,7 @@ ComposeComment.propTypes = {
onAddComment
:
PropTypes
.
func
,
onCancel
:
PropTypes
.
func
,
parentId
:
PropTypes
.
number
,
p
rojectId
:
PropTypes
.
string
,
p
ostURI
:
PropTypes
.
string
,
user
:
PropTypes
.
shape
({
id
:
PropTypes
.
number
,
username
:
PropTypes
.
string
,
...
...
src/views/preview/comment/top-level-comment.jsx
View file @
7eb583a8
...
...
@@ -87,7 +87,7 @@ class TopLevelComment extends React.Component {
onReport
,
onRestore
,
replies
,
p
rojectId
,
p
ostURI
,
visibility
}
=
this
.
props
;
...
...
@@ -97,7 +97,7 @@ class TopLevelComment extends React.Component {
<
FlexRow
className=
"comment-container"
>
<
Comment
highlighted=
{
highlightedCommentId
===
id
}
p
rojectId=
{
projectId
}
p
ostURI=
{
postURI
}
onAddComment=
{
this
.
handleAddComment
}
{
...
{
author
,
...
...
@@ -138,7 +138,7 @@ class TopLevelComment extends React.Component {
id=
{
reply
.
id
}
key=
{
reply
.
id
}
parentId=
{
id
}
p
rojectId=
{
projectId
}
p
ostURI=
{
postURI
}
replyUsername=
{
this
.
authorUsername
(
reply
.
commentee_id
)
}
visibility=
{
reply
.
visibility
}
onAddComment=
{
this
.
handleAddComment
}
...
...
@@ -188,7 +188,7 @@ TopLevelComment.propTypes = {
onReport
:
PropTypes
.
func
,
onRestore
:
PropTypes
.
func
,
parentId
:
PropTypes
.
number
,
p
rojectId
:
PropTypes
.
string
,
p
ostURI
:
PropTypes
.
string
,
replies
:
PropTypes
.
arrayOf
(
PropTypes
.
object
),
visibility
:
PropTypes
.
string
};
...
...
src/views/preview/presentation.jsx
View file @
7eb583a8
...
...
@@ -581,7 +581,7 @@ const PreviewPresentation = ({
{
projectInfo
.
comments_allowed
?
(
isLoggedIn
?
(
isShared
&&
<
ComposeComment
p
rojectId=
{
projectId
}
p
ostURI=
{
`/proxy/comments/project/${projectId}`
}
onAddComment=
{
onAddComment
}
/>
)
:
(
...
...
@@ -613,7 +613,7 @@ const PreviewPresentation = ({
key=
{
comment
.
id
}
moreRepliesToLoad=
{
comment
.
moreRepliesToLoad
}
parentId=
{
comment
.
parent_id
}
p
rojectId=
{
projectId
}
p
ostURI=
{
`/proxy/comments/project/${projectId}`
}
replies=
{
replies
&&
replies
[
comment
.
id
]
?
replies
[
comment
.
id
]
:
[]
}
visibility=
{
comment
.
visibility
}
onAddComment=
{
onAddComment
}
...
...
src/views/studio/studio-comments.jsx
View file @
7eb583a8
import
React
from
'
react
'
;
import
React
,
{
useEffect
,
useCallback
}
from
'
react
'
;
import
PropTypes
from
'
prop-types
'
;
import
{
useParams
}
from
'
react-router-dom
'
;
import
{
connect
}
from
'
react-redux
'
;
import
{
FormattedMessage
}
from
'
react-intl
'
;
const
StudioComments
=
()
=>
{
import
Button
from
'
../../components/forms/button.jsx
'
;
import
ComposeComment
from
'
../preview/comment/compose-comment.jsx
'
;
import
TopLevelComment
from
'
../preview/comment/top-level-comment.jsx
'
;
import
studioCommentActions
from
'
../../redux/studio-comment-actions.js
'
;
const
StudioComments
=
({
comments
,
getTopLevelComments
,
handleNewComment
,
moreCommentsToLoad
,
replies
,
shouldShowCommentComposer
})
=>
{
const
{
studioId
}
=
useParams
();
const
handleLoadComments
=
useCallback
(()
=>
{
getTopLevelComments
(
studioId
,
comments
.
length
);
},
[
studioId
,
comments
.
length
]);
useEffect
(()
=>
{
if
(
comments
.
length
===
0
)
getTopLevelComments
(
studioId
,
0
);
},
[
studioId
]);
return
(
<
div
>
<
h2
>
Comments
</
h2
>
<
p
>
Studio
{
studioId
}
</
p
>
<
div
>
{
shouldShowCommentComposer
&&
<
ComposeComment
postURI=
{
`/proxy/comments/studio/${studioId}`
}
onAddComment=
{
handleNewComment
}
/>
}
{
comments
.
map
(
comment
=>
(
<
TopLevelComment
author=
{
comment
.
author
}
canReply=
{
shouldShowCommentComposer
}
content=
{
comment
.
content
}
datetimeCreated=
{
comment
.
datetime_created
}
id=
{
comment
.
id
}
key=
{
comment
.
id
}
moreRepliesToLoad=
{
comment
.
moreRepliesToLoad
}
parentId=
{
comment
.
parent_id
}
postURI=
{
`/proxy/comments/studio/${studioId}`
}
replies=
{
replies
&&
replies
[
comment
.
id
]
?
replies
[
comment
.
id
]
:
[]
}
visibility=
{
comment
.
visibility
}
/>
))
}
{
moreCommentsToLoad
&&
<
Button
className=
"button load-more-button"
onClick=
{
handleLoadComments
}
>
<
FormattedMessage
id=
"general.loadMore"
/>
</
Button
>
}
</
div
>
</
div
>
);
};
export
default
StudioComments
;
StudioComments
.
propTypes
=
{
comments
:
PropTypes
.
arrayOf
(
PropTypes
.
shape
({})),
getTopLevelComments
:
PropTypes
.
func
,
handleNewComment
:
PropTypes
.
func
,
moreCommentsToLoad
:
PropTypes
.
bool
,
replies
:
PropTypes
.
shape
({}),
shouldShowCommentComposer
:
PropTypes
.
bool
};
export
default
connect
(
state
=>
({
comments
:
state
.
comments
.
comments
,
moreCommentsToLoad
:
state
.
comments
.
moreCommentsToLoad
,
replies
:
state
.
comments
.
replies
,
// TODO permissions like this to a selector for testing
shouldShowCommentComposer
:
!!
state
.
session
.
session
.
user
// is logged in
}),
{
getTopLevelComments
:
studioCommentActions
.
getTopLevelComments
,
handleNewComment
:
studioCommentActions
.
addNewComment
}
)(
StudioComments
);
src/views/studio/studio.jsx
View file @
7eb583a8
...
...
@@ -25,6 +25,7 @@ import {
}
from
'
./lib/redux-modules
'
;
const
{
studioReducer
}
=
require
(
'
../../redux/studio
'
);
const
{
commentsReducer
}
=
require
(
'
../../redux/comments
'
);
const
StudioShell
=
()
=>
{
const
match
=
useRouteMatch
();
...
...
@@ -75,6 +76,7 @@ render(
[
curators
.
key
]:
curators
.
reducer
,
[
managers
.
key
]:
managers
.
reducer
,
[
activity
.
key
]:
activity
.
reducer
,
studio
:
studioReducer
studio
:
studioReducer
,
comments
:
commentsReducer
}
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment