diff options
author | Dessalines <tyhou13@gmx.com> | 2020-01-23 19:39:59 -0500 |
---|---|---|
committer | Dessalines <tyhou13@gmx.com> | 2020-01-23 19:39:59 -0500 |
commit | 8b88a8e75bf0732c1cc757445a7444edb48c68d2 (patch) | |
tree | 6b59100db56dc0acf34ed64232ce91102b4b191b /ui | |
parent | 9f499faa299cf68ab7c423efd12fd16f595e2ecf (diff) |
Squashed commit of the following:
commit f5b75f342bdc35b6c770358b2408a7ffc9ce80ad
Merge: bd1fc2b 69389f6
Author: Dessalines <happydooby@gmail.com>
Date: Thu Jan 23 19:17:42 2020 -0500
Done merging http-api and private_message
commit bd1fc2b80b8c6a42f0df721d91863b8688b339ee
Author: Dessalines <happydooby@gmail.com>
Date: Thu Jan 23 16:46:07 2020 -0500
Remove danger from private-message.tsx
commit 69389f61c9944319b5c71d7fb398294f1f8b8e7c
Author: Dessalines <happydooby@gmail.com>
Date: Thu Jan 23 11:21:21 2020 -0500
Fixing http curl POST docs.
commit 7fdcae4f0739df79cbf8d7ea2138519d87f2aa32
Merge: dbe9ad0 752318f
Author: Dessalines <happydooby@gmail.com>
Date: Thu Jan 23 11:01:06 2020 -0500
Merge remote-tracking branch 'nutomic/http-api' into dessalines-http-api
commit 752318fdf3cf95633744e89bbfe22a761fecc53d
Author: Felix <me@nutomic.com>
Date: Thu Jan 23 15:22:17 2020 +0100
api fixes
commit 9ccff18f23509d309261670b57563b87e8f61f77
Author: Dessalines <happydooby@gmail.com>
Date: Wed Jan 22 22:29:11 2020 -0500
Adding a toaster to replace alerts. Fixes #457
commit 5197407dd29a895fff37d4f0982e44d5e727d426
Merge: bacb9ac 58f673a
Author: Dessalines <happydooby@gmail.com>
Date: Wed Jan 22 21:20:38 2020 -0500
Merge branch 'private_messaging' into dev
commit 58f673ab7856d2380cfaedbc46baf944fedfabb5
Author: Dessalines <happydooby@gmail.com>
Date: Wed Jan 22 21:09:17 2020 -0500
Adding message to comment node actions.
commit bacb9ac59ed3a6d8b4a80f37103ccf9c3ab751c9
Merge: 10c6505 7d3adda
Author: Dessalines <happydooby@gmail.com>
Date: Wed Jan 22 20:37:08 2020 -0500
Merge branch 'private_messaging' into dev
commit 10c65059686ff74fc2ce412572e2d6fc3497c43a
Author: Dessalines <happydooby@gmail.com>
Date: Wed Jan 22 20:35:20 2020 -0500
Adding correct hello_name to mail.
commit 7d3adda0cd4888e70b362cbad1b82f46d79c8d4c
Author: Dessalines <happydooby@gmail.com>
Date: Wed Jan 22 16:35:29 2020 -0500
Adding private messaging, and matrix user ids.
- Fixes #244
commit dbe9ad0998b80d174130d82c11473555c7d6bc0e
Author: Dessalines <happydooby@gmail.com>
Date: Mon Jan 20 18:49:54 2020 -0500
Fixing last.
commit 20c9c54806429395f8a4c1a4e7a6a0f677d4e6b8
Author: Dessalines <happydooby@gmail.com>
Date: Sun Jan 19 13:31:37 2020 -0500
Updating API docs.
commit dc84ccaac94b9887f8512e3c32aeea0df89520be
Merge: 6c61dd2 3edd75e
Author: Dessalines <happydooby@gmail.com>
Date: Sun Jan 19 10:06:25 2020 -0500
Merge branch 'master' into dessalines-http-api
commit 6c61dd266bb6e389312ec56e7fc50f4a6b71b374
Merge: c5eecd0 e518954
Author: Dessalines <happydooby@gmail.com>
Date: Sun Jan 19 09:09:00 2020 -0500
Merge remote-tracking branch 'nutomic/websocket-generics' into dessalines-http-api
commit e518954bcab2553906cec9f2bcfa6d1b5fdc2f38
Author: Felix <me@nutomic.com>
Date: Sun Jan 19 14:25:50 2020 +0100
Use generics to reduce code duplication in websocket
commit c5eecd055e79510421aebe26e5d4f173a5916a3e
Author: Dessalines <happydooby@gmail.com>
Date: Sun Jan 19 00:38:45 2020 -0500
Strongly typing WebsocketJsonResponse. Forgot comment-form.tsx
commit 0c5eb471359929c532a4e6a63477a39ab6db67b6
Author: Dessalines <happydooby@gmail.com>
Date: Sat Jan 18 23:54:10 2020 -0500
First pass at fixing UI to work with new websocketresponses.
commit baf77bb6be88e796683bc21fd16a0359e68c7791
Author: Felix <me@nutomic.com>
Date: Sat Jan 18 17:25:45 2020 +0100
simplify json serialization code
commit 047ec97e1857888aeeac53629a76a4dc7e2f110b
Author: Felix <me@nutomic.com>
Date: Sat Jan 18 14:22:25 2020 +0100
rewrite api endpoint urls
commit 2fb4900b0cf586674d6212e0aa2c1b852d104a1a
Author: Felix <me@nutomic.com>
Date: Thu Jan 16 17:04:37 2020 +0100
fix typo
commit cba80815797b6578ab817b99fee35575cccef27d
Author: Felix <me@nutomic.com>
Date: Thu Jan 16 16:47:38 2020 +0100
fix formatting
commit d7285d8c25c9a88c33519ec12f3c730d6b45fcf5
Author: Felix <me@nutomic.com>
Date: Thu Jan 16 16:09:01 2020 +0100
small fix
commit 415040a1e9b90fcb71872a41fbb6e7e7131d966d
Author: Felix <me@nutomic.com>
Date: Thu Jan 16 15:39:08 2020 +0100
working!
commit 7a97c981a0b74cf6f66574690e58917a47508395
Author: Felix <me@nutomic.com>
Date: Wed Jan 15 16:48:21 2020 +0100
try to simplify code with higher order functions
commit c41082f98f459da2cc48dcfd7d9d5c2ac08865d0
Author: Felix <me@nutomic.com>
Date: Wed Jan 15 16:37:25 2020 +0100
Implement HTTP API using generics (fixes #380)
Diffstat (limited to 'ui')
29 files changed, 1465 insertions, 521 deletions
diff --git a/ui/assets/css/toastify.css b/ui/assets/css/toastify.css new file mode 100644 index 00000000..8804e229 --- /dev/null +++ b/ui/assets/css/toastify.css @@ -0,0 +1,78 @@ +/*! + * Toastify js 1.6.2 + * https://github.com/apvarun/toastify-js + * @license MIT licensed + * + * Copyright (C) 2018 Varun A P + */ + +.toastify { + padding: 12px 20px; + color: #ffffff; + display: inline-block; + box-shadow: 0 3px 6px -1px rgba(0, 0, 0, 0.12), 0 10px 36px -4px rgba(77, 96, 232, 0.3); + background: -webkit-linear-gradient(315deg, #73a5ff, #5477f5); + background: linear-gradient(135deg, #73a5ff, #5477f5); + position: fixed; + opacity: 0; + transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1); + border-radius: 2px; + cursor: pointer; + text-decoration: none; + max-width: calc(50% - 20px); + z-index: 2147483647; +} + +.toastify.on { + opacity: 1; +} + +.toast-close { + opacity: 0.4; + padding: 0 5px; +} + +.toastify-right { + right: 15px; +} + +.toastify-left { + left: 15px; +} + +.toastify-top { + top: -150px; +} + +.toastify-bottom { + bottom: -150px; +} + +.toastify-rounded { + border-radius: 25px; +} + +.toastify-avatar { + width: 1.5em; + height: 1.5em; + margin: 0 5px; + border-radius: 2px; +} + +.toastify-center { + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + max-width: fit-content; +} + +@media only screen and (max-width: 360px) { + .toastify-right, .toastify-left { + margin-left: auto; + margin-right: auto; + left: 0; + right: 0; + max-width: fit-content; + } +} diff --git a/ui/package.json b/ui/package.json index ea6343da..41f47088 100644 --- a/ui/package.json +++ b/ui/package.json @@ -36,6 +36,7 @@ "prettier": "^1.18.2", "rxjs": "^6.4.0", "terser": "^4.6.0", + "toastify-js": "^1.6.2", "tributejs": "^4.1.1", "twemoji": "^12.1.2", "ws": "^7.0.0" diff --git a/ui/src/components/comment-form.tsx b/ui/src/components/comment-form.tsx index f5816899..b8ea0a5a 100644 --- a/ui/src/components/comment-form.tsx +++ b/ui/src/components/comment-form.tsx @@ -10,12 +10,13 @@ import { } from '../interfaces'; import { Subscription } from 'rxjs'; import { + wsJsonToRes, capitalizeFirstLetter, mentionDropdownFetchLimit, - msgOp, mdToHtml, randomStr, markdownHelpUrl, + toast, } from '../utils'; import { WebSocketService, UserService } from '../services'; import autosize from 'autosize'; @@ -293,7 +294,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> { .catch(error => { i.state.imageLoading = false; i.setState(i.state); - alert(error); + toast(error, 'danger'); }); } @@ -311,10 +312,10 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> { this.userSub = WebSocketService.Instance.subject.subscribe( msg => { - let op: UserOperation = msgOp(msg); - if (op == UserOperation.Search) { - let res: SearchResponse = msg; - let users = res.users.map(u => { + let res = wsJsonToRes(msg); + if (res.op == UserOperation.Search) { + let data = res.data as SearchResponse; + let users = data.users.map(u => { return { key: u.name }; }); cb(users); @@ -343,10 +344,10 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> { this.communitySub = WebSocketService.Instance.subject.subscribe( msg => { - let op: UserOperation = msgOp(msg); - if (op == UserOperation.Search) { - let res: SearchResponse = msg; - let communities = res.communities.map(u => { + let res = wsJsonToRes(msg); + if (res.op == UserOperation.Search) { + let data = res.data as SearchResponse; + let communities = data.communities.map(u => { return { key: u.name }; }); cb(communities); diff --git a/ui/src/components/comment-node.tsx b/ui/src/components/comment-node.tsx index baaf63e9..046fc88d 100644 --- a/ui/src/components/comment-node.tsx +++ b/ui/src/components/comment-node.tsx @@ -293,6 +293,16 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { </li> </> )} + {!this.myComment && ( + <li className="list-inline-item"> + <Link + class="text-muted" + to={`/create_private_message?recipient_id=${node.comment.creator_id}`} + > + {i18n.t('message').toLowerCase()} + </Link> + </li> + )} <li className="list-inline-item">•</li> <li className="list-inline-item"> <span diff --git a/ui/src/components/communities.tsx b/ui/src/components/communities.tsx index 598a5dad..867cfd81 100644 --- a/ui/src/components/communities.tsx +++ b/ui/src/components/communities.tsx @@ -10,9 +10,10 @@ import { FollowCommunityForm, ListCommunitiesForm, SortType, + WebSocketJsonResponse, } from '../interfaces'; import { WebSocketService } from '../services'; -import { msgOp } from '../utils'; +import { wsJsonToRes, toast } from '../utils'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; @@ -231,15 +232,15 @@ export class Communities extends Component<any, CommunitiesState> { WebSocketService.Instance.listCommunities(listCommunitiesForm); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { console.log(msg); - let op: UserOperation = msgOp(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + let res = wsJsonToRes(msg); + if (res.error) { + toast(i18n.t(msg.error), 'danger'); return; - } else if (op == UserOperation.ListCommunities) { - let res: ListCommunitiesResponse = msg; - this.state.communities = res.communities; + } else if (res.op == UserOperation.ListCommunities) { + let data = res.data as ListCommunitiesResponse; + this.state.communities = data.communities; this.state.communities.sort( (a, b) => b.number_of_subscribers - a.number_of_subscribers ); @@ -248,11 +249,11 @@ export class Communities extends Component<any, CommunitiesState> { this.setState(this.state); let table = document.querySelector('#community_table'); Sortable.initTable(table); - } else if (op == UserOperation.FollowCommunity) { - let res: CommunityResponse = msg; - let found = this.state.communities.find(c => c.id == res.community.id); - found.subscribed = res.community.subscribed; - found.number_of_subscribers = res.community.number_of_subscribers; + } else if (res.op == UserOperation.FollowCommunity) { + let data = res.data as CommunityResponse; + let found = this.state.communities.find(c => c.id == data.community.id); + found.subscribed = data.community.subscribed; + found.number_of_subscribers = data.community.number_of_subscribers; this.setState(this.state); } } diff --git a/ui/src/components/community-form.tsx b/ui/src/components/community-form.tsx index 2085da28..4dc7bfcb 100644 --- a/ui/src/components/community-form.tsx +++ b/ui/src/components/community-form.tsx @@ -8,10 +8,11 @@ import { ListCategoriesResponse, CommunityResponse, GetSiteResponse, + WebSocketJsonResponse, } from '../interfaces'; import { WebSocketService } from '../services'; -import { msgOp, capitalizeFirstLetter } from '../utils'; -import * as autosize from 'autosize'; +import { wsJsonToRes, capitalizeFirstLetter, toast } from '../utils'; +import autosize from 'autosize'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; @@ -67,14 +68,7 @@ export class CommunityForm extends Component< } this.subscription = WebSocketService.Instance.subject - .pipe( - retryWhen(errors => - errors.pipe( - delay(3000), - take(10) - ) - ) - ) + .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10)))) .subscribe( msg => this.parseMessage(msg), err => console.error(err), @@ -246,34 +240,34 @@ export class CommunityForm extends Component< i.props.onCancel(); } - parseMessage(msg: any) { - let op: UserOperation = msgOp(msg); + parseMessage(msg: WebSocketJsonResponse) { + let res = wsJsonToRes(msg); console.log(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + if (res.error) { + toast(i18n.t(msg.error), 'danger'); this.state.loading = false; this.setState(this.state); return; - } else if (op == UserOperation.ListCategories) { - let res: ListCategoriesResponse = msg; - this.state.categories = res.categories; + } else if (res.op == UserOperation.ListCategories) { + let data = res.data as ListCategoriesResponse; + this.state.categories = data.categories; if (!this.props.community) { - this.state.communityForm.category_id = res.categories[0].id; + this.state.communityForm.category_id = data.categories[0].id; } this.setState(this.state); - } else if (op == UserOperation.CreateCommunity) { - let res: CommunityResponse = msg; + } else if (res.op == UserOperation.CreateCommunity) { + let data = res.data as CommunityResponse; this.state.loading = false; - this.props.onCreate(res.community); + this.props.onCreate(data.community); } - // TODO is ths necessary - else if (op == UserOperation.EditCommunity) { - let res: CommunityResponse = msg; + // TODO is this necessary + else if (res.op == UserOperation.EditCommunity) { + let data = res.data as CommunityResponse; this.state.loading = false; - this.props.onEdit(res.community); - } else if (op == UserOperation.GetSite) { - let res: GetSiteResponse = msg; - this.state.enable_nsfw = res.site.enable_nsfw; + this.props.onEdit(data.community); + } else if (res.op == UserOperation.GetSite) { + let data = res.data as GetSiteResponse; + this.state.enable_nsfw = data.site.enable_nsfw; this.setState(this.state); } } diff --git a/ui/src/components/community.tsx b/ui/src/components/community.tsx index 873b5a8a..9d02dd86 100644 --- a/ui/src/components/community.tsx +++ b/ui/src/components/community.tsx @@ -14,16 +14,18 @@ import { ListingType, GetPostsResponse, CreatePostLikeResponse, + WebSocketJsonResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { PostListings } from './post-listings'; import { SortSelect } from './sort-select'; import { Sidebar } from './sidebar'; import { - msgOp, + wsJsonToRes, routeSortTypeToEnum, fetchLimit, postRefetchSeconds, + toast, } from '../utils'; import { T } from 'inferno-i18next'; import { i18n } from '../i18next'; @@ -253,43 +255,43 @@ export class Community extends Component<any, State> { WebSocketService.Instance.getPosts(getPostsForm); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { console.log(msg); - let op: UserOperation = msgOp(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + let res = wsJsonToRes(msg); + if (res.error) { + toast(i18n.t(msg.error), 'danger'); this.context.router.history.push('/'); return; - } else if (op == UserOperation.GetCommunity) { - let res: GetCommunityResponse = msg; - this.state.community = res.community; - this.state.moderators = res.moderators; - this.state.admins = res.admins; + } else if (res.op == UserOperation.GetCommunity) { + let data = res.data as GetCommunityResponse; + this.state.community = data.community; + this.state.moderators = data.moderators; + this.state.admins = data.admins; document.title = `/c/${this.state.community.name} - ${WebSocketService.Instance.site.name}`; this.setState(this.state); this.keepFetchingPosts(); - } else if (op == UserOperation.EditCommunity) { - let res: CommunityResponse = msg; - this.state.community = res.community; + } else if (res.op == UserOperation.EditCommunity) { + let data = res.data as CommunityResponse; + this.state.community = data.community; this.setState(this.state); - } else if (op == UserOperation.FollowCommunity) { - let res: CommunityResponse = msg; - this.state.community.subscribed = res.community.subscribed; + } else if (res.op == UserOperation.FollowCommunity) { + let data = res.data as CommunityResponse; + this.state.community.subscribed = data.community.subscribed; this.state.community.number_of_subscribers = - res.community.number_of_subscribers; + data.community.number_of_subscribers; this.setState(this.state); - } else if (op == UserOperation.GetPosts) { - let res: GetPostsResponse = msg; - this.state.posts = res.posts; + } else if (res.op == UserOperation.GetPosts) { + let data = res.data as GetPostsResponse; + this.state.posts = data.posts; this.state.loading = false; this.setState(this.state); - } else if (op == UserOperation.CreatePostLike) { - let res: CreatePostLikeResponse = msg; - let found = this.state.posts.find(c => c.id == res.post.id); - found.my_vote = res.post.my_vote; - found.score = res.post.score; - found.upvotes = res.post.upvotes; - found.downvotes = res.post.downvotes; + } else if (res.op == UserOperation.CreatePostLike) { + let data = res.data as CreatePostLikeResponse; + let found = this.state.posts.find(c => c.id == data.post.id); + found.my_vote = data.post.my_vote; + found.score = data.post.score; + found.upvotes = data.post.upvotes; + found.downvotes = data.post.downvotes; this.setState(this.state); } } diff --git a/ui/src/components/create-private-message.tsx b/ui/src/components/create-private-message.tsx new file mode 100644 index 00000000..7160bc52 --- /dev/null +++ b/ui/src/components/create-private-message.tsx @@ -0,0 +1,53 @@ +import { Component } from 'inferno'; +import { PrivateMessageForm } from './private-message-form'; +import { WebSocketService } from '../services'; +import { PrivateMessageFormParams } from '../interfaces'; +import { toast } from '../utils'; +import { i18n } from '../i18next'; + +export class CreatePrivateMessage extends Component<any, any> { + constructor(props: any, context: any) { + super(props, context); + this.handlePrivateMessageCreate = this.handlePrivateMessageCreate.bind( + this + ); + } + + componentDidMount() { + document.title = `${i18n.t('create_private_message')} - ${ + WebSocketService.Instance.site.name + }`; + } + + render() { + return ( + <div class="container"> + <div class="row"> + <div class="col-12 col-lg-6 offset-lg-3 mb-4"> + <h5>{i18n.t('create_private_message')}</h5> + <PrivateMessageForm + onCreate={this.handlePrivateMessageCreate} + params={this.params} + /> + </div> + </div> + </div> + ); + } + + get params(): PrivateMessageFormParams { + let urlParams = new URLSearchParams(this.props.location.search); + let params: PrivateMessageFormParams = { + recipient_id: Number(urlParams.get('recipient_id')), + }; + + return params; + } + + handlePrivateMessageCreate() { + toast(i18n.t('message_sent')); + + // Navigate to the front + this.props.history.push(`/`); + } +} diff --git a/ui/src/components/inbox.tsx b/ui/src/components/inbox.tsx index a302b834..5c3ff6d2 100644 --- a/ui/src/components/inbox.tsx +++ b/ui/src/components/inbox.tsx @@ -12,10 +12,16 @@ import { GetUserMentionsResponse, UserMentionResponse, CommentResponse, + WebSocketJsonResponse, + PrivateMessage as PrivateMessageI, + GetPrivateMessagesForm, + PrivateMessagesResponse, + PrivateMessageResponse, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; -import { msgOp, fetchLimit } from '../utils'; +import { wsJsonToRes, fetchLimit, isCommentType, toast } from '../utils'; import { CommentNodes } from './comment-nodes'; +import { PrivateMessage } from './private-message'; import { SortSelect } from './sort-select'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; @@ -26,9 +32,10 @@ enum UnreadOrAll { } enum UnreadType { - Both, + All, Replies, Mentions, + Messages, } interface InboxState { @@ -36,6 +43,7 @@ interface InboxState { unreadType: UnreadType; replies: Array<Comment>; mentions: Array<Comment>; + messages: Array<PrivateMessageI>; sort: SortType; page: number; } @@ -44,9 +52,10 @@ export class Inbox extends Component<any, InboxState> { private subscription: Subscription; private emptyState: InboxState = { unreadOrAll: UnreadOrAll.Unread, - unreadType: UnreadType.Both, + unreadType: UnreadType.All, replies: [], mentions: [], + messages: [], sort: SortType.New, page: 1, }; @@ -103,7 +112,10 @@ export class Inbox extends Component<any, InboxState> { </a> </small> </h5> - {this.state.replies.length + this.state.mentions.length > 0 && + {this.state.replies.length + + this.state.mentions.length + + this.state.messages.length > + 0 && this.state.unreadOrAll == UnreadOrAll.Unread && ( <ul class="list-inline mb-1 text-muted small font-weight-bold"> <li className="list-inline-item"> @@ -114,9 +126,10 @@ export class Inbox extends Component<any, InboxState> { </ul> )} {this.selects()} - {this.state.unreadType == UnreadType.Both && this.both()} + {this.state.unreadType == UnreadType.All && this.all()} {this.state.unreadType == UnreadType.Replies && this.replies()} {this.state.unreadType == UnreadType.Mentions && this.mentions()} + {this.state.unreadType == UnreadType.Messages && this.messages()} {this.paginator()} </div> </div> @@ -150,8 +163,8 @@ export class Inbox extends Component<any, InboxState> { <option disabled> <T i18nKey="type">#</T> </option> - <option value={UnreadType.Both}> - <T i18nKey="both">#</T> + <option value={UnreadType.All}> + <T i18nKey="all">#</T> </option> <option value={UnreadType.Replies}> <T i18nKey="replies">#</T> @@ -159,6 +172,9 @@ export class Inbox extends Component<any, InboxState> { <option value={UnreadType.Mentions}> <T i18nKey="mentions">#</T> </option> + <option value={UnreadType.Messages}> + <T i18nKey="messages">#</T> + </option> </select> <SortSelect sort={this.state.sort} @@ -169,33 +185,25 @@ export class Inbox extends Component<any, InboxState> { ); } - both() { - let combined: Array<{ - type_: string; - data: Comment; - }> = []; - let replies = this.state.replies.map(e => { - return { type_: 'replies', data: e }; - }); - let mentions = this.state.mentions.map(e => { - return { type_: 'mentions', data: e }; - }); + all() { + let combined: Array<Comment | PrivateMessageI> = []; - combined.push(...replies); - combined.push(...mentions); + combined.push(...this.state.replies); + combined.push(...this.state.mentions); + combined.push(...this.state.messages); // Sort it - if (this.state.sort == SortType.New) { - combined.sort((a, b) => b.data.published.localeCompare(a.data.published)); - } else { - combined.sort((a, b) => b.data.score - a.data.score); - } + combined.sort((a, b) => b.published.localeCompare(a.published)); return ( <div> - {combined.map(i => ( - <CommentNodes nodes={[{ comment: i.data }]} noIndent markable /> - ))} + {combined.map(i => + isCommentType(i) ? ( + <CommentNodes nodes={[{ comment: i }]} noIndent markable /> + ) : ( + <PrivateMessage privateMessage={i} /> + ) + )} </div> ); } @@ -220,6 +228,16 @@ export class Inbox extends Component<any, InboxState> { ); } + messages() { + return ( + <div> + {this.state.messages.map(message => ( + <PrivateMessage privateMessage={message} /> + ))} + </div> + ); + } + paginator() { return ( <div class="mt-2"> @@ -283,6 +301,13 @@ export class Inbox extends Component<any, InboxState> { limit: fetchLimit, }; WebSocketService.Instance.getUserMentions(userMentionsForm); + + let privateMessagesForm: GetPrivateMessagesForm = { + unread_only: this.state.unreadOrAll == UnreadOrAll.Unread, + page: this.state.page, + limit: fetchLimit, + }; + WebSocketService.Instance.getPrivateMessages(privateMessagesForm); } handleSortChange(val: SortType) { @@ -296,94 +321,122 @@ export class Inbox extends Component<any, InboxState> { WebSocketService.Instance.markAllAsRead(); } - parseMessage(msg: any) { + parseMessage(msg: WebSocketJsonResponse) { console.log(msg); - let op: UserOperation = msgOp(msg); - if (msg.error) { - alert(i18n.t(msg.error)); + let res = wsJsonToRes(msg); + if (res.error) { + toast(i18n.t(msg.error), 'danger'); return; - } else if (op == UserOperation.GetReplies) { - let res: GetRepliesResponse = msg; - this.state.replies = res.replies; + } else if (res.op == UserOperation.GetReplies) { + let data = res.data as GetRepliesResponse; + this.state.replies = data.replies; + this.sendUnreadCount(); + window.scrollTo(0, 0); + this.setState(this.state); + } else if (res.op == UserOperation.GetUserMentions) { + let data = res.data as GetUserMentionsResponse; + this.state.mentions = data.mentions; + this.sendUnreadCount(); + window.scrollTo(0, 0); + this.setState(this.state); + } else if (res.op == UserOperation.GetPrivateMessages) { + let data = res.data as PrivateMessagesResponse; + this.state.messages = data.messages; this.sendUnreadCount(); window.scrollTo(0, 0); this.setState(this.state); - } else if (op == UserOperation.GetUserMentions) { - let res: GetUserMentionsResponse = msg; - this.state.mentions = res.mentions; + } else if (res.op == UserOperation.EditPrivateMessage) { + let data = res.data as PrivateMessageResponse; + let found: PrivateMessageI = this.state.messages.find( + m => m.id === data.message.id + ); + found.content = data.message.content; + found.updated = data.message.updated; + found.deleted = data.message.deleted; + // If youre in the unread view, just remove it from the list + if (this.state.unreadOrAll == UnreadOrAll.Unread && data.message.read) { + this.state.messages = this.state.messages.filter( + r => r.id !== data.message.id + ); + } else { + let found = this.state.messages.find(c => c.id == data.message.id); + found.read = data.message.read; |