From ea317af2697647aad51a7f141c41899edaef28d2 Mon Sep 17 00:00:00 2001 From: Dessalines <tyhou13@gmx.com> Date: Wed, 23 Dec 2020 20:58:27 -0500 Subject: [PATCH] First pass at v2_api --- package.json | 2 +- src/client/index.tsx | 4 +- src/server/index.tsx | 10 +- src/shared/components/admin-settings.tsx | 71 +-- src/shared/components/app.tsx | 17 +- src/shared/components/comment-form.tsx | 82 +-- src/shared/components/comment-node.tsx | 322 +++++----- src/shared/components/comment-nodes.tsx | 13 +- src/shared/components/communities.tsx | 85 +-- src/shared/components/community-form.tsx | 72 +-- src/shared/components/community-link.tsx | 13 +- src/shared/components/community.tsx | 202 +++---- src/shared/components/create-community.tsx | 29 +- src/shared/components/create-post.tsx | 47 +- .../components/create-private-message.tsx | 42 +- src/shared/components/inbox.tsx | 362 ++++++----- src/shared/components/instances.tsx | 4 +- src/shared/components/login.tsx | 66 ++- src/shared/components/main.tsx | 255 ++++---- src/shared/components/modlog.tsx | 560 +++++++++--------- src/shared/components/navbar.tsx | 120 ++-- src/shared/components/password_change.tsx | 22 +- src/shared/components/post-form.tsx | 118 ++-- src/shared/components/post-listing.tsx | 392 ++++++------ src/shared/components/post-listings.tsx | 47 +- src/shared/components/post.tsx | 264 ++++----- .../components/private-message-form.tsx | 69 +-- src/shared/components/private-message.tsx | 84 +-- src/shared/components/search.tsx | 102 ++-- src/shared/components/setup.tsx | 23 +- src/shared/components/sidebar.tsx | 128 ++-- src/shared/components/site-form.tsx | 12 +- src/shared/components/theme.tsx | 4 +- src/shared/components/user-details.tsx | 123 ++-- src/shared/components/user-listing.tsx | 14 +- src/shared/components/user.tsx | 206 +++---- src/shared/interfaces.ts | 23 +- src/shared/services/UserService.ts | 15 +- src/shared/services/WebSocketService.ts | 369 +----------- src/shared/utils.ts | 284 +++++---- yarn.lock | 8 +- 41 files changed, 2218 insertions(+), 2467 deletions(-) diff --git a/package.json b/package.json index c5493f5..e5ecf1e 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "eslint": "^7.15.0", "eslint-plugin-jane": "^9.0.4", "husky": "^4.3.5", - "lemmy-js-client": "^1.0.16", + "lemmy-js-client": "1.0.17-beta5", "lint-staged": "^10.5.3", "mini-css-extract-plugin": "^1.3.2", "node-fetch": "^2.6.1", diff --git a/src/client/index.tsx b/src/client/index.tsx index b7f216d..b07d180 100644 --- a/src/client/index.tsx +++ b/src/client/index.tsx @@ -3,12 +3,12 @@ import { BrowserRouter } from 'inferno-router'; import { initializeSite } from '../shared/initialize'; import { App } from '../shared/components/app'; -const site = window.isoData.site; +const site = window.isoData.site_res; initializeSite(site); const wrapper = ( <BrowserRouter> - <App site={window.isoData.site} /> + <App siteRes={window.isoData.site_res} /> </BrowserRouter> ); diff --git a/src/server/index.tsx b/src/server/index.tsx index cc8c3b0..a2bcd4a 100644 --- a/src/server/index.tsx +++ b/src/server/index.tsx @@ -8,8 +8,7 @@ import { App } from '../shared/components/app'; import { InitialFetchRequest, IsoData } from '../shared/interfaces'; import { routes } from '../shared/routes'; import IsomorphicCookie from 'isomorphic-cookie'; -import { setAuth } from '../shared/utils'; -import { GetSiteForm, LemmyHttp } from 'lemmy-js-client'; +import { GetSite, LemmyHttp } from 'lemmy-js-client'; import process from 'process'; import { Helmet } from 'inferno-helmet'; import { initializeSite } from '../shared/initialize'; @@ -30,8 +29,7 @@ server.get('/*', async (req, res) => { const context = {} as any; let auth: string = IsomorphicCookie.load('jwt', req); - let getSiteForm: GetSiteForm = {}; - setAuth(getSiteForm, auth); + let getSiteForm: GetSite = { auth }; let promises: Promise<any>[] = []; @@ -70,14 +68,14 @@ server.get('/*', async (req, res) => { let isoData: IsoData = { path: req.path, - site, + site_res: site, routeData, lang, }; const wrapper = ( <StaticRouter location={req.url} context={isoData}> - <App site={isoData.site} /> + <App siteRes={isoData.site_res} /> </StaticRouter> ); if (context.url) { diff --git a/src/shared/components/admin-settings.tsx b/src/shared/components/admin-settings.tsx index 6340d4f..b9f93f4 100644 --- a/src/shared/components/admin-settings.tsx +++ b/src/shared/components/admin-settings.tsx @@ -4,12 +4,11 @@ import { UserOperation, SiteResponse, GetSiteResponse, - SiteConfigForm, + SaveSiteConfig, GetSiteConfigResponse, - WebSocketJsonResponse, GetSiteConfig, } from 'lemmy-js-client'; -import { WebSocketService } from '../services'; +import { UserService, WebSocketService } from '../services'; import { wsJsonToRes, capitalizeFirstLetter, @@ -18,7 +17,7 @@ import { setIsoData, wsSubscribe, isBrowser, - setAuth, + wsUserOp, } from '../utils'; import autosize from 'autosize'; import { SiteForm } from './site-form'; @@ -30,7 +29,7 @@ import { InitialFetchRequest } from 'shared/interfaces'; interface AdminSettingsState { siteRes: GetSiteResponse; siteConfigRes: GetSiteConfigResponse; - siteConfigForm: SiteConfigForm; + siteConfigForm: SaveSiteConfig; loading: boolean; siteConfigLoading: boolean; } @@ -40,10 +39,10 @@ export class AdminSettings extends Component<any, AdminSettingsState> { private isoData = setIsoData(this.context); private subscription: Subscription; private emptyState: AdminSettingsState = { - siteRes: this.isoData.site, + siteRes: this.isoData.site_res, siteConfigForm: { config_hjson: null, - auth: null, + auth: UserService.Instance.authField(), }, siteConfigRes: { config_hjson: null, @@ -67,13 +66,14 @@ export class AdminSettings extends Component<any, AdminSettingsState> { this.state.siteConfigLoading = false; this.state.loading = false; } else { - WebSocketService.Instance.getSiteConfig(); + WebSocketService.Instance.client.getSiteConfig({ + auth: UserService.Instance.authField(), + }); } } static fetchInitialData(req: InitialFetchRequest): Promise<any>[] { - let form: GetSiteConfig = {}; - setAuth(form, req.auth); + let form: GetSiteConfig = { auth: req.auth }; return [req.client.getSiteConfig(form)]; } @@ -91,7 +91,9 @@ export class AdminSettings extends Component<any, AdminSettingsState> { } get documentTitle(): string { - return `${i18n.t('admin_settings')} - ${this.state.siteRes.site.name}`; + return `${i18n.t('admin_settings')} - ${ + this.state.siteRes.site_view.site.name + }`; } render() { @@ -110,8 +112,8 @@ export class AdminSettings extends Component<any, AdminSettingsState> { ) : ( <div class="row"> <div class="col-12 col-md-6"> - {this.state.siteRes.site.id && ( - <SiteForm site={this.state.siteRes.site} /> + {this.state.siteRes.site_view.site.id && ( + <SiteForm site={this.state.siteRes.site_view.site} /> )} {this.admins()} {this.bannedUsers()} @@ -130,16 +132,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> { <ul class="list-unstyled"> {this.state.siteRes.admins.map(admin => ( <li class="list-inline-item"> - <UserListing - user={{ - name: admin.name, - preferred_username: admin.preferred_username, - avatar: admin.avatar, - id: admin.id, - local: admin.local, - actor_id: admin.actor_id, - }} - /> + <UserListing user={admin.user} /> </li> ))} </ul> @@ -154,16 +147,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> { <ul class="list-unstyled"> {this.state.siteRes.banned.map(banned => ( <li class="list-inline-item"> - <UserListing - user={{ - name: banned.name, - preferred_username: banned.preferred_username, - avatar: banned.avatar, - id: banned.id, - local: banned.local, - actor_id: banned.actor_id, - }} - /> + <UserListing user={banned.user} /> </li> ))} </ul> @@ -214,7 +198,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> { handleSiteConfigSubmit(i: AdminSettings, event: any) { event.preventDefault(); i.state.siteConfigLoading = true; - WebSocketService.Instance.saveSiteConfig(i.state.siteConfigForm); + WebSocketService.Instance.client.saveSiteConfig(i.state.siteConfigForm); i.setState(i.state); } @@ -223,9 +207,8 @@ export class AdminSettings extends Component<any, AdminSettingsState> { i.setState(i.state); } - parseMessage(msg: WebSocketJsonResponse) { - console.log(msg); - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); this.context.router.history.push('/'); @@ -233,21 +216,21 @@ export class AdminSettings extends Component<any, AdminSettingsState> { this.setState(this.state); return; } else if (msg.reconnect) { - } else if (res.op == UserOperation.EditSite) { - let data = res.data as SiteResponse; - this.state.siteRes.site = data.site; + } else if (op == UserOperation.EditSite) { + let data = wsJsonToRes<SiteResponse>(msg).data; + this.state.siteRes.site_view = data.site_view; this.setState(this.state); toast(i18n.t('site_saved')); - } else if (res.op == UserOperation.GetSiteConfig) { - let data = res.data as GetSiteConfigResponse; + } else if (op == UserOperation.GetSiteConfig) { + let data = wsJsonToRes<GetSiteConfigResponse>(msg).data; this.state.siteConfigRes = data; this.state.loading = false; this.state.siteConfigForm.config_hjson = this.state.siteConfigRes.config_hjson; this.setState(this.state); var textarea: any = document.getElementById(this.siteConfigTextAreaId); autosize(textarea); - } else if (res.op == UserOperation.SaveSiteConfig) { - let data = res.data as GetSiteConfigResponse; + } else if (op == UserOperation.SaveSiteConfig) { + let data = wsJsonToRes<GetSiteConfigResponse>(msg).data; this.state.siteConfigRes = data; this.state.siteConfigForm.config_hjson = this.state.siteConfigRes.config_hjson; this.state.siteConfigLoading = false; diff --git a/src/shared/components/app.tsx b/src/shared/components/app.tsx index 6594645..26a2886 100644 --- a/src/shared/components/app.tsx +++ b/src/shared/components/app.tsx @@ -13,7 +13,7 @@ import { GetSiteResponse } from 'lemmy-js-client'; import './styles.scss'; export interface AppProps { - site: GetSiteResponse; + siteRes: GetSiteResponse; } export class App extends Component<AppProps, any> { @@ -21,24 +21,25 @@ export class App extends Component<AppProps, any> { super(props, context); } render() { + let siteRes = this.props.siteRes; return ( <> <Provider i18next={i18n}> <div> - <Theme user={this.props.site.my_user} /> - {this.props.site && - this.props.site.site && - this.props.site.site.icon && ( + <Theme user={siteRes.my_user} /> + {siteRes && + siteRes.site_view.site && + this.props.siteRes.site_view.site.icon && ( <Helmet> <link id="favicon" rel="icon" type="image/x-icon" - href={this.props.site.site.icon} + href={this.props.siteRes.site_view.site.icon} /> </Helmet> )} - <Navbar site={this.props.site} /> + <Navbar site_res={this.props.siteRes} /> <div class="mt-4 p-0 fl-1"> <Switch> {routes.map(({ path, exact, component: C, ...rest }) => ( @@ -53,7 +54,7 @@ export class App extends Component<AppProps, any> { </Switch> <Symbols /> </div> - <Footer site={this.props.site} /> + <Footer site={this.props.siteRes} /> </div> </Provider> </> diff --git a/src/shared/components/comment-form.tsx b/src/shared/components/comment-form.tsx index b032a06..5a0d404 100644 --- a/src/shared/components/comment-form.tsx +++ b/src/shared/components/comment-form.tsx @@ -2,13 +2,18 @@ import { Component } from 'inferno'; import { Link } from 'inferno-router'; import { Subscription } from 'rxjs'; import { - CommentNode as CommentNodeI, - CommentForm as CommentFormI, - WebSocketJsonResponse, + CreateComment, + EditComment, UserOperation, CommentResponse, } from 'lemmy-js-client'; -import { capitalizeFirstLetter, wsJsonToRes, wsSubscribe } from '../utils'; +import { CommentNode as CommentNodeI } from '../interfaces'; +import { + capitalizeFirstLetter, + wsJsonToRes, + wsSubscribe, + wsUserOp, +} from '../utils'; import { WebSocketService, UserService } from '../services'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; @@ -24,30 +29,21 @@ interface CommentFormProps { } interface CommentFormState { - commentForm: CommentFormI; buttonTitle: string; finished: boolean; + formId: string; } export class CommentForm extends Component<CommentFormProps, CommentFormState> { private subscription: Subscription; private emptyState: CommentFormState = { - commentForm: { - auth: null, - content: null, - post_id: this.props.node - ? this.props.node.comment.post_id - : this.props.postId, - creator_id: UserService.Instance.user - ? UserService.Instance.user.id - : null, - }, buttonTitle: !this.props.node ? capitalizeFirstLetter(i18n.t('post')) : this.props.edit ? capitalizeFirstLetter(i18n.t('save')) : capitalizeFirstLetter(i18n.t('reply')), finished: false, + formId: null, }; constructor(props: any, context: any) { @@ -58,18 +54,6 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> { this.state = this.emptyState; - if (this.props.node) { - if (this.props.edit) { - this.state.commentForm.edit_id = this.props.node.comment.id; - this.state.commentForm.parent_id = this.props.node.comment.parent_id; - this.state.commentForm.content = this.props.node.comment.content; - this.state.commentForm.creator_id = this.props.node.comment.creator_id; - } else { - // A reply gets a new parent id - this.state.commentForm.parent_id = this.props.node.comment.id; - } - } - this.parseMessage = this.parseMessage.bind(this); this.subscription = wsSubscribe(this.parseMessage); } @@ -83,7 +67,11 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> { <div class="mb-3"> {UserService.Instance.user ? ( <MarkdownTextArea - initialContent={this.state.commentForm.content} + initialContent={ + this.props.node + ? this.props.node.comment_view.comment.content + : null + } buttonTitle={this.state.buttonTitle} finished={this.state.finished} replyType={!!this.props.node} @@ -110,12 +98,28 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> { } handleCommentSubmit(msg: { val: string; formId: string }) { - this.state.commentForm.content = msg.val; - this.state.commentForm.form_id = msg.formId; + let content = msg.val; + this.state.formId = msg.formId; + + let node = this.props.node; + if (this.props.edit) { - WebSocketService.Instance.editComment(this.state.commentForm); + let form: EditComment = { + content, + form_id: this.state.formId, + edit_id: node.comment_view.comment.id, + auth: UserService.Instance.authField(), + }; + WebSocketService.Instance.client.editComment(form); } else { - WebSocketService.Instance.createComment(this.state.commentForm); + let form: CreateComment = { + content, + form_id: this.state.formId, + post_id: node ? node.comment_view.post.id : this.props.postId, + parent_id: node ? node.comment_view.comment.parent_id : null, + auth: UserService.Instance.authField(), + }; + WebSocketService.Instance.client.createComment(form); } this.setState(this.state); } @@ -124,22 +128,22 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> { this.props.onReplyCancel(); } - parseMessage(msg: WebSocketJsonResponse) { - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); // Only do the showing and hiding if logged in if (UserService.Instance.user) { if ( - res.op == UserOperation.CreateComment || - res.op == UserOperation.EditComment + op == UserOperation.CreateComment || + op == UserOperation.EditComment ) { - let data = res.data as CommentResponse; + let data = wsJsonToRes<CommentResponse>(msg).data; // This only finishes this form, if the randomly generated form_id matches the one received - if (this.state.commentForm.form_id == data.form_id) { + if (this.state.formId == data.form_id) { this.setState({ finished: true }); - // Necessary because it broke tribute for some reaso + // Necessary because it broke tribute for some reason this.setState({ finished: false }); } } diff --git a/src/shared/components/comment-node.tsx b/src/shared/components/comment-node.tsx index 030515c..3d69812 100644 --- a/src/shared/components/comment-node.tsx +++ b/src/shared/components/comment-node.tsx @@ -1,24 +1,29 @@ import { Component, linkEvent } from 'inferno'; import { Link } from 'inferno-router'; import { - CommentNode as CommentNodeI, - CommentLikeForm, - DeleteCommentForm, - RemoveCommentForm, - MarkCommentAsReadForm, - MarkUserMentionAsReadForm, - SaveCommentForm, - BanFromCommunityForm, - BanUserForm, - CommunityUser, - UserView, - AddModToCommunityForm, - AddAdminForm, - TransferCommunityForm, - TransferSiteForm, + CreateCommentLike, + DeleteComment, + RemoveComment, + MarkCommentAsRead, + MarkUserMentionAsRead, + SaveComment, + BanFromCommunity, + BanUser, + CommunityModeratorView, + UserViewSafe, + AddModToCommunity, + AddAdmin, + TransferCommunity, + TransferSite, SortType, + CommentView, + UserMentionView, } from 'lemmy-js-client'; -import { CommentSortType, BanType } from '../interfaces'; +import { + CommentSortType, + CommentNode as CommentNodeI, + BanType, +} from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { mdToHtml, @@ -70,8 +75,8 @@ interface CommentNodeProps { locked?: boolean; markable?: boolean; showContext?: boolean; - moderators: CommunityUser[]; - admins: UserView[]; + moderators: CommunityModeratorView[]; + admins: UserViewSafe[]; // TODO is this necessary, can't I get it from the node itself? postCreatorId?: number; showCommunity?: boolean; @@ -98,12 +103,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { showConfirmTransferCommunity: false, showConfirmAppointAsMod: false, showConfirmAppointAsAdmin: false, - my_vote: this.props.node.comment.my_vote, - score: this.props.node.comment.score, - upvotes: this.props.node.comment.upvotes, - downvotes: this.props.node.comment.downvotes, - borderColor: this.props.node.comment.depth - ? colorList[this.props.node.comment.depth % colorList.length] + my_vote: this.props.node.comment_view.my_vote, + score: this.props.node.comment_view.counts.score, + upvotes: this.props.node.comment_view.counts.upvotes, + downvotes: this.props.node.comment_view.counts.downvotes, + borderColor: this.props.node.depth + ? colorList[this.props.node.depth % colorList.length] : colorList[0], readLoading: false, saveLoading: false, @@ -118,11 +123,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { this.handleCommentDownvote = this.handleCommentDownvote.bind(this); } + // TODO see if there's a better way to do this, and all willReceiveProps componentWillReceiveProps(nextProps: CommentNodeProps) { - this.state.my_vote = nextProps.node.comment.my_vote; - this.state.upvotes = nextProps.node.comment.upvotes; - this.state.downvotes = nextProps.node.comment.downvotes; - this.state.score = nextProps.node.comment.score; + let cv = nextProps.node.comment_view; + this.state.my_vote = cv.my_vote; + this.state.upvotes = cv.counts.upvotes; + this.state.downvotes = cv.counts.downvotes; + this.state.score = cv.counts.score; this.state.readLoading = false; this.state.saveLoading = false; this.setState(this.state); @@ -130,43 +137,30 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { render() { let node = this.props.node; + let cv = this.props.node.comment_view; return ( <div className={`comment ${ - node.comment.parent_id && !this.props.noIndent ? 'ml-1' : '' + cv.comment.parent_id && !this.props.noIndent ? 'ml-1' : '' }`} > <div - id={`comment-${node.comment.id}`} + id={`comment-${cv.comment.id}`} className={`details comment-node py-2 ${ !this.props.noBorder ? 'border-top border-light' : '' } ${this.isCommentNew ? 'mark' : ''}`} style={ !this.props.noIndent && - this.props.node.comment.parent_id && + cv.comment.parent_id && `border-left: 2px ${this.state.borderColor} solid !important` } > <div - class={`${ - !this.props.noIndent && - this.props.node.comment.parent_id && - 'ml-2' - }`} + class={`${!this.props.noIndent && cv.comment.parent_id && 'ml-2'}`} > <div class="d-flex flex-wrap align-items-center text-muted small"> <span class="mr-2"> - <UserListing - user={{ - name: node.comment.creator_name, - preferred_username: node.comment.creator_preferred_username, - avatar: node.comment.creator_avatar, - id: node.comment.creator_id, - local: node.comment.creator_local, - actor_id: node.comment.creator_actor_id, - published: node.comment.creator_published, - }} - /> + <UserListing user={cv.creator} /> </span> {this.isMod && ( @@ -184,7 +178,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { {i18n.t('creator')} </div> )} - {(node.comment.banned_from_community || node.comment.banned) && ( + {(cv.creator_banned_from_community || cv.creator.banned) && ( <div className="badge badge-danger mr-2"> {i18n.t('banned')} </div> @@ -192,18 +186,10 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { {this.props.showCommunity && ( <> <span class="mx-1">{i18n.t('to')}</span> - <CommunityLink - community={{ - name: node.comment.community_name, - id: node.comment.community_id, - local: node.comment.community_local, - actor_id: node.comment.community_actor_id, - icon: node.comment.community_icon, - }} - /> + <CommunityLink community={cv.community} /> <span class="mx-2">â¢</span> - <Link className="mr-2" to={`/post/${node.comment.post_id}`}> - {node.comment.post_name} + <Link className="mr-2" to={`/post/${cv.post.id}`}> + {cv.post.name} </Link> </> )} @@ -224,7 +210,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { </a> <span className="mr-1">â¢</span> <span> - <MomentTime data={node.comment} /> + <MomentTime data={cv.comment} /> </span> </div> {/* end of user row */} @@ -256,7 +242,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { class="btn btn-link btn-animate text-muted" onClick={linkEvent(this, this.handleMarkRead)} data-tippy-content={ - node.comment.read + cv.comment.read ? i18n.t('mark_as_unread') : i18n.t('mark_as_read') } @@ -266,7 +252,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { ) : ( <svg class={`icon icon-inline ${ - node.comment.read && 'text-success' + cv.comment.read && 'text-success' }`} > <use xlinkHref="#icon-check"></use> @@ -333,7 +319,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { <button class="btn btn-link btn-animate"> <Link className="text-muted" - to={`/create_private_message/recipient/${node.comment.creator_id}`} + to={`/create_private_message/recipient/${cv.creator.id}`} title={i18n.t('message').toLowerCase()} > <svg class="icon"> @@ -350,9 +336,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { this.handleSaveCommentClick )} data-tippy-content={ - node.comment.saved - ? i18n.t('unsave') - : i18n.t('save') + cv.saved ? i18n.t('unsave') : i18n.t('save') } > {this.state.saveLoading ? ( @@ -360,7 +344,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { ) : ( <svg class={`icon icon-inline ${ - node.comment.saved && 'text-warning' + cv.saved && 'text-warning' }`} > <use xlinkHref="#icon-star"></use> @@ -398,14 +382,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { this.handleDeleteClick )} data-tippy-content={ - !node.comment.deleted + !cv.comment.deleted ? i18n.t('delete') : i18n.t('restore') } > <svg class={`icon icon-inline ${ - node.comment.deleted && 'text-danger' + cv.comment.deleted && 'text-danger' }`} > <use xlinkHref="#icon-trash"></use> @@ -416,7 +400,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { {/* Admins and mods can remove comments */} {(this.canMod || this.canAdmin) && ( <> - {!node.comment.removed ? ( + {!cv.comment.removed ? ( <button class="btn btn-link btn-animate text-muted" onClick={linkEvent( @@ -443,7 +427,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { {this.canMod && ( <> {!this.isMod && - (!node.comment.banned_from_community ? ( + (!cv.creator_banned_from_community ? ( <button class="btn btn-link btn-animate text-muted" onClick={linkEvent( @@ -464,8 +448,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { {i18n.t('unban')} </button> ))} - {!node.comment.banned_from_community && - node.comment.creator_local && + {!cv.creator_banned_from_community && + cv.creator.local && (!this.state.showConfirmAppointAsMod ? ( <button class="btn btn-link btn-animate text-muted" @@ -508,7 +492,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { {/* Community creators and admins can transfer community to another mod */} {(this.amCommunityCreator || this.canAdmin) && this.isMod && - node.comment.creator_local && + cv.creator.local && (!this.state.showConfirmTransferCommunity ? ( <button class="btn btn-link btn-animate text-muted" @@ -549,7 +533,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { {this.canAdmin && ( <> {!this.isAdmin && - (!node.comment.banned ? ( + (!cv.creator.banned ? ( <button class="btn btn-link btn-animate text-muted" onClick={linkEvent( @@ -570,8 +554,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { {i18n.t('unban_from_site')} </button> ))} - {!node.comment.banned && - node.comment.creator_local && + {!cv.creator.banned && + cv.creator.local && (!this.state.showConfirmAppointAsAdmin ? ( <button class="btn btn-link btn-animate text-muted" @@ -614,7 +598,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { {/* Site Creator can transfer to another admin */} {this.amSiteCreator && this.isAdmin && - node.comment.creator_local && + cv.creator.local && (!this.state.showConfirmTransferSite ? ( <button class="btn btn-link btn-animate text-muted" @@ -711,7 +695,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { {/* </div> */} <div class="form-group row"> <button type="submit" class="btn btn-secondary"> - {i18n.t('ban')} {node.comment.creator_name} + {i18n.t('ban')} {cv.creator.name} </button> </div> </form> @@ -743,11 +727,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { } get linkBtn() { - let node = this.props.node; + let cv = this.props.node.comment_view; return ( <Link className="btn btn-link btn-animate text-muted" - to={`/post/${node.comment.post_id}/comment/${node.comment.id}`} + to={`/post/${cv.post.id}/comment/${cv.comment.id}`} title={this.props.showContext ? i18n.t('show_context') : i18n.t('link')} > <svg class="icon icon-inline"> @@ -768,7 +752,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { get myComment(): boolean { return ( UserService.Instance.user && - this.props.node.comment.creator_id == UserService.Instance.user.id + this.props.node.comment_view.creator.id == UserService.Instance.user.id ); } @@ -776,8 +760,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { return ( this.props.moderators && isMod( - this.props.moderators.map(m => m.user_id), - this.props.node.comment.creator_id + this.props.moderators.map(m => m.moderator.id), + this.props.node.comment_view.creator.id ) ); } @@ -786,26 +770,26 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { return ( this.props.admins && isMod( - this.props.admins.map(a => a.id), - this.props.node.comment.creator_id + this.props.admins.map(a => a.user.id), + this.props.node.comment_view.creator.id ) ); } get isPostCreator(): boolean { - return this.props.node.comment.creator_id == this.props.postCreatorId; + return this.props.node.comment_view.creator.id == this.props.postCreatorId; } get canMod(): boolean { if (this.props.admins && this.props.moderators) { let adminsThenMods = this.props.admins - .map(a => a.id) - .concat(this.props.moderators.map(m => m.user_id)); + .map(a => a.user.id) + .concat(this.props.moderators.map(m => m.moderator.id)); return canMod( UserService.Instance.user, adminsThenMods, - this.props.node.comment.creator_id + this.props.node.comment_view.creator.id ); } else { return false; @@ -817,8 +801,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { this.props.admins && canMod( UserService.Instance.user, - this.props.admins.map(a => a.id), - this.props.node.comment.creator_id + this.props.admins.map(a => a.user.id), + this.props.node.comment_view.creator.id ) ); } @@ -827,8 +811,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { return ( this.props.moderators && UserService.Instance.user && - this.props.node.comment.creator_id != UserService.Instance.user.id && - UserService.Instance.user.id == this.props.moderators[0].user_id + this.props.node.comment_view.creator.id != UserService.Instance.user.id && + UserService.Instance.user.id == this.props.moderators[0].moderator.id ); } @@ -836,18 +820,18 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { return ( this.props.admins && UserService.Instance.user && - this.props.node.comment.creator_id != UserService.Instance.user.id && - UserService.Instance.user.id == this.props.admins[0].id + this.props.node.comment_view.creator.id != UserService.Instance.user.id && + UserService.Instance.user.id == this.props.admins[0].user.id ); } get commentUnlessRemoved(): string { - let node = this.props.node; - return node.comment.removed + let comment = this.props.node.comment_view.comment; + return comment.removed ? `*${i18n.t('removed')}*` - : node.comment.deleted + : comment.deleted ? `*${i18n.t('deleted')}*` - : node.comment.content; + : comment.content; } handleReplyClick(i: CommentNode) { @@ -861,25 +845,25 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { } handleDeleteClick(i: CommentNode) { - let deleteForm: DeleteCommentForm = { - edit_id: i.props.node.comment.id, - deleted: !i.props.node.comment.deleted, - auth: null, + let comment = i.props.node.comment_view.comment; + let deleteForm: DeleteComment = { + edit_id: comment.id, + deleted: !comment.deleted, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.deleteComment(deleteForm); + WebSocketService.Instance.client.deleteComment(deleteForm); } handleSaveCommentClick(i: CommentNode) { - let saved = - i.props.node.comment.saved == undefined - ? true - : !i.props.node.comment.saved; - let form: SaveCommentForm = { - comment_id: i.props.node.comment.id, - save: saved, + let cv = i.props.node.comment_view; + let save = cv.saved == undefined ? true : !cv.saved; + let form: SaveComment = { + comment_id: cv.comment.id, + save, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.saveComment(form); + WebSocketService.Instance.client.saveComment(form); i.state.saveLoading = true; i.setState(this.state); @@ -908,12 +892,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { this.state.my_vote = new_vote; - let form: CommentLikeForm = { - comment_id: i.comment.id, + let form: CreateCommentLike = { + comment_id: i.comment_view.comment.id, score: this.state.my_vote, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.likeComment(form); + WebSocketService.Instance.client.likeComment(form); this.setState(this.state); setupTippy(); } @@ -935,12 +920,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { this.state.my_vote = new_vote; - let form: CommentLikeForm = { - comment_id: i.comment.id, + let form: CreateCommentLike = { + comment_id: i.comment_view.comment.id, score: this.state.my_vote, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.likeComment(form); + WebSocketService.Instance.client.likeComment(form); this.setState(this.state); setupTippy(); } @@ -961,34 +947,40 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { } handleModRemoveSubmit(i: CommentNode) { - event.preventDefault(); - let form: RemoveCommentForm = { - edit_id: i.props.node.comment.id, - removed: !i.props.node.comment.removed, + let comment = i.props.node.comment_view.comment; + let form: RemoveComment = { + edit_id: comment.id, + removed: !comment.removed, reason: i.state.removeReason, - auth: null, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.removeComment(form); + WebSocketService.Instance.client.removeComment(form); i.state.showRemoveDialog = false; i.setState(i.state); } + isUserMentionType( + item: CommentView | UserMentionView + ): item is UserMentionView { + return (item as UserMentionView).user_mention.id !== undefined; + } + handleMarkRead(i: CommentNode) { - // if it has a user_mention_id field, then its a mention - if (i.props.node.comment.user_mention_id) { - let form: MarkUserMentionAsReadForm = { - user_mention_id: i.props.node.comment.user_mention_id, - read: !i.props.node.comment.read, + if (i.isUserMentionType(i.props.node.comment_view)) { + let form: MarkUserMentionAsRead = { + user_mention_id: i.props.node.comment_view.user_mention.id, + read: !i.props.node.comment_view.user_mention.read, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.markUserMentionAsRead(form); + WebSocketService.Instance.client.markUserMentionAsRead(form); } else { - let form: MarkCommentAsReadForm = { - edit_id: i.props.node.comment.id, - read: !i.props.node.comment.read, - auth: null, + let form: MarkCommentAsRead = { + comment_id: i.props.node.comment_view.comment.id, + read: !i.props.node.comment_view.comment.read, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.markCommentAsRead(form); + WebSocketService.Instance.client.markCommentAsRead(form); } i.state.readLoading = true; @@ -1030,37 +1022,39 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { } handleModBanBothSubmit(i: CommentNode) { - event.preventDefault(); + let cv = i.props.node.comment_view; if (i.state.banType == BanType.Community) { // If its an unban, restore all their data - let ban = !i.props.node.comment.banned_from_community; + let ban = !cv.creator_banned_from_community; if (ban == false) { i.state.removeData = false; } - let form: BanFromCommunityForm = { - user_id: i.props.node.comment.creator_id, - community_id: i.props.node.comment.community_id, + let form: BanFromCommunity = { + user_id: cv.creator.id, + community_id: cv.community.id, ban, remove_data: i.state.removeData, reason: i.state.banReason, expires: getUnixTime(i.state.banExpires), + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.banFromCommunity(form); + WebSocketService.Instance.client.banFromCommunity(form); } else { // If its an unban, restore all their data - let ban = !i.props.node.comment.banned; + let ban = !cv.creator.banned; if (ban == false) { i.state.removeData = false; } - let form: BanUserForm = { - user_id: i.props.node.comment.creator_id, + let form: BanUser = { + user_id: cv.creator.id, ban, remove_data: i.state.removeData, reason: i.state.banReason, expires: getUnixTime(i.state.banExpires), + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.banUser(form); + WebSocketService.Instance.client.banUser(form); } i.state.showBanDialog = false; @@ -1078,12 +1072,14 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { } handleAddModToCommunity(i: CommentNode) { - let form: AddModToCommunityForm = { - user_id: i.props.node.comment.creator_id, - community_id: i.props.node.comment.community_id, + let cv = i.props.node.comment_view; + let form: AddModToCommunity = { + user_id: cv.creator.id, + community_id: cv.community.id, added: !i.isMod, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.addModToCommunity(form); + WebSocketService.Instance.client.addModToCommunity(form); i.state.showConfirmAppointAsMod = false; i.setState(i.state); } @@ -1099,11 +1095,12 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { } handleAddAdmin(i: CommentNode) { - let form: AddAdminForm = { - user_id: i.props.node.comment.creator_id, + let form: AddAdmin = { + user_id: i.props.node.comment_view.creator.id, added: !i.isAdmin, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.addAdmin(form); + WebSocketService.Instance.client.addAdmin(form); i.state.showConfirmAppointAsAdmin = false; i.setState(i.state); } @@ -1119,11 +1116,13 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { } handleTransferCommunity(i: CommentNode) { - let form: TransferCommunityForm = { - community_id: i.props.node.comment.community_id, - user_id: i.props.node.comment.creator_id, + let cv = i.props.node.comment_view; + let form: TransferCommunity = { + community_id: cv.community.id, + user_id: cv.creator.id, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.transferCommunity(form); + WebSocketService.Instance.client.transferCommunity(form); i.state.showConfirmTransferCommunity = false; i.setState(i.state); } @@ -1139,17 +1138,18 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> { } handleTransferSite(i: CommentNode) { - let form: TransferSiteForm = { - user_id: i.props.node.comment.creator_id, + let form: TransferSite = { + user_id: i.props.node.comment_view.creator.id, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.transferSite(form); + WebSocketService.Instance.client.transferSite(form); i.state.showConfirmTransferSite = false; i.setState(i.state); } get isCommentNew(): boolean { let now = moment.utc().subtract(10, 'minutes'); - let then = moment.utc(this.props.node.comment.published); + let then = moment.utc(this.props.node.comment_view.comment.published); return now.isBefore(then); } diff --git a/src/shared/components/comment-nodes.tsx b/src/shared/components/comment-nodes.tsx index 3f99bf3..90a7783 100644 --- a/src/shared/components/comment-nodes.tsx +++ b/src/shared/components/comment-nodes.tsx @@ -1,9 +1,8 @@ import { Component } from 'inferno'; -import { CommentSortType } from '../interfaces'; +import { CommentSortType, CommentNode as CommentNodeI } from '../interfaces'; import { - CommentNode as CommentNodeI, - CommunityUser, - UserView, + CommunityModeratorView, + UserViewSafe, SortType, } from 'lemmy-js-client'; import { commentSort, commentSortSortType } from '../utils'; @@ -13,8 +12,8 @@ interface CommentNodesState {} interface CommentNodesProps { nodes: CommentNodeI[]; - moderators?: CommunityUser[]; - admins?: UserView[]; + moderators?: CommunityModeratorView[]; + admins?: UserViewSafe[]; postCreatorId?: number; noBorder?: boolean; noIndent?: boolean; @@ -41,7 +40,7 @@ export class CommentNodes extends Component< <div className="comments"> {this.sorter().map(node => ( <CommentNode - key={node.comment.id} + key={node.comment_view.comment.id} node={node} noBorder={this.props.noBorder} noIndent={this.props.noIndent} diff --git a/src/shared/components/communities.tsx b/src/shared/components/communities.tsx index 523f9a9..148a413 100644 --- a/src/shared/components/communities.tsx +++ b/src/shared/components/communities.tsx @@ -3,24 +3,23 @@ import { HtmlTags } from './html-tags'; import { Subscription } from 'rxjs'; import { UserOperation, - Community, + CommunityView, ListCommunitiesResponse, CommunityResponse, - FollowCommunityForm, - ListCommunitiesForm, + FollowCommunity, + ListCommunities, SortType, - WebSocketJsonResponse, - Site, + SiteView, } from 'lemmy-js-client'; -import { WebSocketService } from '../services'; +import { UserService, WebSocketService } from '../services'; import { wsJsonToRes, toast, getPageFromProps, isBrowser, - setAuth, setIsoData, wsSubscribe, + wsUserOp, } from '../utils'; import { CommunityLink } from './community-link'; import { i18n } from '../i18next'; @@ -29,10 +28,10 @@ import { InitialFetchRequest } from 'shared/interfaces'; const communityLimit = 100; interface CommunitiesState { - communities: Community[]; + communities: CommunityView[]; page: number; loading: boolean; - site: Site; + site_view: SiteView; } interface CommunitiesProps { @@ -46,7 +45,7 @@ export class Communities extends Component<any, CommunitiesState> { communities: [], loading: true, page: getPageFromProps(this.props), - site: this.isoData.site.site, + site_view: this.isoData.site_res.site_view, }; constructor(props: any, context: any) { @@ -60,7 +59,7 @@ export class Communities extends Component<any, CommunitiesState> { if (this.isoData.path == this.context.router.route.match.url) { this.state.communities = this.isoData.routeData[0].communities; this.state.communities.sort( - (a, b) => b.number_of_subscribers - a.number_of_subscribers + (a, b) => b.counts.subscribers - a.counts.subscribers ); this.state.loading = false; } else { @@ -88,7 +87,7 @@ export class Communities extends Component<any, CommunitiesState> { } get documentTitle(): string { - return `${i18n.t('communities')} - ${this.state.site.name}`; + return `${i18n.t('communities')} - ${this.state.site_view.site.name}`; } render() { @@ -124,27 +123,25 @@ export class Communities extends Component<any, CommunitiesState> { </tr> </thead> <tbody> - {this.state.communities.map(community => ( + {this.state.communities.map(cv => ( <tr> <td> - <CommunityLink community={community} /> - </td> - <td>{community.category_name}</td> - <td class="text-right"> - {community.number_of_subscribers} + <CommunityLink community={cv.community} /> </td> + <td>{cv.category.name}</td> + <td class="text-right">{cv.counts.subscribers}</td> <td class="text-right d-none d-lg-table-cell"> - {community.number_of_posts} + {cv.counts.posts} </td> <td class="text-right d-none d-lg-table-cell"> - {community.number_of_comments} + {cv.counts.comments} </td> <td class="text-right"> - {community.subscribed ? ( + {cv.subscribed ? ( <span class="pointer btn-link" onClick={linkEvent( - community.id, + cv.community.id, this.handleUnsubscribe )} > @@ -154,7 +151,7 @@ export class Communities extends Component<any, CommunitiesState> { <span class="pointer btn-link" onClick={linkEvent( - community.id, + cv.community.id, this.handleSubscribe )} > @@ -212,64 +209,68 @@ export class Communities extends Component<any, CommunitiesState> { } handleUnsubscribe(communityId: number) { - let form: FollowCommunityForm = { + let form: FollowCommunity = { community_id: communityId, follow: false, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.followCommunity(form); + WebSocketService.Instance.client.followCommunity(form); } handleSubscribe(communityId: number) { - let form: FollowCommunityForm = { + let form: FollowCommunity = { community_id: communityId, follow: true, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.followCommunity(form); + WebSocketService.Instance.client.followCommunity(form); } refetch() { - let listCommunitiesForm: ListCommunitiesForm = { + let listCommunitiesForm: ListCommunities = { sort: SortType.TopAll, limit: communityLimit, page: this.state.page, + auth: UserService.Instance.authField(false), }; - WebSocketService.Instance.listCommunities(listCommunitiesForm); + WebSocketService.Instance.client.listCommunities(listCommunitiesForm); } static fetchInitialData(req: InitialFetchRequest): Promise<any>[] { let pathSplit = req.path.split('/'); let page = pathSplit[3] ? Number(pathSplit[3]) : 1; - let listCommunitiesForm: ListCommunitiesForm = { + let listCommunitiesForm: ListCommunities = { sort: SortType.TopAll, limit: communityLimit, page, + auth: req.auth, }; - setAuth(listCommunitiesForm, req.auth); return [req.client.listCommunities(listCommunitiesForm)]; } - parseMessage(msg: WebSocketJsonResponse) { - console.log(msg); - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); return; - } else if (res.op == UserOperation.ListCommunities) { - let data = res.data as ListCommunitiesResponse; + } else if (op == UserOperation.ListCommunities) { + let data = wsJsonToRes<ListCommunitiesResponse>(msg).data; this.state.communities = data.communities; this.state.communities.sort( - (a, b) => b.number_of_subscribers - a.number_of_subscribers + (a, b) => b.counts.subscribers - a.counts.subscribers ); this.state.loading = false; window.scrollTo(0, 0); this.setState(this.state); - } 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; + } else if (op == UserOperation.FollowCommunity) { + let data = wsJsonToRes<CommunityResponse>(msg).data; + let found = this.state.communities.find( + c => c.community.id == data.community_view.community.id + ); + found.subscribed = data.community_view.subscribed; + found.counts.subscribers = data.community_view.counts.subscribers; this.setState(this.state); } } diff --git a/src/shared/components/community-form.tsx b/src/shared/components/community-form.tsx index de7b6b2..4a3dad4 100644 --- a/src/shared/components/community-form.tsx +++ b/src/shared/components/community-form.tsx @@ -2,20 +2,21 @@ import { Component, linkEvent } from 'inferno'; import { Prompt } from 'inferno-router'; import { Subscription } from 'rxjs'; import { - CommunityForm as CommunityFormI, + EditCommunity, + CreateCommunity, UserOperation, Category, CommunityResponse, - WebSocketJsonResponse, - Community, + CommunityView, } from 'lemmy-js-client'; -import { WebSocketService } from '../services'; +import { UserService, WebSocketService } from '../services'; import { wsJsonToRes, capitalizeFirstLetter, toast, randomStr, wsSubscribe, + wsUserOp, } from '../utils'; import { i18n } from '../i18next'; @@ -23,16 +24,16 @@ import { MarkdownTextArea } from './markdown-textarea'; import { ImageUploadForm } from './image-upload-form'; interface CommunityFormProps { - community?: Community; // If a community is given, that means this is an edit + community_view?: CommunityView; // If a community is given, that means this is an edit categories: Category[]; onCancel?(): any; - onCreate?(community: Community): any; - onEdit?(community: Community): any; + onCreate?(community: CommunityView): any; + onEdit?(community: CommunityView): any; enableNsfw: boolean; } interface CommunityFormState { - communityForm: CommunityFormI; + communityForm: CreateCommunity; loading: boolean; } @@ -51,6 +52,7 @@ export class CommunityForm extends Component< nsfw: false, icon: null, banner: null, + auth: UserService.Instance.authField(), }, loading: false, }; @@ -70,17 +72,17 @@ export class CommunityForm extends Component< this.handleBannerUpload = this.handleBannerUpload.bind(this); this.handleBannerRemove = this.handleBannerRemove.bind(this); - if (this.props.community) { + let cv = this.props.community_view; + if (cv) { this.state.communityForm = { - name: this.props.community.name, - title: this.props.community.title, - category_id: this.props.community.category_id, - description: this.props.community.description, - edit_id: this.props.community.id, - nsfw: this.props.community.nsfw, - icon: this.props.community.icon, - banner: this.props.community.banner, - auth: null, + name: cv.community.name, + title: cv.community.title, + category_id: cv.category.id, + description: cv.community.description, + nsfw: cv.community.nsfw, + icon: cv.community.icon, + banner: cv.community.banner, + auth: UserService.Instance.authField(), }; } @@ -88,6 +90,7 @@ export class CommunityForm extends Component< this.subscription = wsSubscribe(this.parseMessage); } + // TODO this should be checked out componentDidUpdate() { if ( !this.state.loading && @@ -119,7 +122,7 @@ export class CommunityForm extends Component< message={i18n.t('block_leaving')} /> <form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}> - {!this.props.community && ( + {!this.props.community_view && ( <div class="form-group row"> <label class="col-12 col-form-label" htmlFor="community-name"> {i18n.t('name')} @@ -250,13 +253,13 @@ export class CommunityForm extends Component< <svg class="icon icon-spinner spin"> <use xlinkHref="#icon-spinner"></use> </svg> - ) : this.props.community ? ( + ) : this.props.community_view ? ( capitalizeFirstLetter(i18n.t('save')) ) : ( capitalizeFirstLetter(i18n.t('create')) )} </button> - {this.props.community && ( + {this.props.community_view && ( <button type="button" class="btn btn-secondary" @@ -275,10 +278,14 @@ export class CommunityForm extends Component< handleCreateCommunitySubmit(i: CommunityForm, event: any) { event.preventDefault(); i.state.loading = true; - if (i.props.community) { - WebSocketService.Instance.editCommunity(i.state.communityForm); + if (i.props.community_view) { + let form: EditCommunity = { + ...i.state.communityForm, + edit_id: i.props.community_view.community.id, + }; + WebSocketService.Instance.client.editCommunity(form); } else { - WebSocketService.Instance.createCommunity(i.state.communityForm); + WebSocketService.Instance.client.createCommunity(i.state.communityForm); } i.setState(i.state); } @@ -332,22 +339,21 @@ export class CommunityForm extends Component< this.setState(this.state); } - parseMessage(msg: WebSocketJsonResponse) { - let res = wsJsonToRes(msg); - console.log(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); this.state.loading = false; this.setState(this.state); return; - } else if (res.op == UserOperation.CreateCommunity) { - let data = res.data as CommunityResponse; + } else if (op == UserOperation.CreateCommunity) { + let data = wsJsonToRes<CommunityResponse>(msg).data; this.state.loading = false; - this.props.onCreate(data.community); - } else if (res.op == UserOperation.EditCommunity) { - let data = res.data as CommunityResponse; + this.props.onCreate(data.community_view); + } else if (op == UserOperation.EditCommunity) { + let data = wsJsonToRes<CommunityResponse>(msg).data; this.state.loading = false; - this.props.onEdit(data.community); + this.props.onEdit(data.community_view); } } } diff --git a/src/shared/components/community-link.tsx b/src/shared/components/community-link.tsx index 8bf73ca..b8f3bc0 100644 --- a/src/shared/components/community-link.tsx +++ b/src/shared/components/community-link.tsx @@ -1,19 +1,12 @@ import { Component } from 'inferno'; import { Link } from 'inferno-router'; -import { Community } from 'lemmy-js-client'; +import { CommunitySafe } from 'lemmy-js-client'; import { hostname, showAvatars } from '../utils'; import { PictrsImage } from './pictrs-image'; -interface CommunityOther { - name: string; - id?: number; // Necessary if its federated - icon?: string; - local?: boolean; - actor_id?: string; -} - interface CommunityLinkProps { - community: Community | CommunityOther; + // TODO figure this out better + community: CommunitySafe; realLink?: boolean; useApubName?: boolean; muted?: boolean; diff --git a/src/shared/components/community.tsx b/src/shared/components/community.tsx index c7d0a27..aa0cbf0 100644 --- a/src/shared/components/community.tsx +++ b/src/shared/components/community.tsx @@ -6,19 +6,18 @@ import { GetCommunityResponse, CommunityResponse, SortType, - Post, - GetPostsForm, - GetCommunityForm, + PostView, + GetPosts, + GetCommunity, ListingType, GetPostsResponse, PostResponse, AddModToCommunityResponse, BanFromCommunityResponse, - Comment, - GetCommentsForm, + CommentView, + GetComments, GetCommentsResponse, CommentResponse, - WebSocketJsonResponse, GetSiteResponse, Category, ListCategoriesResponse, @@ -50,8 +49,8 @@ import { setIsoData, wsSubscribe, isBrowser, - setAuth, communityRSSUrl, + wsUserOp, } from '../utils'; import { i18n } from '../i18next'; @@ -63,8 +62,8 @@ interface State { communityLoading: boolean; postsLoading: boolean; commentsLoading: boolean; - posts: Post[]; - comments: Comment[]; + posts: PostView[]; + comments: CommentView[]; dataType: DataType; sort: SortType; page: number; @@ -98,7 +97,7 @@ export class Community extends Component<any, State> { dataType: getDataTypeFromProps(this.props), sort: getSortTypeFromProps(this.props), page: getPageFromProps(this.props), - siteRes: this.isoData.site, + siteRes: this.isoData.site_res, categories: [], }; @@ -127,17 +126,18 @@ export class Community extends Component<any, State> { } else { this.fetchCommunity(); this.fetchData(); - WebSocketService.Instance.listCategories(); + WebSocketService.Instance.client.listCategories(); } setupTippy(); } fetchCommunity() { - let form: GetCommunityForm = { + let form: GetCommunity = { id: this.state.communityId ? this.state.communityId : null, name: this.state.communityName ? this.state.communityName : null, + auth: UserService.Instance.authField(false), }; - WebSocketService.Instance.getCommunity(form); + WebSocketService.Instance.client.getCommunity(form); } componentWillUnmount() { @@ -169,8 +169,8 @@ export class Community extends Component<any, State> { id = Number(idOrName); } - let communityForm: GetCommunityForm = id ? { id } : { name: name_ }; - setAuth(communityForm, req.auth); + let communityForm: GetCommunity = id ? { id } : { name: name_ }; + communityForm.auth = req.auth; promises.push(req.client.getCommunity(communityForm)); let dataType: DataType = pathSplit[4] @@ -186,24 +186,24 @@ export class Community extends Component<any, State> { let page = pathSplit[8] ? Number(pathSplit[8]) : 1; if (dataType == DataType.Post) { - let getPostsForm: GetPostsForm = { + let getPostsForm: GetPosts = { page, limit: fetchLimit, sort, type_: ListingType.Community, + auth: req.auth, }; this.setIdOrName(getPostsForm, id, name_); - setAuth(getPostsForm, req.auth); promises.push(req.client.getPosts(getPostsForm)); } else { - let getCommentsForm: GetCommentsForm = { + let getCommentsForm: GetComments = { page, limit: fetchLimit, sort, type_: ListingType.Community, + auth: req.auth, }; this.setIdOrName(getCommentsForm, id, name_); - setAuth(getCommentsForm, req.auth); promises.push(req.client.getComments(getCommentsForm)); } @@ -232,10 +232,11 @@ export class Community extends Component<any, State> { } get documentTitle(): string { - return `${this.state.communityRes.community.title} - ${this.state.siteRes.site.name}`; + return `${this.state.communityRes.community_view.community.title} - ${this.state.siteRes.site_view.site.name}`; } render() { + let cv = this.state.communityRes.community_view; return ( <div class="container"> {this.state.communityLoading ? ( @@ -250,8 +251,8 @@ export class Community extends Component<any, State> { <HtmlTags title={this.documentTitle} path={this.context.router.route.match.url} - description={this.state.communityRes.community.description} - image={this.state.communityRes.community.icon} + description={cv.community.description} + image={cv.community.icon} /> {this.communityInfo()} {this.selects()} @@ -260,11 +261,11 @@ export class Community extends Component<any, State> { </div> <div class="col-12 col-md-4"> <Sidebar - community={this.state.communityRes.community} + community_view={cv} moderators={this.state.communityRes.moderators} admins={this.state.siteRes.admins} online={this.state.communityRes.online} - enableNsfw={this.state.siteRes.site.enable_nsfw} + enableNsfw={this.state.siteRes.site_view.site.enable_nsfw} categories={this.state.categories} /> </div> @@ -275,6 +276,7 @@ export class Community extends Component<any, State> { } listings() { + let site = this.state.siteRes.site_view.site; return this.state.dataType == DataType.Post ? ( this.state.postsLoading ? ( <h5> @@ -287,8 +289,8 @@ export class Community extends Component<any, State> { posts={this.state.posts} removeDuplicates sort={this.state.sort} - enableDownvotes={this.state.siteRes.site.enable_downvotes} - enableNsfw={this.state.siteRes.site.enable_nsfw} + enableDownvotes={site.enable_downvotes} + enableNsfw={site.enable_nsfw} /> ) ) : this.state.commentsLoading ? ( @@ -303,21 +305,19 @@ export class Community extends Component<any, State> { noIndent sortType={this.state.sort} showContext - enableDownvotes={this.state.siteRes.site.enable_downvotes} + enableDownvotes={site.enable_downvotes} /> ); } communityInfo() { + let community = this.state.communityRes.community_view.community; return ( <div> - <BannerIconHeader - banner={this.state.communityRes.community.banner} - icon={this.state.communityRes.community.icon} - /> - <h5 class="mb-0">{this.state.communityRes.community.title}</h5> + <BannerIconHeader banner={community.banner} icon={community.icon} /> + <h5 class="mb-0">{community.title}</h5> <CommunityLink - community={this.state.communityRes.community} + community={community} realLink useApubName muted @@ -342,7 +342,7 @@ export class Community extends Component<any, State> { </span> <a href={communityRSSUrl( - this.state.communityRes.community.actor_id, + this.state.communityRes.community_view.community.actor_id, this.state.sort )} target="_blank" @@ -405,137 +405,141 @@ export class Community extends Component<any, State> { const sortStr = paramUpdates.sort || this.state.sort; const page = paramUpdates.page || this.state.page; this.props.history.push( - `/c/${this.state.communityRes.community.name}/data_type/${dataTypeStr}/sort/${sortStr}/page/${page}` + `/c/${this.state.communityRes.community_view.community.name}/data_type/${dataTypeStr}/sort/${sortStr}/page/${page}` ); } fetchData() { if (this.state.dataType == DataType.Post) { - let getPostsForm: GetPostsForm = { + let form: GetPosts = { page: this.state.page, limit: fetchLimit, sort: this.state.sort, type_: ListingType.Community, community_id: this.state.communityId, community_name: this.state.communityName, + auth: UserService.Instance.authField(false), }; - WebSocketService.Instance.getPosts(getPostsForm); + WebSocketService.Instance.client.getPosts(form); } else { - let getCommentsForm: GetCommentsForm = { + let form: GetComments = { page: this.state.page, limit: fetchLimit, sort: this.state.sort, type_: ListingType.Community, community_id: this.state.communityId, community_name: this.state.communityName, + auth: UserService.Instance.authField(false), }; - WebSocketService.Instance.getComments(getCommentsForm); + WebSocketService.Instance.client.getComments(form); } } - parseMessage(msg: WebSocketJsonResponse) { - console.log(msg); - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); this.context.router.history.push('/'); return; } else if (msg.reconnect) { - WebSocketService.Instance.communityJoin({ - community_id: this.state.communityRes.community.id, + WebSocketService.Instance.client.communityJoin({ + community_id: this.state.communityRes.community_view.community.id, }); this.fetchData(); - } else if (res.op == UserOperation.GetCommunity) { - let data = res.data as GetCommunityResponse; + } else if (op == UserOperation.GetCommunity) { + let data = wsJsonToRes<GetCommunityResponse>(msg).data; this.state.communityRes = data; this.state.communityLoading = false; this.setState(this.state); - WebSocketService.Instance.communityJoin({ - community_id: data.community.id, + // TODO why is there no auth in this form? + WebSocketService.Instance.client.communityJoin({ + community_id: data.community_view.community.id, }); } else if ( - res.op == UserOperation.EditCommunity || - res.op == UserOperation.DeleteCommunity || - res.op == UserOperation.RemoveCommunity + op == UserOperation.EditCommunity || + op == UserOperation.DeleteCommunity || + op == UserOperation.RemoveCommunity ) { - let data = res.data as CommunityResponse; - this.state.communityRes.community = data.community; + let data = wsJsonToRes<CommunityResponse>(msg).data; + this.state.communityRes.community_view = data.community_view; this.setState(this.state); - } else if (res.op == UserOperation.FollowCommunity) { - let data = res.data as CommunityResponse; - this.state.communityRes.community.subscribed = data.community.subscribed; - this.state.communityRes.community.number_of_subscribers = - data.community.number_of_subscribers; + } else if (op == UserOperation.FollowCommunity) { + let data = wsJsonToRes<CommunityResponse>(msg).data; + this.state.communityRes.community_view.subscribed = + data.community_view.subscribed; + this.state.communityRes.community_view.counts.subscribers = + data.community_view.counts.subscribers; this.setState(this.state); - } else if (res.op == UserOperation.GetPosts) { - let data = res.data as GetPostsResponse; + } else if (op == UserOperation.GetPosts) { + let data = wsJsonToRes<GetPostsResponse>(msg).data; this.state.posts = data.posts; this.state.postsLoading = false; this.setState(this.state); setupTippy(); } else if ( - res.op == UserOperation.EditPost || - res.op == UserOperation.DeletePost || - res.op == UserOperation.RemovePost || - res.op == UserOperation.LockPost || - res.op == UserOperation.StickyPost || - res.op == UserOperation.SavePost + op == UserOperation.EditPost || + op == UserOperation.DeletePost || + op == UserOperation.RemovePost || + op == UserOperation.LockPost || + op == UserOperation.StickyPost || + op == UserOperation.SavePost ) { - let data = res.data as PostResponse; - editPostFindRes(data, this.state.posts); + let data = wsJsonToRes<PostResponse>(msg).data; + editPostFindRes(data.post_view, this.state.posts); this.setState(this.state); - } else if (res.op == UserOperation.CreatePost) { - let data = res.data as PostResponse; - this.state.posts.unshift(data.post); - notifyPost(data.post, this.context.router); + } else if (op == UserOperation.CreatePost) { + let data = wsJsonToRes<PostResponse>(msg).data; + this.state.posts.unshift(data.post_view); + notifyPost(data.post_view, this.context.router); this.setState(this.state); - } else if (res.op == UserOperation.CreatePostLike) { - let data = res.data as PostResponse; - createPostLikeFindRes(data, this.state.posts); + } else if (op == UserOperation.CreatePostLike) { + let data = wsJsonToRes<PostResponse>(msg).data; + createPostLikeFindRes(data.post_view, this.state.posts); this.setState(this.state); - } else if (res.op == UserOperation.AddModToCommunity) { - let data = res.data as AddModToCommunityResponse; + } else if (op == UserOperation.AddModToCommunity) { + let data = wsJsonToRes<AddModToCommunityResponse>(msg).data; this.state.communityRes.moderators = data.moderators; this.setState(this.state); - } else if (res.op == UserOperation.BanFromCommunity) { - let data = res.data as BanFromCommunityResponse; + } else if (op == UserOperation.BanFromCommunity) { + let data = wsJsonToRes<BanFromCommunityResponse>(msg).data; + // TODO this might be incorrect this.state.posts - .filter(p => p.creator_id == data.user.id) - .forEach(p => (p.banned = data.banned)); + .filter(p => p.creator.id == data.user_view.user.id) + .forEach(p => (p.creator_banned_from_community = data.banned)); this.setState(this.state); - } else if (res.op == UserOperation.GetComments) { - let data = res.data as GetCommentsResponse; + } else if (op == UserOperation.GetComments) { + let data = wsJsonToRes<GetCommentsResponse>(msg).data; this.state.comments = data.comments; this.state.commentsLoading = false; this.setState(this.state); } else if ( - res.op == UserOperation.EditComment || - res.op == UserOperation.DeleteComment || - res.op == UserOperation.RemoveComment + op == UserOperation.EditComment || + op == UserOperation.DeleteComment || + op == UserOperation.RemoveComment ) { - let data = res.data as CommentResponse; - editCommentRes(data, this.state.comments); + let data = wsJsonToRes<CommentResponse>(msg).data; + editCommentRes(data.comment_view, this.state.comments); this.setState(this.state); - } else if (res.op == UserOperation.CreateComment) { - let data = res.data as CommentResponse; + } else if (op == UserOperation.CreateComment) { + let data = wsJsonToRes<CommentResponse>(msg).data; // Necessary since it might be a user reply if (data.recipient_ids.length == 0) { - this.state.comments.unshift(data.comment); + this.state.comments.unshift(data.comment_view); this.setState(this.state); } - } else if (res.op == UserOperation.SaveComment) { - let data = res.data as CommentResponse; - saveCommentRes(data, this.state.comments); + } else if (op == UserOperation.SaveComment) { + let data = wsJsonToRes<CommentResponse>(msg).data; + saveCommentRes(data.comment_view, this.state.comments); this.setState(this.state); - } else if (res.op == UserOperation.CreateCommentLike) { - let data = res.data as CommentResponse; - createCommentLikeRes(data, this.state.comments); + } else if (op == UserOperation.CreateCommentLike) { + let data = wsJsonToRes<CommentResponse>(msg).data; + createCommentLikeRes(data.comment_view, this.state.comments); this.setState(this.state); - } else if (res.op == UserOperation.ListCategories) { - let data = res.data as ListCategoriesResponse; + } else if (op == UserOperation.ListCategories) { + let data = wsJsonToRes<ListCategoriesResponse>(msg).data; this.state.categories = data.categories; this.setState(this.state); } diff --git a/src/shared/components/create-community.tsx b/src/shared/components/create-community.tsx index c96a826..b989183 100644 --- a/src/shared/components/create-community.tsx +++ b/src/shared/components/create-community.tsx @@ -3,10 +3,9 @@ import { Subscription } from 'rxjs'; import { CommunityForm } from './community-form'; import { HtmlTags } from './html-tags'; import { - Community, + CommunityView, UserOperation, - WebSocketJsonResponse, - Site, + SiteView, ListCategoriesResponse, Category, } from 'lemmy-js-client'; @@ -16,13 +15,14 @@ import { wsJsonToRes, wsSubscribe, isBrowser, + wsUserOp, } from '../utils'; import { WebSocketService, UserService } from '../services'; import { i18n } from '../i18next'; import { InitialFetchRequest } from 'shared/interfaces'; interface CreateCommunityState { - site: Site; + site_view: SiteView; categories: Category[]; loading: boolean; } @@ -31,7 +31,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> { private isoData = setIsoData(this.context); private subscription: Subscription; private emptyState: CreateCommunityState = { - site: this.isoData.site.site, + site_view: this.isoData.site_res.site_view, categories: [], loading: true, }; @@ -53,7 +53,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> { this.state.categories = this.isoData.routeData[0].categories; this.state.loading = false; } else { - WebSocketService.Instance.listCategories(); + WebSocketService.Instance.client.listCategories(); } } @@ -64,7 +64,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> { } get documentTitle(): string { - return `${i18n.t('create_community')} - ${this.state.site.name}`; + return `${i18n.t('create_community')} - ${this.state.site_view.site.name}`; } render() { @@ -87,7 +87,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> { <CommunityForm categories={this.state.categories} onCreate={this.handleCommunityCreate} - enableNsfw={this.state.site.enable_nsfw} + enableNsfw={this.state.site_view.site.enable_nsfw} /> </div> </div> @@ -96,22 +96,21 @@ export class CreateCommunity extends Component<any, CreateCommunityState> { ); } - handleCommunityCreate(community: Community) { - this.props.history.push(`/c/${community.name}`); + handleCommunityCreate(cv: CommunityView) { + this.props.history.push(`/c/${cv.community.name}`); } static fetchInitialData(req: InitialFetchRequest): Promise<any>[] { return [req.client.listCategories()]; } - parseMessage(msg: WebSocketJsonResponse) { - console.log(msg); - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { // Toast errors are already handled by community-form return; - } else if (res.op == UserOperation.ListCategories) { - let data = res.data as ListCategoriesResponse; + } else if (op == UserOperation.ListCategories) { + let data = wsJsonToRes<ListCategoriesResponse>(msg).data; this.state.categories = data.categories; this.state.loading = false; this.setState(this.state); diff --git a/src/shared/components/create-post.tsx b/src/shared/components/create-post.tsx index a0eeb35..35fd6a8 100644 --- a/src/shared/components/create-post.tsx +++ b/src/shared/components/create-post.tsx @@ -4,29 +4,28 @@ import { PostForm } from './post-form'; import { HtmlTags } from './html-tags'; import { isBrowser, - setAuth, setIsoData, toast, wsJsonToRes, wsSubscribe, + wsUserOp, } from '../utils'; import { UserService, WebSocketService } from '../services'; import { UserOperation, - PostFormParams, - WebSocketJsonResponse, ListCommunitiesResponse, - Community, - Site, - ListCommunitiesForm, + CommunityView, + SiteView, + ListCommunities, SortType, + PostView, } from 'lemmy-js-client'; import { i18n } from '../i18next'; -import { InitialFetchRequest } from 'shared/interfaces'; +import { InitialFetchRequest, PostFormParams } from 'shared/interfaces'; interface CreatePostState { - site: Site; - communities: Community[]; + site_view: SiteView; + communities: CommunityView[]; loading: boolean; } @@ -34,7 +33,7 @@ export class CreatePost extends Component<any, CreatePostState> { private isoData = setIsoData(this.context); private subscription: Subscription; private emptyState: CreatePostState = { - site: this.isoData.site.site, + site_view: this.isoData.site_res.site_view, communities: [], loading: true, }; @@ -62,11 +61,12 @@ export class CreatePost extends Component<any, CreatePostState> { } refetch() { - let listCommunitiesForm: ListCommunitiesForm = { + let listCommunitiesForm: ListCommunities = { sort: SortType.TopAll, limit: 9999, + auth: UserService.Instance.authField(false), }; - WebSocketService.Instance.listCommunities(listCommunitiesForm); + WebSocketService.Instance.client.listCommunities(listCommunitiesForm); } componentWillUnmount() { @@ -76,7 +76,7 @@ export class CreatePost extends Component<any, CreatePostState> { } get documentTitle(): string { - return `${i18n.t('create_post')} - ${this.state.site.name}`; + return `${i18n.t('create_post')} - ${this.state.site_view.site.name}`; } render() { @@ -100,8 +100,8 @@ export class CreatePost extends Component<any, CreatePostState> { communities={this.state.communities} onCreate={this.handlePostCreate} params={this.params} - enableDownvotes={this.state.site.enable_downvotes} - enableNsfw={this.state.site.enable_nsfw} + enableDownvotes={this.state.site_view.site.enable_downvotes} + enableNsfw={this.state.site_view.site.enable_nsfw} /> </div> </div> @@ -149,27 +149,26 @@ export class CreatePost extends Component<any, CreatePostState> { return null; } - handlePostCreate(id: number) { - this.props.history.push(`/post/${id}`); + handlePostCreate(post_view: PostView) { + this.props.history.push(`/post/${post_view.post.id}`); } static fetchInitialData(req: InitialFetchRequest): Promise<any>[] { - let listCommunitiesForm: ListCommunitiesForm = { + let listCommunitiesForm: ListCommunities = { sort: SortType.TopAll, limit: 9999, + auth: req.auth, }; - setAuth(listCommunitiesForm, req.auth); return [req.client.listCommunities(listCommunitiesForm)]; } - parseMessage(msg: WebSocketJsonResponse) { - console.log(msg); - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); return; - } else if (res.op == UserOperation.ListCommunities) { - let data = res.data as ListCommunitiesResponse; + } else if (op == UserOperation.ListCommunities) { + let data = wsJsonToRes<ListCommunitiesResponse>(msg).data; this.state.communities = data.communities; this.state.loading = false; this.setState(this.state); diff --git a/src/shared/components/create-private-message.tsx b/src/shared/components/create-private-message.tsx index e08bc56..90e17c9 100644 --- a/src/shared/components/create-private-message.tsx +++ b/src/shared/components/create-private-message.tsx @@ -4,22 +4,21 @@ import { PrivateMessageForm } from './private-message-form'; import { HtmlTags } from './html-tags'; import { UserService, WebSocketService } from '../services'; import { - Site, - WebSocketJsonResponse, + SiteView, UserOperation, - UserDetailsResponse, - UserView, + GetUserDetailsResponse, + UserViewSafe, SortType, - GetUserDetailsForm, + GetUserDetails, } from 'lemmy-js-client'; import { getRecipientIdFromProps, isBrowser, - setAuth, setIsoData, toast, wsJsonToRes, wsSubscribe, + wsUserOp, } from '../utils'; import { i18n } from '../i18next'; import { InitialFetchRequest } from 'shared/interfaces'; @@ -27,8 +26,8 @@ import { InitialFetchRequest } from 'shared/interfaces'; interface CreatePrivateMessageProps {} interface CreatePrivateMessageState { - site: Site; - recipient: UserView; + site_view: SiteView; + recipient: UserViewSafe; recipient_id: number; loading: boolean; } @@ -40,7 +39,7 @@ export class CreatePrivateMessage extends Component< private isoData = setIsoData(this.context); private subscription: Subscription; private emptyState: CreatePrivateMessageState = { - site: this.isoData.site.site, + site_view: this.isoData.site_res.site_view, recipient: undefined, recipient_id: getRecipientIdFromProps(this.props), loading: true, @@ -70,27 +69,30 @@ export class CreatePrivateMessage extends Component< } fetchUserDetails() { - let form: GetUserDetailsForm = { + let form: GetUserDetails = { user_id: this.state.recipient_id, sort: SortType.New, saved_only: false, + auth: UserService.Instance.authField(false), }; - WebSocketService.Instance.getUserDetails(form); + WebSocketService.Instance.client.getUserDetails(form); } static fetchInitialData(req: InitialFetchRequest): Promise<any>[] { let user_id = Number(req.path.split('/').pop()); - let form: GetUserDetailsForm = { + let form: GetUserDetails = { user_id, sort: SortType.New, saved_only: false, + auth: req.auth, }; - setAuth(form, req.auth); return [req.client.getUserDetails(form)]; } get documentTitle(): string { - return `${i18n.t('create_private_message')} - ${this.state.site.name}`; + return `${i18n.t('create_private_message')} - ${ + this.state.site_view.site.name + }`; } componentWillUnmount() { @@ -118,7 +120,7 @@ export class CreatePrivateMessage extends Component< <h5>{i18n.t('create_private_message')}</h5> <PrivateMessageForm onCreate={this.handlePrivateMessageCreate} - recipient={this.state.recipient} + recipient={this.state.recipient.user} /> </div> </div> @@ -134,16 +136,16 @@ export class CreatePrivateMessage extends Component< this.context.router.history.push(`/`); } - parseMessage(msg: WebSocketJsonResponse) { - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); this.state.loading = false; this.setState(this.state); return; - } else if (res.op == UserOperation.GetUserDetails) { - let data = res.data as UserDetailsResponse; - this.state.recipient = data.user; + } else if (op == UserOperation.GetUserDetails) { + let data = wsJsonToRes<GetUserDetailsResponse>(msg).data; + this.state.recipient = data.user_view; this.state.loading = false; this.setState(this.state); } diff --git a/src/shared/components/inbox.tsx b/src/shared/components/inbox.tsx index 04eb043..5d01fef 100644 --- a/src/shared/components/inbox.tsx +++ b/src/shared/components/inbox.tsx @@ -2,26 +2,25 @@ import { Component, linkEvent } from 'inferno'; import { Subscription } from 'rxjs'; import { UserOperation, - Comment, + CommentView, SortType, - GetRepliesForm, + GetReplies, GetRepliesResponse, - GetUserMentionsForm, + GetUserMentions, GetUserMentionsResponse, UserMentionResponse, CommentResponse, - WebSocketJsonResponse, - PrivateMessage as PrivateMessageI, - GetPrivateMessagesForm, + PrivateMessageView, + GetPrivateMessages, PrivateMessagesResponse, PrivateMessageResponse, - Site, + SiteView, + UserMentionView, } from 'lemmy-js-client'; import { WebSocketService, UserService } from '../services'; import { wsJsonToRes, fetchLimit, - isCommentType, toast, editCommentRes, saveCommentRes, @@ -30,8 +29,8 @@ import { setupTippy, setIsoData, wsSubscribe, - setAuth, isBrowser, + wsUserOp, } from '../utils'; import { CommentNodes } from './comment-nodes'; import { PrivateMessage } from './private-message'; @@ -52,17 +51,27 @@ enum MessageType { Messages, } -type ReplyType = Comment | PrivateMessageI; +enum ReplyEnum { + Reply, + Mention, + Message, +} +type ReplyType = { + id: number; + type_: ReplyEnum; + view: CommentView | PrivateMessageView | UserMentionView; + published: string; +}; interface InboxState { unreadOrAll: UnreadOrAll; messageType: MessageType; - replies: Comment[]; - mentions: Comment[]; - messages: PrivateMessageI[]; + replies: CommentView[]; + mentions: UserMentionView[]; + messages: PrivateMessageView[]; sort: SortType; page: number; - site: Site; + site_view: SiteView; loading: boolean; } @@ -77,7 +86,7 @@ export class Inbox extends Component<any, InboxState> { messages: [], sort: SortType.New, page: 1, - site: this.isoData.site.site, + site_view: this.isoData.site_res.site_view, loading: true, }; @@ -115,7 +124,7 @@ export class Inbox extends Component<any, InboxState> { get documentTitle(): string { return `@${UserService.Instance.user.name} ${i18n.t('inbox')} - ${ - this.state.site.name + this.state.site_view.site.name }`; } @@ -288,33 +297,71 @@ export class Inbox extends Component<any, InboxState> { } combined(): ReplyType[] { - return [ - ...this.state.replies, - ...this.state.mentions, - ...this.state.messages, - ].sort((a, b) => b.published.localeCompare(a.published)); + let id = 0; + let replies: ReplyType[] = this.state.replies.map(r => ({ + id: id++, + type_: ReplyEnum.Reply, + view: r, + published: r.comment.published, + })); + let mentions: ReplyType[] = this.state.mentions.map(r => ({ + id: id++, + type_: ReplyEnum.Mention, + view: r, + published: r.comment.published, + })); + let messages: ReplyType[] = this.state.messages.map(r => ({ + id: id++, + type_: ReplyEnum.Message, + view: r, + published: r.private_message.published, + })); + + return [...replies, ...mentions, ...messages].sort((a, b) => + b.published.localeCompare(a.published) + ); + } + + renderReplyType(i: ReplyType) { + switch (i.type_) { + case ReplyEnum.Reply: + return ( + <CommentNodes + key={i.id} + nodes={[{ comment_view: i.view as CommentView }]} + noIndent + markable + showCommunity + showContext + enableDownvotes={this.state.site_view.site.enable_downvotes} + /> + ); + case ReplyEnum.Mention: + return ( + <CommentNodes + key={i.id} + nodes={[{ comment_view: i.view as UserMentionView }]} + noIndent + markable + showCommunity + showContext + enableDownvotes={this.state.site_view.site.enable_downvotes} + /> + ); + case ReplyEnum.Message: + return ( + <PrivateMessage + key={i.id} + private_message_view={i.view as PrivateMessageView} + /> + ); + default: + return <div />; + } } all() { - return ( - <div> - {this.combined().map(i => - isCommentType(i) ? ( - <CommentNodes - key={i.id} - nodes={[{ comment: i }]} - noIndent - markable - showCommunity - showContext - enableDownvotes={this.state.site.enable_downvotes} - /> - ) : ( - <PrivateMessage key={i.id} privateMessage={i} /> - ) - )} - </div> - ); + return <div>{this.combined().map(i => this.renderReplyType(i))}</div>; } replies() { @@ -326,7 +373,7 @@ export class Inbox extends Component<any, InboxState> { markable showCommunity showContext - enableDownvotes={this.state.site.enable_downvotes} + enableDownvotes={this.state.site_view.site.enable_downvotes} /> </div> ); @@ -335,15 +382,15 @@ export class Inbox extends Component<any, InboxState> { mentions() { return ( <div> - {this.state.mentions.map(mention => ( + {this.state.mentions.map(umv => ( <CommentNodes - key={mention.id} - nodes={[{ comment: mention }]} + key={umv.user_mention.id} + nodes={[{ comment_view: umv }]} noIndent markable showCommunity showContext - enableDownvotes={this.state.site.enable_downvotes} + enableDownvotes={this.state.site_view.site.enable_downvotes} /> ))} </div> @@ -353,8 +400,11 @@ export class Inbox extends Component<any, InboxState> { messages() { return ( <div> - {this.state.messages.map(message => ( - <PrivateMessage key={message.id} privateMessage={message} /> + {this.state.messages.map(pmv => ( + <PrivateMessage + key={pmv.private_message.id} + private_message_view={pmv} + /> ))} </div> ); @@ -413,58 +463,61 @@ export class Inbox extends Component<any, InboxState> { let promises: Promise<any>[] = []; // It can be /u/me, or /username/1 - let repliesForm: GetRepliesForm = { + let repliesForm: GetReplies = { sort: SortType.New, unread_only: true, page: 1, limit: fetchLimit, + auth: req.auth, }; - setAuth(repliesForm, req.auth); promises.push(req.client.getReplies(repliesForm)); - let userMentionsForm: GetUserMentionsForm = { + let userMentionsForm: GetUserMentions = { sort: SortType.New, unread_only: true, page: 1, limit: fetchLimit, + auth: req.auth, }; - setAuth(userMentionsForm, req.auth); promises.push(req.client.getUserMentions(userMentionsForm)); - let privateMessagesForm: GetPrivateMessagesForm = { + let privateMessagesForm: GetPrivateMessages = { unread_only: true, page: 1, limit: fetchLimit, + auth: req.auth, }; - setAuth(privateMessagesForm, req.auth); promises.push(req.client.getPrivateMessages(privateMessagesForm)); return promises; } refetch() { - let repliesForm: GetRepliesForm = { + let repliesForm: GetReplies = { sort: this.state.sort, unread_only: this.state.unreadOrAll == UnreadOrAll.Unread, page: this.state.page, limit: fetchLimit, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.getReplies(repliesForm); + WebSocketService.Instance.client.getReplies(repliesForm); - let userMentionsForm: GetUserMentionsForm = { + let userMentionsForm: GetUserMentions = { sort: this.state.sort, unread_only: this.state.unreadOrAll == UnreadOrAll.Unread, page: this.state.page, limit: fetchLimit, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.getUserMentions(userMentionsForm); + WebSocketService.Instance.client.getUserMentions(userMentionsForm); - let privateMessagesForm: GetPrivateMessagesForm = { + let privateMessagesForm: GetPrivateMessages = { unread_only: this.state.unreadOrAll == UnreadOrAll.Unread, page: this.state.page, limit: fetchLimit, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.getPrivateMessages(privateMessagesForm); + WebSocketService.Instance.client.getPrivateMessages(privateMessagesForm); } handleSortChange(val: SortType) { @@ -475,7 +528,9 @@ export class Inbox extends Component<any, InboxState> { } markAllAsRead(i: Inbox) { - WebSocketService.Instance.markAllAsRead(); + WebSocketService.Instance.client.markAllAsRead({ + auth: UserService.Instance.authField(), + }); i.state.replies = []; i.state.mentions = []; i.state.messages = []; @@ -484,148 +539,182 @@ export class Inbox extends Component<any, InboxState> { i.setState(i.state); } - parseMessage(msg: WebSocketJsonResponse) { - console.log(msg); - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); return; } else if (msg.reconnect) { this.refetch(); - } else if (res.op == UserOperation.GetReplies) { - let data = res.data as GetRepliesResponse; + } else if (op == UserOperation.GetReplies) { + let data = wsJsonToRes<GetRepliesResponse>(msg).data; this.state.replies = data.replies; this.state.loading = false; this.sendUnreadCount(); window.scrollTo(0, 0); this.setState(this.state); setupTippy(); - } else if (res.op == UserOperation.GetUserMentions) { - let data = res.data as GetUserMentionsResponse; + } else if (op == UserOperation.GetUserMentions) { + let data = wsJsonToRes<GetUserMentionsResponse>(msg).data; this.state.mentions = data.mentions; this.sendUnreadCount(); window.scrollTo(0, 0); this.setState(this.state); setupTippy(); - } else if (res.op == UserOperation.GetPrivateMessages) { - let data = res.data as PrivateMessagesResponse; - this.state.messages = data.messages; + } else if (op == UserOperation.GetPrivateMessages) { + let data = wsJsonToRes<PrivateMessagesResponse>(msg).data; + this.state.messages = data.private_messages; this.sendUnreadCount(); window.scrollTo(0, 0); this.setState(this.state); setupTippy(); - } 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 + } else if (op == UserOperation.EditPrivateMessage) { + let data = wsJsonToRes<PrivateMessageResponse>(msg).data; + let found: PrivateMessageView = this.state.messages.find( + m => + m.private_message.id === data.private_message_view.private_message.id ); if (found) { - found.content = data.message.content; - found.updated = data.message.updated; + found.private_message.content = + data.private_message_view.private_message.content; + found.private_message.updated = + data.private_message_view.private_message.updated; } this.setState(this.state); - } else if (res.op == UserOperation.DeletePrivateMessage) { - let data = res.data as PrivateMessageResponse; - let found: PrivateMessageI = this.state.messages.find( - m => m.id === data.message.id + } else if (op == UserOperation.DeletePrivateMessage) { + let data = wsJsonToRes<PrivateMessageResponse>(msg).data; + let found: PrivateMessageView = this.state.messages.find( + m => + m.private_message.id === data.private_message_view.private_message.id ); if (found) { - found.deleted = data.message.deleted; - found.updated = data.message.updated; + found.private_message.deleted = + data.private_message_view.private_message.deleted; + found.private_message.updated = + data.private_message_view.private_message.updated; } this.setState(this.state); - } else if (res.op == UserOperation.MarkPrivateMessageAsRead) { - let data = res.data as PrivateMessageResponse; - let found: PrivateMessageI = this.state.messages.find( - m => m.id === data.message.id + } else if (op == UserOperation.MarkPrivateMessageAsRead) { + let data = wsJsonToRes<PrivateMessageResponse>(msg).data; + let found: PrivateMessageView = this.state.messages.find( + m => + m.private_message.id === data.private_message_view.private_message.id ); if (found) { - found.updated = data.message.updated; + found.private_message.updated = + data.private_message_view.private_message.updated; // If youre in the unread view, just remove it from the list - if (this.state.unreadOrAll == UnreadOrAll.Unread && data.message.read) { + if ( + this.state.unreadOrAll == UnreadOrAll.Unread && + data.private_message_view.private_message.read + ) { this.state.messages = this.state.messages.filter( - r => r.id !== data.message.id + r => + r.private_message.id !== + data.private_message_view.private_message.id ); } else { - let found = this.state.messages.find(c => c.id == data.message.id); - found.read = data.message.read; + let found = this.state.messages.find( + c => + c.private_message.id == + data.private_message_view.private_message.id + ); + found.private_message.read = + data.private_message_view.private_message.read; } } this.sendUnreadCount(); this.setState(this.state); - } else if (res.op == UserOperation.MarkAllAsRead) { + } else if (op == UserOperation.MarkAllAsRead) { // Moved to be instant } else if ( - res.op == UserOperation.EditComment || - res.op == UserOperation.DeleteComment || - res.op == UserOperation.RemoveComment + op == UserOperation.EditComment || + op == UserOperation.DeleteComment || + op == UserOperation.RemoveComment ) { - let data = res.data as CommentResponse; - editCommentRes(data, this.state.replies); + let data = wsJsonToRes<CommentResponse>(msg).data; + editCommentRes(data.comment_view, this.state.replies); this.setState(this.state); - } else if (res.op == UserOperation.MarkCommentAsRead) { - let data = res.data as CommentResponse; + } else if (op == UserOperation.MarkCommentAsRead) { + let data = wsJsonToRes<CommentResponse>(msg).data; // If youre in the unread view, just remove it from the list - if (this.state.unreadOrAll == UnreadOrAll.Unread && data.comment.read) { + if ( + this.state.unreadOrAll == UnreadOrAll.Unread && + data.comment_view.comment.read + ) { this.state.replies = this.state.replies.filter( - r => r.id !== data.comment.id + r => r.comment.id !== data.comment_view.comment.id ); } else { - let found = this.state.replies.find(c => c.id == data.comment.id); - found.read = data.comment.read; + let found = this.state.replies.find( + c => c.comment.id == data.comment_view.comment.id + ); + found.comment.read = data.comment_view.comment.read; } this.sendUnreadCount(); this.setState(this.state); setupTippy(); - } else if (res.op == UserOperation.MarkUserMentionAsRead) { - let data = res.data as UserMentionResponse; - - let found = this.state.mentions.find(c => c.id == data.mention.id); - found.content = data.mention.content; - found.updated = data.mention.updated; - found.removed = data.mention.removed; - found.deleted = data.mention.deleted; - found.upvotes = data.mention.upvotes; - found.downvotes = data.mention.downvotes; - found.score = data.mention.score; + } else if (op == UserOperation.MarkUserMentionAsRead) { + let data = wsJsonToRes<UserMentionResponse>(msg).data; + + // TODO this might not be correct, it might need to use the comment id + let found = this.state.mentions.find( + c => c.user_mention.id == data.user_mention_view.user_mention.id + ); + found.comment.content = data.user_mention_view.comment.content; + found.comment.updated = data.user_mention_view.comment.updated; + found.comment.removed = data.user_mention_view.comment.removed; + found.comment.deleted = data.user_mention_view.comment.deleted; + found.counts.upvotes = data.user_mention_view.counts.upvotes; + found.counts.downvotes = data.user_mention_view.counts.downvotes; + found.counts.score = data.user_mention_view.counts.score; // If youre in the unread view, just remove it from the list - if (this.state.unreadOrAll == UnreadOrAll.Unread && data.mention.read) { + if ( + this.state.unreadOrAll == UnreadOrAll.Unread && + data.user_mention_view.user_mention.read + ) { this.state.mentions = this.state.mentions.filter( - r => r.id !== data.mention.id + r => r.user_mention.id !== data.user_mention_view.user_mention.id ); } else { - let found = this.state.mentions.find(c => c.id == data.mention.id); - found.read = data.mention.read; + let found = this.state.mentions.find( + c => c.user_mention.id == data.user_mention_view.user_mention.id + ); + // TODO test to make sure these mentions are getting marked as read + found.user_mention.read = data.user_mention_view.user_mention.read; } this.sendUnreadCount(); this.setState(this.state); - } else if (res.op == UserOperation.CreateComment) { - let data = res.data as CommentResponse; + } else if (op == UserOperation.CreateComment) { + let data = wsJsonToRes<CommentResponse>(msg).data; if (data.recipient_ids.includes(UserService.Instance.user.id)) { - this.state.replies.unshift(data.comment); + this.state.replies.unshift(data.comment_view); this.setState(this.state); - } else if (data.comment.creator_id == UserService.Instance.user.id) { + } else if (data.comment_view.creator.id == UserService.Instance.user.id) { + // TODO this seems wrong, you should be using form_id toast(i18n.t('reply_sent')); } - } else if (res.op == UserOperation.CreatePrivateMessage) { - let data = res.data as PrivateMessageResponse; - if (data.message.recipient_id == UserService.Instance.user.id) { - this.state.messages.unshift(data.message); + } else if (op == UserOperation.CreatePrivateMessage) { + let data = wsJsonToRes<PrivateMessageResponse>(msg).data; + if ( + data.private_message_view.recipient.id == UserService.Instance.user.id + ) { + this.state.messages.unshift(data.private_message_view); this.setState(this.state); } - } else if (res.op == UserOperation.SaveComment) { - let data = res.data as CommentResponse; - saveCommentRes(data, this.state.replies); + } else if (op == UserOperation.SaveComment) { + let data = wsJsonToRes<CommentResponse>(msg).data; + saveCommentRes(data.comment_view, this.state.replies); this.setState(this.state); setupTippy(); - } else if (res.op == UserOperation.CreateCommentLike) { - let data = res.data as CommentResponse; - createCommentLikeRes(data, this.state.replies); + } else if (op == UserOperation.CreateCommentLike) { + let data = wsJsonToRes<CommentResponse>(msg).data; + createCommentLikeRes(data.comment_view, this.state.replies); this.setState(this.state); } } @@ -636,13 +725,14 @@ export class Inbox extends Component<any, InboxState> { unreadCount(): number { return ( - this.state.replies.filter(r => !r.read).length + - this.state.mentions.filter(r => !r.read).length + + this.state.replies.filter(r => !r.comment.read).length + + this.state.mentions.filter(r => !r.user_mention.read).length + this.state.messages.filter( r => UserService.Instance.user && - !r.read && - r.creator_id !== UserService.Instance.user.id + !r.private_message.read && + // TODO also seems very strang and wrong + r.creator.id !== UserService.Instance.user.id ).length ); } diff --git a/src/shared/components/instances.tsx b/src/shared/components/instances.tsx index 7be27c7..85a3c45 100644 --- a/src/shared/components/instances.tsx +++ b/src/shared/components/instances.tsx @@ -11,7 +11,7 @@ interface InstancesState { export class Instances extends Component<any, InstancesState> { private isoData = setIsoData(this.context); private emptyState: InstancesState = { - siteRes: this.isoData.site, + siteRes: this.isoData.site_res, }; constructor(props: any, context: any) { @@ -20,7 +20,7 @@ export class Instances extends Component<any, InstancesState> { } get documentTitle(): string { - return `${i18n.t('instances')} - ${this.state.siteRes.site.name}`; + return `${i18n.t('instances')} - ${this.state.siteRes.site_view.site.name}`; } render() { diff --git a/src/shared/components/login.tsx b/src/shared/components/login.tsx index f3b0536..8ca9396 100644 --- a/src/shared/components/login.tsx +++ b/src/shared/components/login.tsx @@ -1,15 +1,14 @@ import { Component, linkEvent } from 'inferno'; import { Subscription } from 'rxjs'; import { - LoginForm, - RegisterForm, + Login as LoginForm, + Register, LoginResponse, UserOperation, - PasswordResetForm, + PasswordReset, GetSiteResponse, GetCaptchaResponse, - WebSocketJsonResponse, - Site, + SiteView, } from 'lemmy-js-client'; import { WebSocketService, UserService } from '../services'; import { @@ -19,18 +18,19 @@ import { wsSubscribe, isBrowser, setIsoData, + wsUserOp, } from '../utils'; import { i18n } from '../i18next'; import { HtmlTags } from './html-tags'; interface State { loginForm: LoginForm; - registerForm: RegisterForm; + registerForm: Register; loginLoading: boolean; registerLoading: boolean; captcha: GetCaptchaResponse; captchaPlaying: boolean; - site: Site; + site_view: SiteView; } export class Login extends Component<any, State> { @@ -55,7 +55,7 @@ export class Login extends Component<any, State> { registerLoading: false, captcha: undefined, captchaPlaying: false, - site: this.isoData.site.site, + site_view: this.isoData.site_res.site_view, }; constructor(props: any, context: any) { @@ -67,7 +67,7 @@ export class Login extends Component<any, State> { this.subscription = wsSubscribe(this.parseMessage); if (isBrowser()) { - WebSocketService.Instance.getCaptcha(); + WebSocketService.Instance.client.getCaptcha(); } } @@ -78,7 +78,7 @@ export class Login extends Component<any, State> { } get documentTitle(): string { - return `${i18n.t('login')} - ${this.state.site.name}`; + return `${i18n.t('login')} - ${this.state.site_view.site.name}`; } render() { @@ -280,7 +280,7 @@ export class Login extends Component<any, State> { </div> </div> )} - {this.state.site.enable_nsfw && ( + {this.state.site_view.site.enable_nsfw && ( <div class="form-group row"> <div class="col-sm-10"> <div class="form-check"> @@ -349,7 +349,7 @@ export class Login extends Component<any, State> { event.preventDefault(); i.state.loginLoading = true; i.setState(i.state); - WebSocketService.Instance.login(i.state.loginForm); + WebSocketService.Instance.client.login(i.state.loginForm); } handleLoginUsernameChange(i: Login, event: any) { @@ -366,7 +366,7 @@ export class Login extends Component<any, State> { event.preventDefault(); i.state.registerLoading = true; i.setState(i.state); - WebSocketService.Instance.register(i.state.registerForm); + WebSocketService.Instance.client.register(i.state.registerForm); } handleRegisterUsernameChange(i: Login, event: any) { @@ -404,15 +404,15 @@ export class Login extends Component<any, State> { handleRegenCaptcha(_i: Login, event: any) { event.preventDefault(); - WebSocketService.Instance.getCaptcha(); + WebSocketService.Instance.client.getCaptcha(); } handlePasswordReset(i: Login, event: any) { event.preventDefault(); - let resetForm: PasswordResetForm = { + let resetForm: PasswordReset = { email: i.state.loginForm.username_or_email, }; - WebSocketService.Instance.passwordReset(resetForm); + WebSocketService.Instance.client.passwordReset(resetForm); } handleCaptchaPlay(i: Login, event: any) { @@ -432,44 +432,48 @@ export class Login extends Component<any, State> { return `data:image/png;base64,${this.state.captcha.ok.png}`; } - parseMessage(msg: WebSocketJsonResponse) { - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); this.state = this.emptyState; this.state.registerForm.captcha_answer = undefined; // Refetch another captcha - WebSocketService.Instance.getCaptcha(); + WebSocketService.Instance.client.getCaptcha(); this.setState(this.state); return; } else { - if (res.op == UserOperation.Login) { - let data = res.data as LoginResponse; + if (op == UserOperation.Login) { + let data = wsJsonToRes<LoginResponse>(msg).data; this.state = this.emptyState; this.setState(this.state); UserService.Instance.login(data); - WebSocketService.Instance.userJoin(); + WebSocketService.Instance.client.userJoin({ + auth: UserService.Instance.authField(), + }); toast(i18n.t('logged_in')); this.props.history.push('/'); - } else if (res.op == UserOperation.Register) { - let data = res.data as LoginResponse; + } else if (op == UserOperation.Register) { + let data = wsJsonToRes<LoginResponse>(msg).data; this.state = this.emptyState; this.setState(this.state); UserService.Instance.login(data); - WebSocketService.Instance.userJoin(); + WebSocketService.Instance.client.userJoin({ + auth: UserService.Instance.authField(), + }); this.props.history.push('/communities'); - } else if (res.op == UserOperation.GetCaptcha) { - let data = res.data as GetCaptchaResponse; + } else if (op == UserOperation.GetCaptcha) { + let data = wsJsonToRes<GetCaptchaResponse>(msg).data; if (data.ok) { this.state.captcha = data; this.state.registerForm.captcha_uuid = data.ok.uuid; this.setState(this.state); } - } else if (res.op == UserOperation.PasswordReset) { + } else if (op == UserOperation.PasswordReset) { toast(i18n.t('reset_password_mail_sent')); - } else if (res.op == UserOperation.GetSite) { - let data = res.data as GetSiteResponse; - this.state.site = data.site; + } else if (op == UserOperation.GetSite) { + let data = wsJsonToRes<GetSiteResponse>(msg).data; + this.state.site_view = data.site_view; this.setState(this.state); } } diff --git a/src/shared/components/main.tsx b/src/shared/components/main.tsx index 0814bdd..0bfe08d 100644 --- a/src/shared/components/main.tsx +++ b/src/shared/components/main.tsx @@ -3,26 +3,25 @@ import { Link } from 'inferno-router'; import { Subscription } from 'rxjs'; import { UserOperation, - CommunityUser, + CommunityFollowerView, GetFollowedCommunitiesResponse, - ListCommunitiesForm, + ListCommunities, ListCommunitiesResponse, - Community, + CommunityView, SortType, GetSiteResponse, ListingType, SiteResponse, GetPostsResponse, PostResponse, - Post, - GetPostsForm, - Comment, - GetCommentsForm, + PostView, + GetPosts, + CommentView, + GetComments, GetCommentsResponse, CommentResponse, AddAdminResponse, BanUserResponse, - WebSocketJsonResponse, } from 'lemmy-js-client'; import { DataType, InitialFetchRequest } from '../interfaces'; import { WebSocketService, UserService } from '../services'; @@ -55,20 +54,20 @@ import { setIsoData, wsSubscribe, isBrowser, - setAuth, + wsUserOp, } from '../utils'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; import { HtmlTags } from './html-tags'; interface MainState { - subscribedCommunities: CommunityUser[]; - trendingCommunities: Community[]; + subscribedCommunities: CommunityFollowerView[]; + trendingCommunities: CommunityView[]; siteRes: GetSiteResponse; showEditSite: boolean; loading: boolean; - posts: Post[]; - comments: Comment[]; + posts: PostView[]; + comments: CommentView[]; listingType: ListingType; dataType: DataType; sort: SortType; @@ -95,7 +94,7 @@ export class Main extends Component<any, MainState> { private emptyState: MainState = { subscribedCommunities: [], trendingCommunities: [], - siteRes: this.isoData.site, + siteRes: this.isoData.site_res, showEditSite: false, loading: true, posts: [], @@ -134,7 +133,9 @@ export class Main extends Component<any, MainState> { this.fetchTrendingCommunities(); this.fetchData(); if (UserService.Instance.user) { - WebSocketService.Instance.getFollowedCommunities(); + WebSocketService.Instance.client.getFollowedCommunities({ + auth: UserService.Instance.authField(), + }); } } @@ -142,20 +143,21 @@ export class Main extends Component<any, MainState> { } fetchTrendingCommunities() { - let listCommunitiesForm: ListCommunitiesForm = { + let listCommunitiesForm: ListCommunities = { sort: SortType.Hot, limit: 6, + auth: UserService.Instance.authField(false), }; - WebSocketService.Instance.listCommunities(listCommunitiesForm); + WebSocketService.Instance.client.listCommunities(listCommunitiesForm); } componentDidMount() { // This means it hasn't been set up yet - if (!this.state.siteRes.site) { + if (!this.state.siteRes.site_view) { this.context.router.history.push('/setup'); } - WebSocketService.Instance.communityJoin({ community_id: 0 }); + WebSocketService.Instance.client.communityJoin({ community_id: 0 }); } componentWillUnmount() { @@ -199,26 +201,26 @@ export class Main extends Component<any, MainState> { let promises: Promise<any>[] = []; if (dataType == DataType.Post) { - let getPostsForm: GetPostsForm = { + let getPostsForm: GetPosts = { page, limit: fetchLimit, sort, type_, + auth: req.auth, }; - setAuth(getPostsForm, req.auth); promises.push(req.client.getPosts(getPostsForm)); } else { - let getCommentsForm: GetCommentsForm = { + let getCommentsForm: GetComments = { page, limit: fetchLimit, sort, type_, + auth: req.auth, }; - setAuth(getCommentsForm, req.auth); promises.push(req.client.getComments(getCommentsForm)); } - let trendingCommunitiesForm: ListCommunitiesForm = { + let trendingCommunitiesForm: ListCommunities = { sort: SortType.Hot, limit: 6, }; @@ -245,7 +247,9 @@ export class Main extends Component<any, MainState> { get documentTitle(): string { return `${ - this.state.siteRes.site ? this.state.siteRes.site.name : 'Lemmy' + this.state.siteRes.site_view + ? this.state.siteRes.site_view.site.name + : 'Lemmy' }`; } @@ -256,7 +260,7 @@ export class Main extends Component<any, MainState> { title={this.documentTitle} path={this.context.router.route.match.url} /> - {this.state.siteRes.site && ( + {this.state.siteRes.site_view.site && ( <div class="row"> <main role="main" class="col-12 col-md-8"> {this.posts()} @@ -316,9 +320,9 @@ export class Main extends Component<any, MainState> { </T> </h5> <ul class="list-inline"> - {this.state.trendingCommunities.map(community => ( + {this.state.trendingCommunities.map(cv => ( <li class="list-inline-item d-inline"> - <CommunityLink community={community} /> + <CommunityLink community={cv.community} /> </li> ))} </ul> @@ -338,17 +342,9 @@ export class Main extends Component<any, MainState> { </T> </h5> <ul class="list-inline mb-0"> - {this.state.subscribedCommunities.map(community => ( + {this.state.subscribedCommunities.map(cfv => ( <li class="list-inline-item d-inline"> - <CommunityLink - community={{ - name: community.community_name, - id: community.community_id, - local: community.community_local, - actor_id: community.community_actor_id, - icon: community.community_icon, - }} - /> + <CommunityLink community={cfv.community} /> </li> ))} </ul> @@ -357,6 +353,7 @@ export class Main extends Component<any, MainState> { } sidebar() { + let site = this.state.siteRes.site_view.site; return ( <div> {!this.state.showEditSite ? ( @@ -365,14 +362,11 @@ export class Main extends Component<any, MainState> { {this.siteName()} {this.adminButtons()} </div> - <BannerIconHeader banner={this.state.siteRes.site.banner} /> + <BannerIconHeader banner={site.banner} /> {this.siteInfo()} </div> ) : ( - <SiteForm - site={this.state.siteRes.site} - onCancel={this.handleEditCancel} - /> + <SiteForm site={site} onCancel={this.handleEditCancel} /> )} </div> ); @@ -391,7 +385,8 @@ export class Main extends Component<any, MainState> { siteInfo() { return ( <div> - {this.state.siteRes.site.description && this.siteDescription()} + {this.state.siteRes.site_view.site.description && + this.siteDescription()} {this.badges()} {this.admins()} </div> @@ -406,18 +401,9 @@ export class Main extends Component<any, MainState> { return ( <ul class="mt-1 list-inline small mb-0"> <li class="list-inline-item">{i18n.t('admins')}:</li> - {this.state.siteRes.admins.map(admin => ( + {this.state.siteRes.admins.map(av => ( <li class="list-inline-item"> - <UserListing - user={{ - name: admin.name, - preferred_username: admin.preferred_username, - avatar: admin.avatar, - local: admin.local, - actor_id: admin.actor_id, - id: admin.id, - }} - /> + <UserListing user={av.user} /> </li> ))} </ul> @@ -425,6 +411,7 @@ export class Main extends Component<any, MainState> { } badges() { + let site_view = this.state.siteRes.site_view; return ( <ul class="my-2 list-inline"> <li className="list-inline-item badge badge-secondary"> @@ -432,22 +419,22 @@ export class Main extends Component<any, MainState> { </li> <li className="list-inline-item badge badge-secondary"> {i18n.t('number_of_users', { - count: this.state.siteRes.site.number_of_users, + count: site_view.counts.users, })} </li> <li className="list-inline-item badge badge-secondary"> {i18n.t('number_of_communities', { - count: this.state.siteRes.site.number_of_communities, + count: site_view.counts.communities, })} </li> <li className="list-inline-item badge badge-secondary"> {i18n.t('number_of_posts', { - count: this.state.siteRes.site.number_of_posts, + count: site_view.counts.posts, })} </li> <li className="list-inline-item badge badge-secondary"> {i18n.t('number_of_comments', { - count: this.state.siteRes.site.number_of_comments, + count: site_view.counts.comments, })} </li> <li className="list-inline-item"> @@ -483,7 +470,9 @@ export class Main extends Component<any, MainState> { return ( <div className="md-div" - dangerouslySetInnerHTML={mdToHtml(this.state.siteRes.site.description)} + dangerouslySetInnerHTML={mdToHtml( + this.state.siteRes.site_view.site.description + )} /> ); } @@ -509,14 +498,15 @@ export class Main extends Component<any, MainState> { } listings() { + let site = this.state.siteRes.site_view.site; return this.state.dataType == DataType.Post ? ( <PostListings posts={this.state.posts} showCommunity removeDuplicates sort={this.state.sort} - enableDownvotes={this.state.siteRes.site.enable_downvotes} - enableNsfw={this.state.siteRes.site.enable_nsfw} + enableDownvotes={site.enable_downvotes} + enableNsfw={site.enable_nsfw} /> ) : ( <CommentNodes @@ -525,7 +515,7 @@ export class Main extends Component<any, MainState> { showCommunity sortType={this.state.sort} showContext - enableDownvotes={this.state.siteRes.site.enable_downvotes} + enableDownvotes={site.enable_downvotes} /> ); } @@ -615,8 +605,8 @@ export class Main extends Component<any, MainState> { get showLocal(): boolean { return ( - this.isoData.site.federated_instances !== null && - this.isoData.site.federated_instances.length > 0 + this.isoData.site_res.federated_instances !== null && + this.isoData.site_res.federated_instances.length > 0 ); } @@ -624,7 +614,7 @@ export class Main extends Component<any, MainState> { return ( UserService.Instance.user && this.state.siteRes.admins - .map(a => a.id) + .map(a => a.user.id) .includes(UserService.Instance.user.id) ); } @@ -666,69 +656,70 @@ export class Main extends Component<any, MainState> { fetchData() { if (this.state.dataType == DataType.Post) { - let getPostsForm: GetPostsForm = { + let getPostsForm: GetPosts = { page: this.state.page, limit: fetchLimit, sort: this.state.sort, type_: this.state.listingType, + auth: UserService.Instance.authField(false), }; - WebSocketService.Instance.getPosts(getPostsForm); + WebSocketService.Instance.client.getPosts(getPostsForm); } else { - let getCommentsForm: GetCommentsForm = { + let getCommentsForm: GetComments = { page: this.state.page, limit: fetchLimit, sort: this.state.sort, type_: this.state.listingType, + auth: UserService.Instance.authField(false), }; - WebSocketService.Instance.getComments(getCommentsForm); + WebSocketService.Instance.client.getComments(getCommentsForm); } } - parseMessage(msg: WebSocketJsonResponse) { - console.log(msg); - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); return; } else if (msg.reconnect) { - WebSocketService.Instance.communityJoin({ community_id: 0 }); + WebSocketService.Instance.client.communityJoin({ community_id: 0 }); this.fetchData(); - } else if (res.op == UserOperation.GetFollowedCommunities) { - let data = res.data as GetFollowedCommunitiesResponse; + } else if (op == UserOperation.GetFollowedCommunities) { + let data = wsJsonToRes<GetFollowedCommunitiesResponse>(msg).data; this.state.subscribedCommunities = data.communities; this.setState(this.state); - } else if (res.op == UserOperation.ListCommunities) { - let data = res.data as ListCommunitiesResponse; + } else if (op == UserOperation.ListCommunities) { + let data = wsJsonToRes<ListCommunitiesResponse>(msg).data; this.state.trendingCommunities = data.communities; this.setState(this.state); - } else if (res.op == UserOperation.EditSite) { - let data = res.data as SiteResponse; - this.state.siteRes.site = data.site; + } else if (op == UserOperation.EditSite) { + let data = wsJsonToRes<SiteResponse>(msg).data; + this.state.siteRes.site_view = data.site_view; this.state.showEditSite = false; this.setState(this.state); toast(i18n.t('site_saved')); - } else if (res.op == UserOperation.GetPosts) { - let data = res.data as GetPostsResponse; + } else if (op == UserOperation.GetPosts) { + let data = wsJsonToRes<GetPostsResponse>(msg).data; this.state.posts = data.posts; this.state.loading = false; this.setState(this.state); setupTippy(); - } else if (res.op == UserOperation.CreatePost) { - let data = res.data as PostResponse; + } else if (op == UserOperation.CreatePost) { + let data = wsJsonToRes<PostResponse>(msg).data; // If you're on subscribed, only push it if you're subscribed. if (this.state.listingType == ListingType.Subscribed) { if ( this.state.subscribedCommunities - .map(c => c.community_id) - .includes(data.post.community_id) + .map(c => c.community.id) + .includes(data.post_view.community.id) ) { - this.state.posts.unshift(data.post); - notifyPost(data.post, this.context.router); + this.state.posts.unshift(data.post_view); + notifyPost(data.post_view, this.context.router); } } else { // NSFW posts - let nsfw = data.post.nsfw || data.post.community_nsfw; + let nsfw = data.post_view.post.nsfw || data.post_view.community.nsfw; // Don't push the post if its nsfw, and don't have that setting on if ( @@ -737,63 +728,65 @@ export class Main extends Component<any, MainState> { UserService.Instance.user && UserService.Instance.user.show_nsfw) ) { - this.state.posts.unshift(data.post); - notifyPost(data.post, this.context.router); + this.state.posts.unshift(data.post_view); + notifyPost(data.post_view, this.context.router); } } this.setState(this.state); } else if ( - res.op == UserOperation.EditPost || - res.op == UserOperation.DeletePost || - res.op == UserOperation.RemovePost || - res.op == UserOperation.LockPost || - res.op == UserOperation.StickyPost || - res.op == UserOperation.SavePost + op == UserOperation.EditPost || + op == UserOperation.DeletePost || + op == UserOperation.RemovePost || + op == UserOperation.LockPost || + op == UserOperation.StickyPost || + op == UserOperation.SavePost ) { - let data = res.data as PostResponse; - editPostFindRes(data, this.state.posts); + let data = wsJsonToRes<PostResponse>(msg).data; + editPostFindRes(data.post_view, this.state.posts); this.setState(this.state); - } else if (res.op == UserOperation.CreatePostLike) { - let data = res.data as PostResponse; - createPostLikeFindRes(data, this.state.posts); + } else if (op == UserOperation.CreatePostLike) { + let data = wsJsonToRes<PostResponse>(msg).data; + createPostLikeFindRes(data.post_view, this.state.posts); this.setState(this.state); - } else if (res.op == UserOperation.AddAdmin) { - let data = res.data as AddAdminResponse; + } else if (op == UserOperation.AddAdmin) { + let data = wsJsonToRes<AddAdminResponse>(msg).data; this.state.siteRes.admins = data.admins; this.setState(this.state); - } else if (res.op == UserOperation.BanUser) { - let data = res.data as BanUserResponse; - let found = this.state.siteRes.banned.find(u => (u.id = data.user.id)); + } else if (op == UserOperation.BanUser) { + let data = wsJsonToRes<BanUserResponse>(msg).data; + let found = this.state.siteRes.banned.find( + u => (u.user.id = data.user_view.user.id) + ); // Remove the banned if its found in the list, and the action is an unban if (found && !data.banned) { this.state.siteRes.banned = this.state.siteRes.banned.filter( - i => i.id !== data.user.id + i => i.user.id !== data.user_view.user.id ); } else { - this.state.siteRes.banned.push(data.user); + this.state.siteRes.banned.push(data.user_view); } this.state.posts - .filter(p => p.creator_id == data.user.id) - .forEach(p => (p.banned = data.banned)); + .filter(p => p.creator.id == data.user_view.user.id) + .forEach(p => (p.creator.banned = data.banned)); this.setState(this.state); - } else if (res.op == UserOperation.GetComments) { - let data = res.data as GetCommentsResponse; + } else if (op == UserOperation.GetComments) { + let data = wsJsonToRes<GetCommentsResponse>(msg).data; this.state.comments = data.comments; this.state.loading = false; this.setState(this.state); } else if ( - res.op == UserOperation.EditComment || - res.op == UserOperation.DeleteComment || - res.op == UserOperation.RemoveComment + op == UserOperation.EditComment || + op == UserOperation.DeleteComment || + op == UserOperation.RemoveComment ) { - let data = res.data as CommentResponse; - editCommentRes(data, this.state.comments); + let data = wsJsonToRes<CommentResponse>(msg).data; + editCommentRes(data.comment_view, this.state.comments); this.setState(this.state); - } else if (res.op == UserOperation.CreateComment) { - let data = res.data as CommentResponse; + } else if (op == UserOperation.CreateComment) { + let data = wsJsonToRes<CommentResponse>(msg).data; // Necessary since it might be a user reply if (data.recipient_ids.length == 0) { @@ -801,23 +794,23 @@ export class Main extends Component<any, MainState> { if (this.state.listingType == ListingType.Subscribed) { if ( this.state.subscribedCommunities - .map(c => c.community_id) - .includes(data.comment.community_id) + .map(c => c.community.id) + .includes(data.comment_view.community.id) ) { - this.state.comments.unshift(data.comment); + this.state.comments.unshift(data.comment_view); } } else { - this.state.comments.unshift(data.comment); + this.state.comments.unshift(data.comment_view); } this.setState(this.state); } - } else if (res.op == UserOperation.SaveComment) { - let data = res.data as CommentResponse; - saveCommentRes(data, this.state.comments); + } else if (op == UserOperation.SaveComment) { + let data = wsJsonToRes<CommentResponse>(msg).data; + saveCommentRes(data.comment_view, this.state.comments); this.setState(this.state); - } else if (res.op == UserOperation.CreateCommentLike) { - let data = res.data as CommentResponse; - createCommentLikeRes(data, this.state.comments); + } else if (op == UserOperation.CreateCommentLike) { + let data = wsJsonToRes<CommentResponse>(msg).data; + createCommentLikeRes(data.comment_view, this.state.comments); this.setState(this.state); } } diff --git a/src/shared/components/modlog.tsx b/src/shared/components/modlog.tsx index ded9a30..fcd18e5 100644 --- a/src/shared/components/modlog.tsx +++ b/src/shared/components/modlog.tsx @@ -3,51 +3,71 @@ import { Link } from 'inferno-router'; import { Subscription } from 'rxjs'; import { UserOperation, - GetModlogForm, + GetModlog, GetModlogResponse, - ModRemovePost, - ModLockPost, - ModStickyPost, - ModRemoveComment, - ModRemoveCommunity, - ModBanFromCommunity, - ModBan, - ModAddCommunity, - ModAdd, - WebSocketJsonResponse, - Site, + SiteView, + ModRemovePostView, + ModLockPostView, + ModStickyPostView, + ModRemoveCommentView, + ModRemoveCommunityView, + ModBanFromCommunityView, + ModBanView, + ModAddCommunityView, + ModAddView, } from 'lemmy-js-client'; import { WebSocketService } from '../services'; import { wsJsonToRes, - addTypeInfo, fetchLimit, toast, setIsoData, wsSubscribe, isBrowser, + wsUserOp, } from '../utils'; import { MomentTime } from './moment-time'; import { HtmlTags } from './html-tags'; import moment from 'moment'; import { i18n } from '../i18next'; import { InitialFetchRequest } from 'shared/interfaces'; +import { UserListing } from './user-listing'; +import { CommunityLink } from './community-link'; + +enum ModlogEnum { + ModRemovePost, + ModLockPost, + ModStickyPost, + ModRemoveComment, + ModRemoveCommunity, + ModBanFromCommunity, + ModAddCommunity, + ModAdd, + ModBan, +} + +type ModlogType = { + id: number; + type_: ModlogEnum; + view: + | ModRemovePostView + | ModLockPostView + | ModStickyPostView + | ModRemoveCommentView + | ModRemoveCommunityView + | ModBanFromCommunityView + | ModBanView + | ModAddCommunityView + | ModAddView; + when_: string; +}; interface ModlogState { - combined: { - type_: string; - data: - | ModRemovePost - | ModLockPost - | ModStickyPost - | ModRemoveCommunity - | ModAdd - | ModBan; - }[]; + res: GetModlogResponse; communityId?: number; communityName?: string; page: number; - site: Site; + site_view: SiteView; loading: boolean; } @@ -55,10 +75,20 @@ export class Modlog extends Component<any, ModlogState> { private isoData = setIsoData(this.context); private subscription: Subscription; private emptyState: ModlogState = { - combined: [], + res: { + removed_posts: [], + locked_posts: [], + stickied_posts: [], + removed_comments: [], + removed_communities: [], + banned_from_community: [], + banned: [], + added_to_community: [], + added: [], + }, page: 1, loading: true, - site: this.isoData.site.site, + site_view: this.isoData.site_res.site_view, }; constructor(props: any, context: any) { @@ -75,7 +105,7 @@ export class Modlog extends Component<any, ModlogState> { // Only fetch the data if coming from another route if (this.isoData.path == this.context.router.route.match.url) { let data = this.isoData.routeData[0]; - this.setCombined(data); + this.state.res = data; this.state.loading = false; } else { this.refetch(); @@ -88,264 +118,233 @@ export class Modlog extends Component<any, ModlogState> { } } - setCombined(res: GetModlogResponse) { - let removed_posts = addTypeInfo(res.removed_posts, 'removed_posts'); - let locked_posts = addTypeInfo(res.locked_posts, 'locked_posts'); - let stickied_posts = addTypeInfo(res.stickied_posts, 'stickied_posts'); - let removed_comments = addTypeInfo( - res.removed_comments, - 'removed_comments' - ); - let removed_communities = addTypeInfo( - res.removed_communities, - 'removed_communities' - ); - let banned_from_community = addTypeInfo( - res.banned_from_community, - 'banned_from_community' - ); - let added_to_community = addTypeInfo( - res.added_to_community, - 'added_to_community' + buildCombined(res: GetModlogResponse): ModlogType[] { + let removed_posts: ModlogType[] = res.removed_posts.map(r => ({ + id: r.mod_remove_post.id, + type_: ModlogEnum.ModRemovePost, + view: r, + when_: r.mod_remove_post.when_, + })); + + let locked_posts: ModlogType[] = res.locked_posts.map(r => ({ + id: r.mod_lock_post.id, + type_: ModlogEnum.ModLockPost, + view: r, + when_: r.mod_lock_post.when_, + })); + + let stickied_posts: ModlogType[] = res.stickied_posts.map(r => ({ + id: r.mod_sticky_post.id, + type_: ModlogEnum.ModStickyPost, + view: r, + when_: r.mod_sticky_post.when_, + })); + + let removed_comments: ModlogType[] = res.removed_comments.map(r => ({ + id: r.mod_remove_comment.id, + type_: ModlogEnum.ModRemoveComment, + view: r, + when_: r.mod_remove_comment.when_, + })); + + let removed_communities: ModlogType[] = res.removed_communities.map(r => ({ + id: r.mod_remove_community.id, + type_: ModlogEnum.ModRemoveCommunity, + view: r, + when_: r.mod_remove_community.when_, + })); + + let banned_from_community: ModlogType[] = res.banned_from_community.map( + r => ({ + id: r.mod_ban_from_community.id, + type_: ModlogEnum.ModBanFromCommunity, + view: r, + when_: r.mod_ban_from_community.when_, + }) ); - let added = addTypeInfo(res.added, 'added'); - let banned = addTypeInfo(res.banned, 'banned'); - this.state.combined = []; - - this.state.combined.push(...removed_posts); - this.state.combined.push(...locked_posts); - this.state.combined.push(...stickied_posts); - this.state.combined.push(...removed_comments); - this.state.combined.push(...removed_communities); - this.state.combined.push(...banned_from_community); - this.state.combined.push(...added_to_community); - this.state.combined.push(...added); - this.state.combined.push(...banned); - - if (this.state.communityId && this.state.combined.length > 0) { - this.state.communityName = (this.state.combined[0] - .data as ModRemovePost).community_name; + + let added_to_community: ModlogType[] = res.added_to_community.map(r => ({ + id: r.mod_add_community.id, + type_: ModlogEnum.ModAddCommunity, + view: r, + when_: r.mod_add_community.when_, + })); + + let added: ModlogType[] = res.added.map(r => ({ + id: r.mod_add.id, + type_: ModlogEnum.ModAdd, + view: r, + when_: r.mod_add.when_, + })); + + let banned: ModlogType[] = res.banned.map(r => ({ + id: r.mod_ban.id, + type_: ModlogEnum.ModBan, + view: r, + when_: r.mod_ban.when_, + })); + + let combined: ModlogType[] = []; + + combined.push(...removed_posts); + combined.push(...locked_posts); + combined.push(...stickied_posts); + combined.push(...removed_comments); + combined.push(...removed_communities); + combined.push(...banned_from_community); + combined.push(...added_to_community); + combined.push(...added); + combined.push(...banned); + + if (this.state.communityId && combined.length > 0) { + this.state.communityName = (combined[0] + .view as ModRemovePostView).community.name; } // Sort them by time - this.state.combined.sort((a, b) => - b.data.when_.localeCompare(a.data.when_) - ); + combined.sort((a, b) => b.when_.localeCompare(a.when_)); + + return combined; + } + + renderModlogType(i: ModlogType) { + switch (i.type_) { + case ModlogEnum.ModRemovePost: + let mrpv = i.view as ModRemovePostView; + return [ + mrpv.mod_remove_post.removed ? 'Removed ' : 'Restored ', + <span> + Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link> + </span>, + mrpv.mod_remove_post.reason && + ` reason: ${mrpv.mod_remove_post.reason}`, + ]; + case ModlogEnum.ModLockPost: + let mlpv = i.view as ModLockPostView; + return [ + mlpv.mod_lock_post.locked ? 'Locked ' : 'Unlocked ', + <span> + Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link> + </span>, + ]; + case ModlogEnum.ModStickyPost: + let mspv = i.view as ModStickyPostView; + return [ + mspv.mod_sticky_post.stickied ? 'Stickied ' : 'Unstickied ', + <span> + Post <Link to={`/post/${mspv.post.id}`}>{mspv.post.name}</Link> + </span>, + ]; + case ModlogEnum.ModRemoveComment: + let mrc = i.view as ModRemoveCommentView; + return [ + mrc.mod_remove_comment.removed ? 'Removed ' : 'Restored ', + <span> + Comment{' '} + <Link to={`/post/${mrc.post.id}/comment/${mrc.comment.id}`}> + {mrc.comment.content} + </Link> + </span>, + <span> + {' '} + by <UserListing user={mrc.commenter} /> + </span>, + mrc.mod_remove_comment.reason && + ` reason: ${mrc.mod_remove_comment.reason}`, + ]; + case ModlogEnum.ModRemoveCommunity: + let mrco = i.view as ModRemoveCommunityView; + return [ + mrco.mod_remove_community.removed ? 'Removed ' : 'Restored ', + <span> + Community <CommunityLink community={mrco.community} /> + </span>, + mrco.mod_remove_community.reason && + ` reason: ${mrco.mod_remove_community.reason}`, + mrco.mod_remove_community.expires && + ` expires: ${moment + .utc(mrco.mod_remove_community.expires) + .fromNow()}`, + ]; + case ModlogEnum.ModBanFromCommunity: + let mbfc = i.view as ModBanFromCommunityView; + return [ + <span> + {mbfc.mod_ban_from_community.banned ? 'Banned ' : 'Unbanned '}{' '} + </span>, + <span> + <UserListing user={mbfc.banned_user} /> + </span>, + <span> from the community </span>, + <span> + <CommunityLink community={mbfc.community} /> + </span>, + <div> + {mbfc.mod_ban_from_community.reason && + ` reason: ${mbfc.mod_ban_from_community.reason}`} + </div>, + <div> + {mbfc.mod_ban_from_community.expires && + ` expires: ${moment + .utc(mbfc.mod_ban_from_community.expires) + .fromNow()}`} + </div>, + ]; + case ModlogEnum.ModAddCommunity: + let mac = i.view as ModAddCommunityView; + return [ + <span> + {mac.mod_add_community.removed ? 'Removed ' : 'Appointed '}{' '} + </span>, + <span> + <UserListing user={mac.modded_user} /> + </span>, + <span> as a mod to the community </span>, + <span> + <CommunityLink community={mac.community} /> + </span>, + ]; + case ModlogEnum.ModBan: + let mb = i.view as ModBanView; + return [ + <span>{mb.mod_ban.banned ? 'Banned ' : 'Unbanned '} </span>, + <span> + <UserListing user={mb.banned_user} /> + </span>, + <div>{mb.mod_ban.reason && ` reason: ${mb.mod_ban.reason}`}</div>, + <div> + {mb.mod_ban.expires && + ` expires: ${moment.utc(mb.mod_ban.expires).fromNow()}`} + </div>, + ]; + case ModlogEnum.ModAdd: + let ma = i.view as ModAddView; + return [ + <span>{ma.mod_add.removed ? 'Removed ' : 'Appointed '} </span>, + <span> + <UserListing user={ma.modded_user} /> + </span>, + <span> as an admin </span>, + ]; + default: + return <div />; + } } combined() { + let combined = this.buildCombined(this.state.res); + return ( <tbody> - {this.state.combined.map(i => ( + {combined.map(i => ( <tr> <td> - <MomentTime data={i.data} /> - </td> - <td> - <Link to={`/u/${i.data.mod_user_name}`}> - {i.data.mod_user_name} - </Link> + <MomentTime data={i} /> </td> <td> - {i.type_ == 'removed_posts' && ( - <> - {(i.data as ModRemovePost).removed ? 'Removed' : 'Restored'} - <span> - {' '} - Post{' '} - <Link to={`/post/${(i.data as ModRemovePost).post_id}`}> - {(i.data as ModRemovePost).post_name} - </Link> - </span> - <div> - {(i.data as ModRemovePost).reason && - ` reason: ${(i.data as ModRemovePost).reason}`} - </div> - </> - )} - {i.type_ == 'locked_posts' && ( - <> - {(i.data as ModLockPost).locked ? 'Locked' : 'Unlocked'} - <span> - {' '} - Post{' '} - <Link to={`/post/${(i.data as ModLockPost).post_id}`}> - {(i.data as ModLockPost).post_name} - </Link> - </span> - </> - )} - {i.type_ == 'stickied_posts' && ( - <> - {(i.data as ModStickyPost).stickied - ? 'Stickied' - : 'Unstickied'} - <span> - {' '} - Post{' '} - <Link to={`/post/${(i.data as ModStickyPost).post_id}`}> - {(i.data as ModStickyPost).post_name} - </Link> - </span> - </> - )} - {i.type_ == 'removed_comments' && ( - <> - {(i.data as ModRemoveComment).removed - ? 'Removed' - : 'Restored'} - <span> - {' '} - Comment{' '} - <Link - to={`/post/${ - (i.data as ModRemoveComment).post_id - }/comment/${(i.data as ModRemoveComment).comment_id}`} - > - {(i.data as ModRemoveComment).comment_content} - </Link> - </span> - <span> - {' '} - by{' '} - <Link - to={`/u/${ - (i.data as ModRemoveComment).comment_user_name - }`} - > - {(i.data as ModRemoveComment).comment_user_name} - </Link> - </span> - <div> - {(i.data as ModRemoveComment).reason && - ` reason: ${(i.data as ModRemoveComment).reason}`} - </div> - </> - )} - {i.type_ == 'removed_communities' && ( - <> - {(i.data as ModRemoveCommunity).removed - ? 'Removed' - : 'Restored'} - <span> - {' '} - Community{' '} - <Link - to={`/c/${(i.data as ModRemoveCommunity).community_name}`} - > - {(i.data as ModRemoveCommunity).community_name} - </Link> - </span> - <div> - {(i.data as ModRemoveCommunity).reason && - ` reason: ${(i.data as ModRemoveCommunity).reason}`} - </div> - <div> - {(i.data as ModRemoveCommunity).expires && - ` expires: ${moment - .utc((i.data as ModRemoveCommunity).expires) - .fromNow()}`} - </div> - </> - )} - {i.type_ == 'banned_from_community' && ( - <> - <span> - {(i.data as ModBanFromCommunity).banned - ? 'Banned ' - : 'Unbanned '}{' '} - </span> - <span> - <Link - to={`/u/${ - (i.data as ModBanFromCommunity).other_user_name - }`} - > - {(i.data as ModBanFromCommunity).other_user_name} - </Link> - </span> - <span> from the community </span> - <span> - <Link - to={`/c/${ - (i.data as ModBanFromCommunity).community_name - }`} - > - {(i.data as ModBanFromCommunity).community_name} - </Link> - </span> - <div> - {(i.data as ModBanFromCommunity).reason && - ` reason: ${(i.data as ModBanFromCommunity).reason}`} - </div> - <div> - {(i.data as ModBanFromCommunity).expires && - ` expires: ${moment - .utc((i.data as ModBanFromCommunity).expires) - .fromNow()}`} - </div> - </> - )} - {i.type_ == 'added_to_community' && ( - <> - <span> - {(i.data as ModAddCommunity).removed - ? 'Removed ' - : 'Appointed '}{' '} - </span> - <span> - <Link - to={`/u/${(i.data as ModAddCommunity).other_user_name}`} - > - {(i.data as ModAddCommunity).other_user_name} - </Link> - </span> - <span> as a mod to the community </span> - <span> - <Link - to={`/c/${(i.data as ModAddCommunity).community_name}`} - > - {(i.data as ModAddCommunity).community_name} - </Link> - </span> - </> - )} - {i.type_ == 'banned' && ( - <> - <span> - {(i.data as ModBan).banned ? 'Banned ' : 'Unbanned '}{' '} - </span> - <span> - <Link to={`/u/${(i.data as ModBan).other_user_name}`}> - {(i.data as ModBan).other_user_name} - </Link> - </span> - <div> - {(i.data as ModBan).reason && - ` reason: ${(i.data as ModBan).reason}`} - </div> - <div> - {(i.data as ModBan).expires && - ` expires: ${moment - .utc((i.data as ModBan).expires) - .fromNow()}`} - </div> - </> - )} - {i.type_ == 'added' && ( - <> - <span> - {(i.data as ModAdd).removed ? 'Removed ' : 'Appointed '}{' '} - </span> - <span> - <Link to={`/u/${(i.data as ModAdd).other_user_name}`}> - {(i.data as ModAdd).other_user_name} - </Link> - </span> - <span> as an admin </span> - </> - )} + <UserListing user={i.view.moderator} /> </td> + <td>{this.renderModlogType(i)}</td> </tr> ))} </tbody> @@ -353,7 +352,7 @@ export class Modlog extends Component<any, ModlogState> { } get documentTitle(): string { - return `Modlog - ${this.state.site.name}`; + return `Modlog - ${this.state.site_view.site.name}`; } render() { @@ -435,12 +434,12 @@ export class Modlog extends Component<any, ModlogState> { } refetch() { - let modlogForm: GetModlogForm = { + let modlogForm: GetModlog = { community_id: this.state.communityId, page: this.state.page, limit: fetchLimit, }; - WebSocketService.Instance.getModlog(modlogForm); + WebSocketService.Instance.client.getModlog(modlogForm); } static fetchInitialData(req: InitialFetchRequest): Promise<any>[] { @@ -448,7 +447,7 @@ export class Modlog extends Component<any, ModlogState> { let communityId = pathSplit[3]; let promises: Promise<any>[] = []; - let modlogForm: GetModlogForm = { + let modlogForm: GetModlog = { page: 1, limit: fetchLimit, }; @@ -461,17 +460,16 @@ export class Modlog extends Component<any, ModlogState> { return promises; } - parseMessage(msg: WebSocketJsonResponse) { - console.log(msg); - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); return; - } else if (res.op == UserOperation.GetModlog) { - let data = res.data as GetModlogResponse; + } else if (op == UserOperation.GetModlog) { + let data = wsJsonToRes<GetModlogResponse>(msg).data; this.state.loading = false; window.scrollTo(0, 0); - this.setCombined(data); + this.state.res = data; this.setState(this.state); } } diff --git a/src/shared/components/navbar.tsx b/src/shared/components/navbar.tsx index f5a4c14..e31d0d5 100644 --- a/src/shared/components/navbar.tsx +++ b/src/shared/components/navbar.tsx @@ -4,19 +4,18 @@ import { Subscription } from 'rxjs'; import { WebSocketService, UserService } from '../services'; import { UserOperation, - GetRepliesForm, + GetReplies, GetRepliesResponse, - GetUserMentionsForm, + GetUserMentions, GetUserMentionsResponse, - GetPrivateMessagesForm, + GetPrivateMessages, PrivateMessagesResponse, SortType, GetSiteResponse, - Comment, + CommentView, CommentResponse, - PrivateMessage, PrivateMessageResponse, - WebSocketJsonResponse, + PrivateMessageView, } from 'lemmy-js-client'; import { wsJsonToRes, @@ -30,20 +29,21 @@ import { isBrowser, wsSubscribe, supportLemmyUrl, + wsUserOp, } from '../utils'; import { i18n } from '../i18next'; import { PictrsImage } from './pictrs-image'; interface NavbarProps { - site: GetSiteResponse; + site_res: GetSiteResponse; } interface NavbarState { isLoggedIn: boolean; expanded: boolean; - replies: Comment[]; - mentions: Comment[]; - messages: PrivateMessage[]; + replies: CommentView[]; + mentions: CommentView[]; + messages: PrivateMessageView[]; unreadCount: number; searchParam: string; toggleSearch: boolean; @@ -56,7 +56,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> { private unreadCountSub: Subscription; private searchTextField: RefObject<HTMLInputElement>; emptyState: NavbarState = { - isLoggedIn: !!this.props.site.my_user, + isLoggedIn: !!this.props.site_res.my_user, unreadCount: 0, replies: [], mentions: [], @@ -88,7 +88,9 @@ export class Navbar extends Component<NavbarProps, NavbarState> { // i18n.changeLanguage('de'); } else { this.requestNotificationPermission(); - WebSocketService.Instance.userJoin(); + WebSocketService.Instance.client.userJoin({ + auth: UserService.Instance.authField(), + }); this.fetchUnreads(); } @@ -96,7 +98,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> { // A login if (res !== undefined) { this.requestNotificationPermission(); - WebSocketService.Instance.getSite(); + WebSocketService.Instance.client.getSite(); } else { this.setState({ isLoggedIn: false }); } @@ -165,20 +167,23 @@ export class Navbar extends Component<NavbarProps, NavbarState> { // TODO class active corresponding to current page navbar() { - let user = this.props.site.my_user; + let user = this.props.site_res.my_user; return ( <nav class="navbar navbar-expand-lg navbar-light shadow-sm p-0 px-3"> <div class="container"> - {this.props.site.site && ( + {this.props.site_res.site_view && ( <Link - title={this.props.site.version} + title={this.props.site_res.version} className="d-flex align-items-center navbar-brand mr-md-3" to="/" > - {this.props.site.site.icon && showAvatars() && ( - <PictrsImage src={this.props.site.site.icon} icon /> + {this.props.site_res.site_view.site.icon && showAvatars() && ( + <PictrsImage + src={this.props.site_res.site_view.site.icon} + icon + /> )} - {this.props.site.site.name} + {this.props.site_res.site_view.site.name} </Link> )} {this.state.isLoggedIn && ( @@ -362,8 +367,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> { i.setState(i.state); } - parseMessage(msg: WebSocketJsonResponse) { - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { if (msg.error == 'not_logged_in') { UserService.Instance.logout(); @@ -371,62 +376,68 @@ export class Navbar extends Component<NavbarProps, NavbarState> { } return; } else if (msg.reconnect) { - WebSocketService.Instance.userJoin(); + WebSocketService.Instance.client.userJoin({ + auth: UserService.Instance.authField(), + }); this.fetchUnreads(); - } else if (res.op == UserOperation.GetReplies) { - let data = res.data as GetRepliesResponse; - let unreadReplies = data.replies.filter(r => !r.read); + } else if (op == UserOperation.GetReplies) { + let data = wsJsonToRes<GetRepliesResponse>(msg).data; + let unreadReplies = data.replies.filter(r => !r.comment.read); this.state.replies = unreadReplies; this.state.unreadCount = this.calculateUnreadCount(); this.setState(this.state); this.sendUnreadCount(); - } else if (res.op == UserOperation.GetUserMentions) { - let data = res.data as GetUserMentionsResponse; - let unreadMentions = data.mentions.filter(r => !r.read); + } else if (op == UserOperation.GetUserMentions) { + let data = wsJsonToRes<GetUserMentionsResponse>(msg).data; + let unreadMentions = data.mentions.filter(r => !r.comment.read); this.state.mentions = unreadMentions; this.state.unreadCount = this.calculateUnreadCount(); this.setState(this.state); this.sendUnreadCount(); - } else if (res.op == UserOperation.GetPrivateMessages) { - let data = res.data as PrivateMessagesResponse; - let unreadMessages = data.messages.filter(r => !r.read); + } else if (op == UserOperation.GetPrivateMessages) { + let data = wsJsonToRes<PrivateMessagesResponse>(msg).data; + let unreadMessages = data.private_messages.filter( + r => !r.private_message.read + ); this.state.messages = unreadMessages; this.state.unreadCount = this.calculateUnreadCount(); this.setState(this.state); this.sendUnreadCount(); - } else if (res.op == UserOperation.GetSite) { + } else if (op == UserOperation.GetSite) { // This is only called on a successful login - let data = res.data as GetSiteResponse; + let data = wsJsonToRes<GetSiteResponse>(msg).data; UserService.Instance.user = data.my_user; setTheme(UserService.Instance.user.theme); i18n.changeLanguage(getLanguage()); this.state.isLoggedIn = true; this.setState(this.state); - } else if (res.op == UserOperation.CreateComment) { - let data = res.data as CommentResponse; + } else if (op == UserOperation.CreateComment) { + let data = wsJsonToRes<CommentResponse>(msg).data; if (this.state.isLoggedIn) { if (data.recipient_ids.includes(UserService.Instance.user.id)) { - this.state.replies.push(data.comment); + this.state.replies.push(data.comment_view); this.state.unreadCount++; this.setState(this.state); this.sendUnreadCount(); - notifyComment(data.comment, this.context.router); + notifyComment(data.comment_view, this.context.router); } } - } else if (res.op == UserOperation.CreatePrivateMessage) { - let data = res.data as PrivateMessageResponse; + } else if (op == UserOperation.CreatePrivateMessage) { + let data = wsJsonToRes<PrivateMessageResponse>(msg).data; if (this.state.isLoggedIn) { - if (data.message.recipient_id == UserService.Instance.user.id) { - this.state.messages.push(data.message); + if ( + data.private_message_view.recipient.id == UserService.Instance.user.id + ) { + this.state.messages.push(data.private_message_view); this.state.unreadCount++; this.setState(this.state); this.sendUnreadCount(); - notifyPrivateMessage(data.message, this.context.router); + notifyPrivateMessage(data.private_message_view, this.context.router); } } } @@ -434,30 +445,33 @@ export class Navbar extends Component<NavbarProps, NavbarState> { fetchUnreads() { console.log('Fetching unreads...'); - let repliesForm: GetRepliesForm = { + let repliesForm: GetReplies = { sort: SortType.New, unread_only: true, page: 1, limit: fetchLimit, + auth: UserService.Instance.authField(), }; - let userMentionsForm: GetUserMentionsForm = { + let userMentionsForm: GetUserMentions = { sort: SortType.New, unread_only: true, page: 1, limit: fetchLimit, + auth: UserService.Instance.authField(), }; - let privateMessagesForm: GetPrivateMessagesForm = { + let privateMessagesForm: GetPrivateMessages = { unread_only: true, page: 1, limit: fetchLimit, + auth: UserService.Instance.authField(), }; if (this.currentLocation !== '/inbox') { - WebSocketService.Instance.getReplies(repliesForm); - WebSocketService.Instance.getUserMentions(userMentionsForm); - WebSocketService.Instance.getPrivateMessages(privateMessagesForm); + WebSocketService.Instance.client.getReplies(repliesForm); + WebSocketService.Instance.client.getUserMentions(userMentionsForm); + WebSocketService.Instance.client.getPrivateMessages(privateMessagesForm); } } @@ -471,17 +485,17 @@ export class Navbar extends Component<NavbarProps, NavbarState> { calculateUnreadCount(): number { return ( - this.state.replies.filter(r => !r.read).length + - this.state.mentions.filter(r => !r.read).length + - this.state.messages.filter(r => !r.read).length + this.state.replies.filter(r => !r.comment.read).length + + this.state.mentions.filter(r => !r.comment.read).length + + this.state.messages.filter(r => !r.private_message.read).length ); } get canAdmin(): boolean { return ( UserService.Instance.user && - this.props.site.admins - .map(a => a.id) + this.props.site_res.admins + .map(a => a.user.id) .includes(UserService.Instance.user.id) ); } diff --git a/src/shared/components/password_change.tsx b/src/shared/components/password_change.tsx index 1725906..4dcd6c6 100644 --- a/src/shared/components/password_change.tsx +++ b/src/shared/components/password_change.tsx @@ -3,9 +3,8 @@ import { Subscription } from 'rxjs'; import { UserOperation, LoginResponse, - PasswordChangeForm, - WebSocketJsonResponse, - Site, + PasswordChange as PasswordChangeForm, + SiteView, } from 'lemmy-js-client'; import { WebSocketService, UserService } from '../services'; import { @@ -15,6 +14,7 @@ import { setIsoData, isBrowser, wsSubscribe, + wsUserOp, } from '../utils'; import { i18n } from '../i18next'; import { HtmlTags } from './html-tags'; @@ -22,7 +22,7 @@ import { HtmlTags } from './html-tags'; interface State { passwordChangeForm: PasswordChangeForm; loading: boolean; - site: Site; + site_view: SiteView; } export class PasswordChange extends Component<any, State> { @@ -36,7 +36,7 @@ export class PasswordChange extends Component<any, State> { password_verify: undefined, }, loading: false, - site: this.isoData.site.site, + site_view: this.isoData.site_res.site_view, }; constructor(props: any, context: any) { @@ -55,7 +55,7 @@ export class PasswordChange extends Component<any, State> { } get documentTitle(): string { - return `${i18n.t('password_change')} - ${this.state.site.name}`; + return `${i18n.t('password_change')} - ${this.state.site_view.site.name}`; } render() { @@ -138,18 +138,18 @@ export class PasswordChange extends Component<any, State> { i.state.loading = true; i.setState(i.state); - WebSocketService.Instance.passwordChange(i.state.passwordChangeForm); + WebSocketService.Instance.client.passwordChange(i.state.passwordChangeForm); } - parseMessage(msg: WebSocketJsonResponse) { - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); this.state.loading = false; this.setState(this.state); return; - } else if (res.op == UserOperation.PasswordChange) { - let data = res.data as LoginResponse; + } else if (op == UserOperation.PasswordChange) { + let data = wsJsonToRes<LoginResponse>(msg).data; this.state = this.emptyState; this.setState(this.state); UserService.Instance.login(data); diff --git a/src/shared/components/post-form.tsx b/src/shared/components/post-form.tsx index 5e683f1..c64104b 100644 --- a/src/shared/components/post-form.tsx +++ b/src/shared/components/post-form.tsx @@ -4,19 +4,19 @@ import { PostListings } from './post-listings'; import { MarkdownTextArea } from './markdown-textarea'; import { Subscription } from 'rxjs'; import { - PostForm as PostFormI, - PostFormParams, - Post, + CreatePost, + EditPost, + PostView, PostResponse, UserOperation, - Community, + CommunityView, SortType, - SearchForm, + Search, SearchType, SearchResponse, - WebSocketJsonResponse, } from 'lemmy-js-client'; import { WebSocketService, UserService } from '../services'; +import { PostFormParams } from '../interfaces'; import { wsJsonToRes, getPageTitle, @@ -33,6 +33,7 @@ import { validTitle, wsSubscribe, isBrowser, + wsUserOp, } from '../utils'; var Choices; @@ -46,24 +47,24 @@ import { pictrsUri } from '../env'; const MAX_POST_TITLE_LENGTH = 200; interface PostFormProps { - post?: Post; // If a post is given, that means this is an edit - communities?: Community[]; + post_view?: PostView; // If a post is given, that means this is an edit + communities?: CommunityView[]; params?: PostFormParams; onCancel?(): any; - onCreate?(id: number): any; - onEdit?(post: Post): any; + onCreate?(post: PostView): any; + onEdit?(post: PostView): any; enableNsfw: boolean; enableDownvotes: boolean; } interface PostFormState { - postForm: PostFormI; + postForm: CreatePost; loading: boolean; imageLoading: boolean; previewMode: boolean; suggestedTitle: string; - suggestedPosts: Post[]; - crossPosts: Post[]; + suggestedPosts: PostView[]; + crossPosts: PostView[]; } export class PostForm extends Component<PostFormProps, PostFormState> { @@ -72,10 +73,10 @@ export class PostForm extends Component<PostFormProps, PostFormState> { private choices: any; private emptyState: PostFormState = { postForm: { + community_id: null, name: null, nsfw: false, - auth: null, - community_id: null, + auth: UserService.Instance.authField(), }, loading: false, imageLoading: false, @@ -93,16 +94,15 @@ export class PostForm extends Component<PostFormProps, PostFormState> { this.state = this.emptyState; - if (this.props.post) { + // Means its an edit + if (this.props.post_view) { this.state.postForm = { - body: this.props.post.body, - // NOTE: debouncing breaks both these for some reason, unless you use defaultValue - name: this.props.post.name, - community_id: this.props.post.community_id, - edit_id: this.props.post.id, - url: this.props.post.url, - nsfw: this.props.post.nsfw, - auth: null, + body: this.props.post_view.post.body, + name: this.props.post_view.post.name, + community_id: this.props.post_view.community.id, + url: this.props.post_view.post.url, + nsfw: this.props.post_view.post.nsfw, + auth: UserService.Instance.authField(), }; } @@ -285,7 +285,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> { /> </div> </div> - {!this.props.post && ( + {!this.props.post_view && ( <div class="form-group row"> <label class="col-sm-2 col-form-label" htmlFor="post-community"> {i18n.t('community')} @@ -298,11 +298,13 @@ export class PostForm extends Component<PostFormProps, PostFormState> { onInput={linkEvent(this, this.handlePostCommunityChange)} > <option>{i18n.t('select_a_community')}</option> - {this.props.communities.map(community => ( - <option value={community.id}> - {community.local - ? community.name - : `${hostname(community.actor_id)}/${community.name}`} + {this.props.communities.map(cv => ( + <option value={cv.community.id}> + {cv.community.local + ? cv.community.name + : `${hostname(cv.community.actor_id)}/${ + cv.community.name + }`} </option> ))} </select> @@ -340,13 +342,13 @@ export class PostForm extends Component<PostFormProps, PostFormState> { <svg class="icon icon-spinner spin"> <use xlinkHref="#icon-spinner"></use> </svg> - ) : this.props.post ? ( + ) : this.props.post_view ? ( capitalizeFirstLetter(i18n.t('save')) ) : ( capitalizeFirstLetter(i18n.t('create')) )} </button> - {this.props.post && ( + {this.props.post_view && ( <button type="button" class="btn btn-secondary" @@ -370,10 +372,14 @@ export class PostForm extends Component<PostFormProps, PostFormState> { i.state.postForm.url = undefined; } - if (i.props.post) { - WebSocketService.Instance.editPost(i.state.postForm); + if (i.props.post_view) { + let form: EditPost = { + ...i.state.postForm, + edit_id: i.props.post_view.post.id, + }; + WebSocketService.Instance.client.editPost(form); } else { - WebSocketService.Instance.createPost(i.state.postForm); + WebSocketService.Instance.client.createPost(i.state.postForm); } i.state.loading = true; i.setState(i.state); @@ -396,15 +402,16 @@ export class PostForm extends Component<PostFormProps, PostFormState> { fetchPageTitle() { if (validURL(this.state.postForm.url)) { - let form: SearchForm = { + let form: Search = { q: this.state.postForm.url, type_: SearchType.Url, sort: SortType.TopAll, page: 1, limit: 6, + auth: UserService.Instance.authField(false), }; - WebSocketService.Instance.search(form); + WebSocketService.Instance.client.search(form); // Fetch the page title getPageTitle(this.state.postForm.url).then(d => { @@ -424,17 +431,18 @@ export class PostForm extends Component<PostFormProps, PostFormState> { } fetchSimilarPosts() { - let form: SearchForm = { + let form: Search = { q: this.state.postForm.name, type_: SearchType.Posts, sort: SortType.TopAll, community_id: this.state.postForm.community_id, page: 1, limit: 6, + auth: UserService.Instance.authField(false), }; if (this.state.postForm.name !== '') { - WebSocketService.Instance.search(form); + WebSocketService.Instance.client.search(form); } else { this.state.suggestedPosts = []; } @@ -570,16 +578,16 @@ export class PostForm extends Component<PostFormProps, PostFormState> { } } - if (this.props.post) { - this.state.postForm.community_id = this.props.post.community_id; + if (this.props.post_view) { + this.state.postForm.community_id = this.props.post_view.community.id; } else if ( this.props.params && (this.props.params.community_id || this.props.params.community_name) ) { if (this.props.params.community_name) { let foundCommunityId = this.props.communities.find( - r => r.name == this.props.params.community_name - ).id; + r => r.community.name == this.props.params.community_name + ).community.id; this.state.postForm.community_id = foundCommunityId; } else if (this.props.params.community_id) { this.state.postForm.community_id = this.props.params.community_id; @@ -596,27 +604,27 @@ export class PostForm extends Component<PostFormProps, PostFormState> { } } - parseMessage(msg: WebSocketJsonResponse) { - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); this.state.loading = false; this.setState(this.state); return; - } else if (res.op == UserOperation.CreatePost) { - let data = res.data as PostResponse; - if (data.post.creator_id == UserService.Instance.user.id) { + } else if (op == UserOperation.CreatePost) { + let data = wsJsonToRes<PostResponse>(msg).data; + if (data.post_view.creator.id == UserService.Instance.user.id) { this.state.loading = false; - this.props.onCreate(data.post.id); + this.props.onCreate(data.post_view); } - } else if (res.op == UserOperation.EditPost) { - let data = res.data as PostResponse; - if (data.post.creator_id == UserService.Instance.user.id) { + } else if (op == UserOperation.EditPost) { + let data = wsJsonToRes<PostResponse>(msg).data; + if (data.post_view.creator.id == UserService.Instance.user.id) { this.state.loading = false; - this.props.onEdit(data.post); + this.props.onEdit(data.post_view); } - } else if (res.op == UserOperation.Search) { - let data = res.data as SearchResponse; + } else if (op == UserOperation.Search) { + let data = wsJsonToRes<SearchResponse>(msg).data; if (data.type_ == SearchType[SearchType.Posts]) { this.state.suggestedPosts = data.posts; diff --git a/src/shared/components/post-listing.tsx b/src/shared/components/post-listing.tsx index b9485b7..881b9b0 100644 --- a/src/shared/components/post-listing.tsx +++ b/src/shared/components/post-listing.tsx @@ -2,21 +2,21 @@ import { Component, linkEvent } from 'inferno'; import { Link } from 'inferno-router'; import { WebSocketService, UserService } from '../services'; import { - Post, - CreatePostLikeForm, - DeletePostForm, - RemovePostForm, - LockPostForm, - StickyPostForm, - SavePostForm, - CommunityUser, - UserView, - BanFromCommunityForm, - BanUserForm, - AddModToCommunityForm, - AddAdminForm, - TransferSiteForm, - TransferCommunityForm, + PostView, + CreatePostLike, + DeletePost, + RemovePost, + LockPost, + StickyPost, + SavePost, + UserViewSafe, + BanFromCommunity, + BanUser, + AddModToCommunity, + AddAdmin, + TransferSite, + TransferCommunity, + CommunityModeratorView, } from 'lemmy-js-client'; import { BanType } from '../interfaces'; import { MomentTime } from './moment-time'; @@ -62,11 +62,12 @@ interface PostListingState { } interface PostListingProps { - post: Post; + post_view: PostView; + duplicates?: PostView[]; showCommunity?: boolean; showBody?: boolean; - moderators?: CommunityUser[]; - admins?: UserView[]; + moderators?: CommunityModeratorView[]; + admins?: UserViewSafe[]; enableDownvotes: boolean; enableNsfw: boolean; } @@ -87,10 +88,10 @@ export class PostListing extends Component<PostListingProps, PostListingState> { viewSource: false, showAdvanced: false, showMoreMobile: false, - my_vote: this.props.post.my_vote, - score: this.props.post.score, - upvotes: this.props.post.upvotes, - downvotes: this.props.post.downvotes, + my_vote: this.props.post_view.my_vote, + score: this.props.post_view.counts.score, + upvotes: this.props.post_view.counts.upvotes, + downvotes: this.props.post_view.counts.downvotes, }; constructor(props: any, context: any) { @@ -104,11 +105,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> { } componentWillReceiveProps(nextProps: PostListingProps) { - this.state.my_vote = nextProps.post.my_vote; - this.state.upvotes = nextProps.post.upvotes; - this.state.downvotes = nextProps.post.downvotes; - this.state.score = nextProps.post.score; - if (this.props.post.id !== nextProps.post.id) { + this.state.my_vote = nextProps.post_view.my_vote; + this.state.upvotes = nextProps.post_view.counts.upvotes; + this.state.downvotes = nextProps.post_view.counts.downvotes; + this.state.score = nextProps.post_view.counts.score; + if (this.props.post_view.post.id !== nextProps.post_view.post.id) { this.state.imageExpanded = false; } this.setState(this.state); @@ -125,7 +126,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { ) : ( <div class="col-12"> <PostForm - post={this.props.post} + post_view={this.props.post_view} onEdit={this.handleEditPost} onCancel={this.handleEditCancel} enableNsfw={this.props.enableNsfw} @@ -138,22 +139,21 @@ export class PostListing extends Component<PostListingProps, PostListingState> { } body() { + let post = this.props.post_view.post; return ( <div class="row"> <div class="col-12"> - {this.props.post.url && - this.props.showBody && - this.props.post.embed_title && ( - <IFramelyCard post={this.props.post} /> - )} + {post.url && this.props.showBody && post.embed_title && ( + <IFramelyCard post={post} /> + )} {this.props.showBody && - this.props.post.body && + post.body && (this.state.viewSource ? ( - <pre>{this.props.post.body}</pre> + <pre>{post.body}</pre> ) : ( <div className="md-div" - dangerouslySetInnerHTML={mdToHtml(this.props.post.body)} + dangerouslySetInnerHTML={mdToHtml(post.body)} /> ))} </div> @@ -162,18 +162,18 @@ export class PostListing extends Component<PostListingProps, PostListingState> { } imgThumb(src: string) { - let post = this.props.post; + let post_view = this.props.post_view; return ( <PictrsImage src={src} thumbnail - nsfw={post.nsfw || post.community_nsfw} + nsfw={post_view.post.nsfw || post_view.community.nsfw} /> ); } getImageSrc(): string { - let post = this.props.post; + let post = this.props.post_view.post; if (isImage(post.url)) { if (post.url.includes('pictrs')) { return post.url; @@ -190,7 +190,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { } thumbnail() { - let post = this.props.post; + let post = this.props.post_view.post; if (isImage(post.url)) { return ( @@ -270,21 +270,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> { } createdLine() { - let post = this.props.post; + let post_view = this.props.post_view; return ( <ul class="list-inline mb-1 text-muted small"> <li className="list-inline-item"> - <UserListing - user={{ - name: post.creator_name, - preferred_username: post.creator_preferred_username, - avatar: post.creator_avatar, - id: post.creator_id, - local: post.creator_local, - actor_id: post.creator_actor_id, - published: post.creator_published, - }} - /> + <UserListing user={post_view.creator} /> {this.isMod && ( <span className="mx-1 badge badge-light">{i18n.t('mod')}</span> @@ -292,36 +282,29 @@ export class PostListing extends Component<PostListingProps, PostListingState> { {this.isAdmin && ( <span className="mx-1 badge badge-light">{i18n.t('admin')}</span> )} - {(post.banned_from_community || post.banned) && ( + {(post_view.creator_banned_from_community || + post_view.creator.banned) && ( <span className="mx-1 badge badge-danger">{i18n.t('banned')}</span> )} {this.props.showCommunity && ( <span> <span class="mx-1"> {i18n.t('to')} </span> - <CommunityLink - community={{ - name: post.community_name, - id: post.community_id, - local: post.community_local, - actor_id: post.community_actor_id, - icon: post.community_icon, - }} - /> + <CommunityLink community={post_view.community} /> </span> )} </li> <li className="list-inline-item">â¢</li> - {post.url && !(hostname(post.url) == externalHost) && ( + {post_view.post.url && !(hostname(post_view.post.url) == externalHost) && ( <> <li className="list-inline-item"> <a className="text-muted font-italic" - href={post.url} + href={post_view.post.url} target="_blank" - title={post.url} + title={post_view.post.url} rel="noopener" > - {hostname(post.url)} + {hostname(post_view.post.url)} </a> </li> <li className="list-inline-item">â¢</li> @@ -329,19 +312,21 @@ export class PostListing extends Component<PostListingProps, PostListingState> { )} <li className="list-inline-item"> <span> - <MomentTime data={post} /> + <MomentTime data={post_view.post} /> </span> </li> - {post.body && ( + {post_view.post.body && ( <> <li className="list-inline-item">â¢</li> <li className="list-inline-item"> {/* Using a link with tippy doesn't work on touch devices unfortunately */} <Link className="text-muted" - data-tippy-content={md.render(previewLines(post.body))} + data-tippy-content={md.render( + previewLines(post_view.post.body) + )} data-tippy-allowHtml={true} - to={`/post/${post.id}`} + to={`/post/${post_view.post.id}`} > <svg class="mr-1 icon icon-inline"> <use xlinkHref="#icon-book-open"></use> @@ -392,7 +377,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { } postTitleLine() { - let post = this.props.post; + let post = this.props.post_view.post; return ( <div className="post-title overflow-hidden"> <h5> @@ -415,7 +400,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { {post.name} </Link> )} - {(isImage(post.url) || this.props.post.thumbnail_url) && + {(isImage(post.url) || post.thumbnail_url) && (!this.state.imageExpanded ? ( <span class="text-monospace unselectable pointer ml-2 text-muted small" @@ -492,22 +477,22 @@ export class PostListing extends Component<PostListingProps, PostListingState> { } commentsLine(mobile: boolean = false) { - let post = this.props.post; + let post_view = this.props.post_view; return ( <div class="d-flex justify-content-between justify-content-lg-start flex-wrap text-muted font-weight-bold mb-1"> <button class="btn btn-link text-muted p-0"> <Link className="text-muted small" title={i18n.t('number_of_comments', { - count: post.number_of_comments, + count: post_view.counts.comments, })} - to={`/post/${post.id}`} + to={`/post/${post_view.post.id}`} > <svg class="mr-1 icon icon-inline"> <use xlinkHref="#icon-message-square"></use> </svg> {i18n.t('number_of_comments', { - count: post.number_of_comments, + count: post_view.counts.comments, })} </Link> </button> @@ -531,12 +516,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> { class="btn btn-link btn-animate text-muted py-0" onClick={linkEvent(this, this.handleSavePostClick)} data-tippy-content={ - post.saved ? i18n.t('unsave') : i18n.t('save') + post_view.saved ? i18n.t('unsave') : i18n.t('save') } > <small> <svg - class={`icon icon-inline ${post.saved && 'text-warning'}`} + class={`icon icon-inline ${ + post_view.saved && 'text-warning' + }`} > <use xlinkHref="#icon-star"></use> </svg> @@ -583,10 +570,12 @@ export class PostListing extends Component<PostListingProps, PostListingState> { class="btn btn-link btn-animate text-muted py-0 pl-1 pr-0" onClick={linkEvent(this, this.handleSavePostClick)} data-tippy-content={ - post.saved ? i18n.t('unsave') : i18n.t('save') + post_view.saved ? i18n.t('unsave') : i18n.t('save') } > - <svg class={`icon icon-inline ${post.saved && 'text-warning'}`}> + <svg + class={`icon icon-inline ${post_view.saved && 'text-warning'}`} + > <use xlinkHref="#icon-star"></use> </svg> </button> @@ -611,20 +600,18 @@ export class PostListing extends Component<PostListingProps, PostListingState> { duplicatesLine() { return ( - this.props.post.duplicates && ( + this.props.duplicates && ( <ul class="list-inline mb-1 small text-muted"> <> <li className="list-inline-item mr-2"> {i18n.t('cross_posted_to')} </li> - {this.props.post.duplicates.map(post => ( + {this.props.duplicates.map(pv => ( <li className="list-inline-item mr-2"> - <Link to={`/post/${post.id}`}> - {post.community_local - ? post.community_name - : `${post.community_name}@${hostname( - post.community_actor_id - )}`} + <Link to={`/post/${pv.post.id}`}> + {pv.community.local + ? pv.community.name + : `${pv.community.name}@${hostname(pv.community.actor_id)}`} </Link> </li> ))} @@ -635,7 +622,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { } postActions(mobile: boolean = false) { - let post = this.props.post; + let post_view = this.props.post_view; return ( UserService.Instance.user && ( <> @@ -646,11 +633,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> { class="btn btn-link btn-animate text-muted py-0 pl-0" onClick={linkEvent(this, this.handleSavePostClick)} data-tippy-content={ - post.saved ? i18n.t('unsave') : i18n.t('save') + post_view.saved ? i18n.t('unsave') : i18n.t('save') } > <svg - class={`icon icon-inline ${post.saved && 'text-warning'}`} + class={`icon icon-inline ${ + post_view.saved && 'text-warning' + }`} > <use xlinkHref="#icon-star"></use> </svg> @@ -682,11 +671,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> { class="btn btn-link btn-animate text-muted py-0" onClick={linkEvent(this, this.handleDeleteClick)} data-tippy-content={ - !post.deleted ? i18n.t('delete') : i18n.t('restore') + !post_view.post.deleted ? i18n.t('delete') : i18n.t('restore') } > <svg - class={`icon icon-inline ${post.deleted && 'text-danger'}`} + class={`icon icon-inline ${ + post_view.post.deleted && 'text-danger' + }`} > <use xlinkHref="#icon-trash"></use> </svg> @@ -706,7 +697,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { </button> ) : ( <> - {this.props.showBody && post.body && ( + {this.props.showBody && post_view.post.body && ( <button class="btn btn-link btn-animate text-muted py-0" onClick={linkEvent(this, this.handleViewSource)} @@ -727,11 +718,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> { class="btn btn-link btn-animate text-muted py-0" onClick={linkEvent(this, this.handleModLock)} data-tippy-content={ - post.locked ? i18n.t('unlock') : i18n.t('lock') + post_view.post.locked ? i18n.t('unlock') : i18n.t('lock') } > <svg - class={`icon icon-inline ${post.locked && 'text-danger'}`} + class={`icon icon-inline ${ + post_view.post.locked && 'text-danger' + }`} > <use xlinkHref="#icon-lock"></use> </svg> @@ -740,12 +733,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> { class="btn btn-link btn-animate text-muted py-0" onClick={linkEvent(this, this.handleModSticky)} data-tippy-content={ - post.stickied ? i18n.t('unsticky') : i18n.t('sticky') + post_view.post.stickied + ? i18n.t('unsticky') + : i18n.t('sticky') } > <svg class={`icon icon-inline ${ - post.stickied && 'text-success' + post_view.post.stickied && 'text-success' }`} > <use xlinkHref="#icon-pin"></use> @@ -755,7 +750,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { )} {/* Mods can ban from community, and appoint as mods to community */} {(this.canMod || this.canAdmin) && - (!post.removed ? ( + (!post_view.post.removed ? ( <button class="btn btn-link btn-animate text-muted py-0" onClick={linkEvent(this, this.handleModRemoveShow)} @@ -773,7 +768,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { {this.canMod && ( <> {!this.isMod && - (!post.banned_from_community ? ( + (!post_view.creator_banned_from_community ? ( <button class="btn btn-link btn-animate text-muted py-0" onClick={linkEvent( @@ -794,22 +789,23 @@ export class PostListing extends Component<PostListingProps, PostListingState> { {i18n.t('unban')} </button> ))} - {!post.banned_from_community && post.creator_local && ( - <button - class="btn btn-link btn-animate text-muted py-0" - onClick={linkEvent(this, this.handleAddModToCommunity)} - > - {this.isMod - ? i18n.t('remove_as_mod') - : i18n.t('appoint_as_mod')} - </button> - )} + {!post_view.creator_banned_from_community && + post_view.creator.local && ( + <button + class="btn btn-link btn-animate text-muted py-0" + onClick={linkEvent(this, this.handleAddModToCommunity)} + > + {this.isMod + ? i18n.t('remove_as_mod') + : i18n.t('appoint_as_mod')} + </button> + )} </> )} {/* Community creators and admins can transfer community to another mod */} {(this.amCommunityCreator || this.canAdmin) && this.isMod && - post.creator_local && + post_view.creator.local && (!this.state.showConfirmTransferCommunity ? ( <button class="btn btn-link btn-animate text-muted py-0" @@ -846,7 +842,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { {this.canAdmin && ( <> {!this.isAdmin && - (!post.banned ? ( + (!post_view.creator.banned ? ( <button class="btn btn-link btn-animate text-muted py-0" onClick={linkEvent(this, this.handleModBanShow)} @@ -861,7 +857,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { {i18n.t('unban_from_site')} </button> ))} - {!post.banned && post.creator_local && ( + {!post_view.creator.banned && post_view.creator.local && ( <button class="btn btn-link btn-animate text-muted py-0" onClick={linkEvent(this, this.handleAddAdmin)} @@ -916,7 +912,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { } removeAndBanDialogs() { - let post = this.props.post; + let post = this.props.post_view; return ( <> {this.state.showRemoveDialog && ( @@ -972,7 +968,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { {/* </div> */} <div class="form-group row"> <button type="submit" class="btn btn-secondary"> - {i18n.t('ban')} {post.creator_name} + {i18n.t('ban')} {post.creator.name} </button> </div> </form> @@ -982,7 +978,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> { } mobileThumbnail() { - return this.props.post.thumbnail_url || isImage(this.props.post.url) ? ( + let post = this.props.post_view.post; + return post.thumbnail_url || isImage(post.url) ? ( <div class="row"> <div className={`${this.state.imageExpanded ? 'col-12' : 'col-8'}`}> {this.postTitleLine()} @@ -998,13 +995,14 @@ export class PostListing extends Component<PostListingProps, PostListingState> { } showMobilePreview() { + let post = this.props.post_view.post; return ( - this.props.post.body && + post.body && !this.props.showBody && ( <div className="md-div mb-1" dangerouslySetInnerHTML={{ - __html: md.render(previewLines(this.props.post.body)), + __html: md.render(previewLines(post.body)), }} /> ) @@ -1067,7 +1065,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> { private get myPost(): boolean { return ( UserService.Instance.user && - this.props.post.creator_id == UserService.Instance.user.id + this.props.post_view.creator.id == UserService.Instance.user.id ); } @@ -1075,8 +1073,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> { return ( this.props.moderators && isMod( - this.props.moderators.map(m => m.user_id), - this.props.post.creator_id + this.props.moderators.map(m => m.moderator.id), + this.props.post_view.creator.id ) ); } @@ -1085,8 +1083,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> { return ( this.props.admins && isMod( - this.props.admins.map(a => a.id), - this.props.post.creator_id + this.props.admins.map(a => a.user.id), + this.props.post_view.creator.id ) ); } @@ -1094,13 +1092,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> { get canMod(): boolean { if (this.props.admins && this.props.moderators) { let adminsThenMods = this.props.admins - .map(a => a.id) - .concat(this.props.moderators.map(m => m.user_id)); + .map(a => a.user.id) + .concat(this.props.moderators.map(m => m.moderator.id)); return canMod( UserService.Instance.user, adminsThenMods, - this.props.post.creator_id + this.props.post_view.creator.id ); } else { return false; @@ -1110,13 +1108,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> { get canModOnSelf(): boolean { if (this.props.admins && this.props.moderators) { let adminsThenMods = this.props.admins - .map(a => a.id) - .concat(this.props.moderators.map(m => m.user_id)); + .map(a => a.user.id) + .concat(this.props.moderators.map(m => m.moderator.id)); return canMod( UserService.Instance.user, adminsThenMods, - this.props.post.creator_id, + this.props.post_view.creator.id, true ); } else { @@ -1129,8 +1127,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> { this.props.admins && canMod( UserService.Instance.user, - this.props.admins.map(a => a.id), - this.props.post.creator_id + this.props.admins.map(a => a.user.id), + this.props.post_view.creator.id ) ); } @@ -1139,8 +1137,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> { return ( this.props.moderators && UserService.Instance.user && - this.props.post.creator_id != UserService.Instance.user.id && - UserService.Instance.user.id == this.props.moderators[0].user_id + this.props.post_view.creator.id != UserService.Instance.user.id && + UserService.Instance.user.id == this.props.moderators[0].moderator.id ); } @@ -1148,8 +1146,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> { return ( this.props.admins && UserService.Instance.user && - this.props.post.creator_id != UserService.Instance.user.id && - UserService.Instance.user.id == this.props.admins[0].id + this.props.post_view.creator.id != UserService.Instance.user.id && + UserService.Instance.user.id == this.props.admins[0].user.id ); } @@ -1174,12 +1172,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> { i.state.my_vote = new_vote; - let form: CreatePostLikeForm = { - post_id: i.props.post.id, + let form: CreatePostLike = { + post_id: i.props.post_view.post.id, score: i.state.my_vote, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.likePost(form); + WebSocketService.Instance.client.likePost(form); i.setState(i.state); setupTippy(); } @@ -1205,12 +1204,13 @@ export class PostListing extends Component<PostListingProps, PostListingState> { i.state.my_vote = new_vote; - let form: CreatePostLikeForm = { - post_id: i.props.post.id, + let form: CreatePostLike = { + post_id: i.props.post_view.post.id, score: i.state.my_vote, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.likePost(form); + WebSocketService.Instance.client.likePost(form); i.setState(i.state); setupTippy(); } @@ -1232,33 +1232,35 @@ export class PostListing extends Component<PostListingProps, PostListingState> { } handleDeleteClick(i: PostListing) { - let deleteForm: DeletePostForm = { - edit_id: i.props.post.id, - deleted: !i.props.post.deleted, - auth: null, + let deleteForm: DeletePost = { + edit_id: i.props.post_view.post.id, + deleted: !i.props.post_view.post.deleted, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.deletePost(deleteForm); + WebSocketService.Instance.client.deletePost(deleteForm); } handleSavePostClick(i: PostListing) { - let saved = i.props.post.saved == undefined ? true : !i.props.post.saved; - let form: SavePostForm = { - post_id: i.props.post.id, + let saved = + i.props.post_view.saved == undefined ? true : !i.props.post_view.saved; + let form: SavePost = { + post_id: i.props.post_view.post.id, save: saved, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.savePost(form); + WebSocketService.Instance.client.savePost(form); } get crossPostParams(): string { - let params = `?title=${this.props.post.name}`; - let post = this.props.post; + let post = this.props.post_view.post; + let params = `?title=${post.name}`; if (post.url) { params += `&url=${encodeURIComponent(post.url)}`; } - if (this.props.post.body) { - params += `&body=${this.props.post.body}`; + if (post.body) { + params += `&body=${post.body}`; } return params; } @@ -1280,34 +1282,34 @@ export class PostListing extends Component<PostListingProps, PostListingState> { handleModRemoveSubmit(i: PostListing) { event.preventDefault(); - let form: RemovePostForm = { - edit_id: i.props.post.id, - removed: !i.props.post.removed, + let form: RemovePost = { + edit_id: i.props.post_view.post.id, + removed: !i.props.post_view.post.removed, reason: i.state.removeReason, - auth: null, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.removePost(form); + WebSocketService.Instance.client.removePost(form); i.state.showRemoveDialog = false; i.setState(i.state); } handleModLock(i: PostListing) { - let form: LockPostForm = { - edit_id: i.props.post.id, - locked: !i.props.post.locked, - auth: null, + let form: LockPost = { + edit_id: i.props.post_view.post.id, + locked: !i.props.post_view.post.locked, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.lockPost(form); + WebSocketService.Instance.client.lockPost(form); } handleModSticky(i: PostListing) { - let form: StickyPostForm = { - edit_id: i.props.post.id, - stickied: !i.props.post.stickied, - auth: null, + let form: StickyPost = { + edit_id: i.props.post_view.post.id, + stickied: !i.props.post_view.post.stickied, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.stickyPost(form); + WebSocketService.Instance.client.stickyPost(form); } handleModBanFromCommunityShow(i: PostListing) { @@ -1349,33 +1351,35 @@ export class PostListing extends Component<PostListingProps, PostListingState> { if (i.state.banType == BanType.Community) { // If its an unban, restore all their data - let ban = !i.props.post.banned_from_community; + let ban = !i.props.post_view.creator_banned_from_community; if (ban == false) { i.state.removeData = false; } - let form: BanFromCommunityForm = { - user_id: i.props.post.creator_id, - community_id: i.props.post.community_id, + let form: BanFromCommunity = { + user_id: i.props.post_view.creator.id, + community_id: i.props.post_view.community.id, ban, remove_data: i.state.removeData, reason: i.state.banReason, expires: getUnixTime(i.state.banExpires), + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.banFromCommunity(form); + WebSocketService.Instance.client.banFromCommunity(form); } else { // If its an unban, restore all their data - let ban = !i.props.post.banned; + let ban = !i.props.post_view.creator.banned; if (ban == false) { i.state.removeData = false; } - let form: BanUserForm = { - user_id: i.props.post.creator_id, + let form: BanUser = { + user_id: i.props.post_view.creator.id, ban, remove_data: i.state.removeData, reason: i.state.banReason, expires: getUnixTime(i.state.banExpires), + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.banUser(form); + WebSocketService.Instance.client.banUser(form); } i.state.showBanDialog = false; @@ -1383,21 +1387,23 @@ export class PostListing extends Component<PostListingProps, PostListingState> { } handleAddModToCommunity(i: PostListing) { - let form: AddModToCommunityForm = { - user_id: i.props.post.creator_id, - community_id: i.props.post.community_id, + let form: AddModToCommunity = { + user_id: i.props.post_view.creator.id, + community_id: i.props.post_view.community.id, added: !i.isMod, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.addModToCommunity(form); + WebSocketService.Instance.client.addModToCommunity(form); i.setState(i.state); } handleAddAdmin(i: PostListing) { - let form: AddAdminForm = { - user_id: i.props.post.creator_id, + let form: AddAdmin = { + user_id: i.props.post_view.creator.id, added: !i.isAdmin, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.addAdmin(form); + WebSocketService.Instance.client.addAdmin(form); i.setState(i.state); } @@ -1412,11 +1418,12 @@ export class PostListing extends Component<PostListingProps, PostListingState> { } handleTransferCommunity(i: PostListing) { - let form: TransferCommunityForm = { - community_id: i.props.post.community_id, - user_id: i.props.post.creator_id, + let form: TransferCommunity = { + community_id: i.props.post_view.community.id, + user_id: i.props.post_view.creator.id, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.transferCommunity(form); + WebSocketService.Instance.client.transferCommunity(form); i.state.showConfirmTransferCommunity = false; i.setState(i.state); } @@ -1432,10 +1439,11 @@ export class PostListing extends Component<PostListingProps, PostListingState> { } handleTransferSite(i: PostListing) { - let form: TransferSiteForm = { - user_id: i.props.post.creator_id, + let form: TransferSite = { + user_id: i.props.post_view.creator.id, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.transferSite(form); + WebSocketService.Instance.client.transferSite(form); i.state.showConfirmTransferSite = false; i.setState(i.state); } diff --git a/src/shared/components/post-listings.tsx b/src/shared/components/post-listings.tsx index 47aab75..a7e0693 100644 --- a/src/shared/components/post-listings.tsx +++ b/src/shared/components/post-listings.tsx @@ -1,13 +1,13 @@ import { Component } from 'inferno'; import { Link } from 'inferno-router'; -import { Post, SortType } from 'lemmy-js-client'; +import { PostView, SortType } from 'lemmy-js-client'; import { postSort } from '../utils'; import { PostListing } from './post-listing'; import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; interface PostListingsProps { - posts: Post[]; + posts: PostView[]; showCommunity?: boolean; removeDuplicates?: boolean; sort?: SortType; @@ -16,6 +16,8 @@ interface PostListingsProps { } export class PostListings extends Component<PostListingsProps, any> { + private duplicatesMap = new Map<number, PostView[]>(); + constructor(props: any, context: any) { super(props, context); } @@ -24,10 +26,11 @@ export class PostListings extends Component<PostListingsProps, any> { return ( <div> {this.props.posts.length > 0 ? ( - this.outer().map(post => ( + this.outer().map(post_view => ( <> <PostListing - post={post} + post_view={post_view} + duplicates={this.duplicatesMap.get(post_view.post.id)} showCommunity={this.props.showCommunity} enableDownvotes={this.props.enableDownvotes} enableNsfw={this.props.enableNsfw} @@ -49,7 +52,7 @@ export class PostListings extends Component<PostListingsProps, any> { ); } - outer(): Post[] { + outer(): PostView[] { let out = this.props.posts; if (this.props.removeDuplicates) { out = this.removeDuplicates(out); @@ -62,23 +65,23 @@ export class PostListings extends Component<PostListingsProps, any> { return out; } - removeDuplicates(posts: Post[]): Post[] { + removeDuplicates(posts: PostView[]): PostView[] { // A map from post url to list of posts (dupes) - let urlMap = new Map<string, Post[]>(); + let urlMap = new Map<string, PostView[]>(); // Loop over the posts, find ones with same urls - for (let post of posts) { + for (let pv of posts) { if ( - post.url && - !post.deleted && - !post.removed && - !post.community_deleted && - !post.community_removed + pv.post.url && + !pv.post.deleted && + !pv.post.removed && + !pv.community.deleted && + !pv.community.removed ) { - if (!urlMap.get(post.url)) { - urlMap.set(post.url, [post]); + if (!urlMap.get(pv.post.url)) { + urlMap.set(pv.post.url, [pv]); } else { - urlMap.get(post.url).push(post); + urlMap.get(pv.post.url).push(pv); } } } @@ -89,18 +92,18 @@ export class PostListings extends Component<PostListingsProps, any> { if (e[1].length == 1) { urlMap.delete(e[0]); } else { - e[1].sort((a, b) => a.published.localeCompare(b.published)); + e[1].sort((a, b) => a.post.published.localeCompare(b.post.published)); } } for (let i = 0; i < posts.length; i++) { - let post = posts[i]; - if (post.url) { - let found = urlMap.get(post.url); + let pv = posts[i]; + if (pv.post.url) { + let found = urlMap.get(pv.post.url); if (found) { // If its the oldest, add - if (post.id == found[0].id) { - post.duplicates = found.slice(1); + if (pv.post.id == found[0].post.id) { + this.duplicatesMap.set(pv.post.id, found.slice(1)); } // Otherwise, delete it else { diff --git a/src/shared/components/post.tsx b/src/shared/components/post.tsx index 017ff45..1d800fc 100644 --- a/src/shared/components/post.tsx +++ b/src/shared/components/post.tsx @@ -3,25 +3,23 @@ import { HtmlTags } from './html-tags'; import { Subscription } from 'rxjs'; import { UserOperation, - Post as PostI, + PostView, GetPostResponse, PostResponse, - MarkCommentAsReadForm, + MarkCommentAsRead, CommentResponse, CommunityResponse, - CommentNode as CommentNodeI, BanFromCommunityResponse, BanUserResponse, AddModToCommunityResponse, AddAdminResponse, SearchType, SortType, - SearchForm, - GetPostForm, + Search, + GetPost, SearchResponse, GetSiteResponse, GetCommunityResponse, - WebSocketJsonResponse, ListCategoriesResponse, Category, } from 'lemmy-js-client'; @@ -29,6 +27,7 @@ import { CommentSortType, CommentViewType, InitialFetchRequest, + CommentNode as CommentNodeI, } from '../interfaces'; import { WebSocketService, UserService } from '../services'; import { @@ -44,10 +43,10 @@ import { getIdFromProps, getCommentIdFromProps, wsSubscribe, - setAuth, isBrowser, previewLines, isImage, + wsUserOp, } from '../utils'; import { PostListing } from './post-listing'; import { Sidebar } from './sidebar'; @@ -64,7 +63,7 @@ interface PostState { commentViewType: CommentViewType; scrolled?: boolean; loading: boolean; - crossPosts: PostI[]; + crossPosts: PostView[]; siteRes: GetSiteResponse; categories: Category[]; } @@ -81,7 +80,7 @@ export class Post extends Component<any, PostState> { scrolled: false, loading: true, crossPosts: [], - siteRes: this.isoData.site, + siteRes: this.isoData.site_res, categories: [], }; @@ -104,15 +103,16 @@ export class Post extends Component<any, PostState> { } } else { this.fetchPost(); - WebSocketService.Instance.listCategories(); + WebSocketService.Instance.client.listCategories(); } } fetchPost() { - let form: GetPostForm = { + let form: GetPost = { id: this.state.postId, + auth: UserService.Instance.authField(false), }; - WebSocketService.Instance.getPost(form); + WebSocketService.Instance.client.getPost(form); } static fetchInitialData(req: InitialFetchRequest): Promise<any>[] { @@ -121,10 +121,10 @@ export class Post extends Component<any, PostState> { let id = Number(pathSplit[2]); - let postForm: GetPostForm = { + let postForm: GetPost = { id, + auth: req.auth, }; - setAuth(postForm, req.auth); promises.push(req.client.getPost(postForm)); promises.push(req.client.listCategories()); @@ -138,7 +138,7 @@ export class Post extends Component<any, PostState> { } componentDidMount() { - WebSocketService.Instance.postJoin({ post_id: this.state.postId }); + WebSocketService.Instance.client.postJoin({ post_id: this.state.postId }); autosize(document.querySelectorAll('textarea')); } @@ -172,23 +172,28 @@ export class Post extends Component<any, PostState> { this.markScrolledAsRead(this.state.commentId); } + // TODO this needs some re-work markScrolledAsRead(commentId: number) { - let found = this.state.postRes.comments.find(c => c.id == commentId); - let parent = this.state.postRes.comments.find(c => found.parent_id == c.id); + let found = this.state.postRes.comments.find( + c => c.comment.id == commentId + ); + let parent = this.state.postRes.comments.find( + c => found.comment.parent_id == c.comment.id + ); let parent_user_id = parent - ? parent.creator_id - : this.state.postRes.post.creator_id; + ? parent.creator.id + : this.state.postRes.post_view.creator.id; if ( UserService.Instance.user && UserService.Instance.user.id == parent_user_id ) { - let form: MarkCommentAsReadForm = { - edit_id: found.id, + let form: MarkCommentAsRead = { + comment_id: found.creator.id, read: true, - auth: null, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.markCommentAsRead(form); + WebSocketService.Instance.client.markCommentAsRead(form); UserService.Instance.unreadCountSub.next( UserService.Instance.unreadCountSub.value - 1 ); @@ -196,27 +201,24 @@ export class Post extends Component<any, PostState> { } get documentTitle(): string { - return `${this.state.postRes.post.name} - ${this.state.siteRes.site.name}`; + return `${this.state.postRes.post_view.post.name} - ${this.state.siteRes.site_view.site.name}`; } get imageTag(): string { + let post = this.state.postRes.post_view.post; return ( - this.state.postRes.post.thumbnail_url || - (this.state.postRes.post.url - ? isImage(this.state.postRes.post.url) - ? this.state.postRes.post.url - : undefined - : undefined) + post.thumbnail_url || + (post.url ? (isImage(post.url) ? post.url : undefined) : undefined) ); } get descriptionTag(): string { - return this.state.postRes.post.body - ? previewLines(this.state.postRes.post.body) - : undefined; + let body = this.state.postRes.post_view.post.body; + return body ? previewLines(body) : undefined; } render() { + let pv = this.state.postRes.post_view; return ( <div class="container"> {this.state.loading ? ( @@ -235,18 +237,21 @@ export class Post extends Component<any, PostState> { description={this.descriptionTag} /> <PostListing - post={this.state.postRes.post} + post_view={pv} + duplicates={this.state.crossPosts} showBody showCommunity moderators={this.state.postRes.moderators} admins={this.state.siteRes.admins} - enableDownvotes={this.state.siteRes.site.enable_downvotes} - enableNsfw={this.state.siteRes.site.enable_nsfw} + enableDownvotes={ + this.state.siteRes.site_view.site.enable_downvotes + } + enableNsfw={this.state.siteRes.site_view.site.enable_nsfw} /> <div className="mb-2" /> <CommentForm postId={this.state.postId} - disabled={this.state.postRes.post.locked} + disabled={pv.post.locked} /> {this.state.postRes.comments.length > 0 && this.sortRadios()} {this.state.commentViewType == CommentViewType.Tree && @@ -343,12 +348,12 @@ export class Post extends Component<any, PostState> { <CommentNodes nodes={commentsToFlatNodes(this.state.postRes.comments)} noIndent - locked={this.state.postRes.post.locked} + locked={this.state.postRes.post_view.post.locked} moderators={this.state.postRes.moderators} admins={this.state.siteRes.admins} - postCreatorId={this.state.postRes.post.creator_id} + postCreatorId={this.state.postRes.post_view.creator.id} showContext - enableDownvotes={this.state.siteRes.site.enable_downvotes} + enableDownvotes={this.state.siteRes.site_view.site.enable_downvotes} sort={this.state.commentSort} /> </div> @@ -359,11 +364,11 @@ export class Post extends Component<any, PostState> { return ( <div class="mb-3"> <Sidebar - community={this.state.postRes.community} + community_view={this.state.postRes.community_view} moderators={this.state.postRes.moderators} admins={this.state.siteRes.admins} online={this.state.postRes.online} - enableNsfw={this.state.siteRes.site.enable_nsfw} + enableNsfw={this.state.siteRes.site_view.site.enable_nsfw} showIcon categories={this.state.categories} /> @@ -385,18 +390,18 @@ export class Post extends Component<any, PostState> { buildCommentsTree(): CommentNodeI[] { let map = new Map<number, CommentNodeI>(); - for (let comment of this.state.postRes.comments) { + for (let comment_view of this.state.postRes.comments) { let node: CommentNodeI = { - comment: comment, + comment_view: comment_view, children: [], }; - map.set(comment.id, { ...node }); + map.set(comment_view.comment.id, { ...node }); } let tree: CommentNodeI[] = []; - for (let comment of this.state.postRes.comments) { - let child = map.get(comment.id); - if (comment.parent_id) { - let parent_ = map.get(comment.parent_id); + for (let comment_view of this.state.postRes.comments) { + let child = map.get(comment_view.comment.id); + if (comment_view.comment.parent_id) { + let parent_ = map.get(comment_view.comment.parent_id); parent_.children.push(child); } else { tree.push(child); @@ -410,7 +415,7 @@ export class Post extends Component<any, PostState> { setDepth(node: CommentNodeI, i: number = 0): void { for (let child of node.children) { - child.comment.depth = i; + child.depth = i; this.setDepth(child, i + 1); } } @@ -421,155 +426,146 @@ export class Post extends Component<any, PostState> { <div> <CommentNodes nodes={nodes} - locked={this.state.postRes.post.locked} + locked={this.state.postRes.post_view.post.locked} moderators={this.state.postRes.moderators} admins={this.state.siteRes.admins} - postCreatorId={this.state.postRes.post.creator_id} + postCreatorId={this.state.postRes.post_view.creator.id} sort={this.state.commentSort} - enableDownvotes={this.state.siteRes.site.enable_downvotes} + enableDownvotes={this.state.siteRes.site_view.site.enable_downvotes} /> </div> ); } - parseMessage(msg: WebSocketJsonResponse) { - console.log(msg); - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); return; } else if (msg.reconnect) { let postId = Number(this.props.match.params.id); - WebSocketService.Instance.postJoin({ post_id: postId }); - WebSocketService.Instance.getPost({ + WebSocketService.Instance.client.postJoin({ post_id: postId }); + WebSocketService.Instance.client.getPost({ id: postId, + auth: UserService.Instance.authField(false), }); - } else if (res.op == UserOperation.GetPost) { - let data = res.data as GetPostResponse; + } else if (op == UserOperation.GetPost) { + let data = wsJsonToRes<GetPostResponse>(msg).data; this.state.postRes = data; this.state.loading = false; // Get cross-posts - if (this.state.postRes.post.url) { - let form: SearchForm = { - q: this.state.postRes.post.url, + if (this.state.postRes.post_view.post.url) { + let form: Search = { + q: this.state.postRes.post_view.post.url, type_: SearchType.Url, sort: SortType.TopAll, page: 1, limit: 6, + auth: UserService.Instance.authField(false), }; - WebSocketService.Instance.search(form); + WebSocketService.Instance.client.search(form); } this.setState(this.state); setupTippy(); - } else if (res.op == UserOperation.CreateComment) { - let data = res.data as CommentResponse; + } else if (op == UserOperation.CreateComment) { + let data = wsJsonToRes<CommentResponse>(msg).data; // Necessary since it might be a user reply if (data.recipient_ids.length == 0) { - this.state.postRes.comments.unshift(data.comment); + this.state.postRes.comments.unshift(data.comment_view); this.setState(this.state); } } else if ( - res.op == UserOperation.EditComment || - res.op == UserOperation.DeleteComment || - res.op == UserOperation.RemoveComment + op == UserOperation.EditComment || + op == UserOperation.DeleteComment || + op == UserOperation.RemoveComment ) { - let data = res.data as CommentResponse; - editCommentRes(data, this.state.postRes.comments); + let data = wsJsonToRes<CommentResponse>(msg).data; + editCommentRes(data.comment_view, this.state.postRes.comments); this.setState(this.state); - } else if (res.op == UserOperation.SaveComment) { - let data = res.data as CommentResponse; - saveCommentRes(data, this.state.postRes.comments); + } else if (op == UserOperation.SaveComment) { + let data = wsJsonToRes<CommentResponse>(msg).data; + saveCommentRes(data.comment_view, this.state.postRes.comments); this.setState(this.state); setupTippy(); - } else if (res.op == UserOperation.CreateCommentLike) { - let data = res.data as CommentResponse; - createCommentLikeRes(data, this.state.postRes.comments); + } else if (op == UserOperation.CreateCommentLike) { + let data = wsJsonToRes<CommentResponse>(msg).data; + createCommentLikeRes(data.comment_view, this.state.postRes.comments); this.setState(this.state); - } else if (res.op == UserOperation.CreatePostLike) { - let data = res.data as PostResponse; - createPostLikeRes(data, this.state.postRes.post); + } else if (op == UserOperation.CreatePostLike) { + let data = wsJsonToRes<PostResponse>(msg).data; + createPostLikeRes(data.post_view, this.state.postRes.post_view); this.setState(this.state); } else if ( - res.op == UserOperation.EditPost || - res.op == UserOperation.DeletePost || - res.op == UserOperation.RemovePost || - res.op == UserOperation.LockPost || - res.op == UserOperation.StickyPost + op == UserOperation.EditPost || + op == UserOperation.DeletePost || + op == UserOperation.RemovePost || + op == UserOperation.LockPost || + op == UserOperation.StickyPost || + op == UserOperation.SavePost ) { - let data = res.data as PostResponse; - this.state.postRes.post = data.post; - this.setState(this.state); - setupTippy(); - } else if (res.op == UserOperation.SavePost) { - let data = res.data as PostResponse; - this.state.postRes.post = data.post; + let data = wsJsonToRes<PostResponse>(msg).data; + this.state.postRes.post_view = data.post_view; this.setState(this.state); setupTippy(); } else if ( - res.op == UserOperation.EditCommunity || - res.op == UserOperation.DeleteCommunity || - res.op == UserOperation.RemoveCommunity + op == UserOperation.EditCommunity || + op == UserOperation.DeleteCommunity || + op == UserOperation.RemoveCommunity || + op == UserOperation.FollowCommunity ) { - let data = res.data as CommunityResponse; - this.state.postRes.community = data.community; - this.state.postRes.post.community_id = data.community.id; - this.state.postRes.post.community_name = data.community.name; + let data = wsJsonToRes<CommunityResponse>(msg).data; + this.state.postRes.community_view = data.community_view; + this.state.postRes.post_view.community = data.community_view.community; this.setState(this.state); - } else if (res.op == UserOperation.FollowCommunity) { - let data = res.data as CommunityResponse; - this.state.postRes.community.subscribed = data.community.subscribed; - this.state.postRes.community.number_of_subscribers = - data.community.number_of_subscribers; this.setState(this.state); - } else if (res.op == UserOperation.BanFromCommunity) { - let data = res.data as BanFromCommunityResponse; + } else if (op == UserOperation.BanFromCommunity) { + let data = wsJsonToRes<BanFromCommunityResponse>(msg).data; this.state.postRes.comments - .filter(c => c.creator_id == data.user.id) - .forEach(c => (c.banned_from_community = data.banned)); - if (this.state.postRes.post.creator_id == data.user.id) { - this.state.postRes.post.banned_from_community = data.banned; + .filter(c => c.creator.id == data.user_view.user.id) + .forEach(c => (c.creator_banned_from_community = data.banned)); + if (this.state.postRes.post_view.creator.id == data.user_view.user.id) { + this.state.postRes.post_view.creator_banned_from_community = + data.banned; } this.setState(this.state); - } else if (res.op == UserOperation.AddModToCommunity) { - let data = res.data as AddModToCommunityResponse; + } else if (op == UserOperation.AddModToCommunity) { + let data = wsJsonToRes<AddModToCommunityResponse>(msg).data; this.state.postRes.moderators = data.moderators; this.setState(this.state); - } else if (res.op == UserOperation.BanUser) { - let data = res.data as BanUserResponse; + } else if (op == UserOperation.BanUser) { + let data = wsJsonToRes<BanUserResponse>(msg).data; this.state.postRes.comments - .filter(c => c.creator_id == data.user.id) - .forEach(c => (c.banned = data.banned)); - if (this.state.postRes.post.creator_id == data.user.id) { - this.state.postRes.post.banned = data.banned; + .filter(c => c.creator.id == data.user_view.user.id) + .forEach(c => (c.creator.banned = data.banned)); + if (this.state.postRes.post_view.creator.id == data.user_view.user.id) { + this.state.postRes.post_view.creator.banned = data.banned; } this.setState(this.state); - } else if (res.op == UserOperation.AddAdmin) { - let data = res.data as AddAdminResponse; + } else if (op == UserOperation.AddAdmin) { + let data = wsJsonToRes<AddAdminResponse>(msg).data; this.state.siteRes.admins = data.admins; this.setState(this.state); - } else if (res.op == UserOperation.Search) { - let data = res.data as SearchResponse; + } else if (op == UserOperation.Search) { + let data = wsJsonToRes<SearchResponse>(msg).data; this.state.crossPosts = data.posts.filter( - p => p.id != Number(this.props.match.params.id) + p => p.post.id != Number(this.props.match.params.id) ); - if (this.state.crossPosts.length) { - this.state.postRes.post.duplicates = this.state.crossPosts; - } this.setState(this.state); - } else if (res.op == UserOperation.TransferSite) { - let data = res.data as GetSiteResponse; + } else if (op == UserOperation.TransferSite) { + let data = wsJsonToRes<GetSiteResponse>(msg).data; this.state.siteRes = data; this.setState(this.state); - } else if (res.op == UserOperation.TransferCommunity) { - let data = res.data as GetCommunityResponse; - this.state.postRes.community = data.community; + } else if (op == UserOperation.TransferCommunity) { + let data = wsJsonToRes<GetCommunityResponse>(msg).data; + this.state.postRes.community_view = data.community_view; + this.state.postRes.post_view.community = data.community_view.community; this.state.postRes.moderators = data.moderators; this.setState(this.state); - } else if (res.op == UserOperation.ListCategories) { - let data = res.data as ListCategoriesResponse; + } else if (op == UserOperation.ListCategories) { + let data = wsJsonToRes<ListCategoriesResponse>(msg).data; this.state.categories = data.categories; this.setState(this.state); } diff --git a/src/shared/components/private-message-form.tsx b/src/shared/components/private-message-form.tsx index 6abc5f3..a49f6ed 100644 --- a/src/shared/components/private-message-form.tsx +++ b/src/shared/components/private-message-form.tsx @@ -2,15 +2,14 @@ import { Component, linkEvent } from 'inferno'; import { Prompt } from 'inferno-router'; import { Subscription } from 'rxjs'; import { - PrivateMessageForm as PrivateMessageFormI, - EditPrivateMessageForm, - PrivateMessage, + CreatePrivateMessage, + EditPrivateMessage, + PrivateMessageView, PrivateMessageResponse, - UserView, + UserSafe, UserOperation, - WebSocketJsonResponse, } from 'lemmy-js-client'; -import { WebSocketService } from '../services'; +import { UserService, WebSocketService } from '../services'; import { capitalizeFirstLetter, wsJsonToRes, @@ -18,6 +17,7 @@ import { setupTippy, wsSubscribe, isBrowser, + wsUserOp, } from '../utils'; import { UserListing } from './user-listing'; import { MarkdownTextArea } from './markdown-textarea'; @@ -25,15 +25,15 @@ import { i18n } from '../i18next'; import { T } from 'inferno-i18next'; interface PrivateMessageFormProps { - recipient: UserView; - privateMessage?: PrivateMessage; // If a pm is given, that means this is an edit + recipient: UserSafe; + privateMessage?: PrivateMessageView; // If a pm is given, that means this is an edit onCancel?(): any; - onCreate?(message: PrivateMessage): any; - onEdit?(message: PrivateMessage): any; + onCreate?(message: PrivateMessageView): any; + onEdit?(message: PrivateMessageView): any; } interface PrivateMessageFormState { - privateMessageForm: PrivateMessageFormI; + privateMessageForm: CreatePrivateMessage; loading: boolean; previewMode: boolean; showDisclaimer: boolean; @@ -48,6 +48,7 @@ export class PrivateMessageForm extends Component< privateMessageForm: { content: null, recipient_id: this.props.recipient.id, + auth: UserService.Instance.authField(), }, loading: false, previewMode: false, @@ -64,11 +65,9 @@ export class PrivateMessageForm extends Component< this.parseMessage = this.parseMessage.bind(this); this.subscription = wsSubscribe(this.parseMessage); + // Its an edit if (this.props.privateMessage) { - this.state.privateMessageForm = { - content: this.props.privateMessage.content, - recipient_id: this.props.privateMessage.recipient_id, - }; + this.state.privateMessageForm.content = this.props.privateMessage.private_message.content; } } @@ -106,16 +105,7 @@ export class PrivateMessageForm extends Component< </label> <div class="col-sm-10 form-control-plaintext"> - <UserListing - user={{ - name: this.props.recipient.name, - preferred_username: this.props.recipient.preferred_username, - avatar: this.props.recipient.avatar, - id: this.props.recipient.id, - local: this.props.recipient.local, - actor_id: this.props.recipient.actor_id, - }} - /> + <UserListing user={this.props.recipient} /> </div> </div> )} @@ -198,13 +188,14 @@ export class PrivateMessageForm extends Component< handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) { event.preventDefault(); if (i.props.privateMessage) { - let editForm: EditPrivateMessageForm = { - edit_id: i.props.privateMessage.id, + let form: EditPrivateMessage = { + edit_id: i.props.privateMessage.private_message.id, content: i.state.privateMessageForm.content, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.editPrivateMessage(editForm); + WebSocketService.Instance.client.editPrivateMessage(form); } else { - WebSocketService.Instance.createPrivateMessage( + WebSocketService.Instance.client.createPrivateMessage( i.state.privateMessageForm ); } @@ -232,25 +223,25 @@ export class PrivateMessageForm extends Component< i.setState(i.state); } - parseMessage(msg: WebSocketJsonResponse) { - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); this.state.loading = false; this.setState(this.state); return; } else if ( - res.op == UserOperation.EditPrivateMessage || - res.op == UserOperation.DeletePrivateMessage || - res.op == UserOperation.MarkPrivateMessageAsRead + op == UserOperation.EditPrivateMessage || + op == UserOperation.DeletePrivateMessage || + op == UserOperation.MarkPrivateMessageAsRead ) { - let data = res.data as PrivateMessageResponse; + let data = wsJsonToRes<PrivateMessageResponse>(msg).data; this.state.loading = false; - this.props.onEdit(data.message); - } else if (res.op == UserOperation.CreatePrivateMessage) { - let data = res.data as PrivateMessageResponse; + this.props.onEdit(data.private_message_view); + } else if (op == UserOperation.CreatePrivateMessage) { + let data = wsJsonToRes<PrivateMessageResponse>(msg).data; this.state.loading = false; - this.props.onCreate(data.message); + this.props.onCreate(data.private_message_view); this.setState(this.state); } } diff --git a/src/shared/components/private-message.tsx b/src/shared/components/private-message.tsx index a0e86a7..ffad47f 100644 --- a/src/shared/components/private-message.tsx +++ b/src/shared/components/private-message.tsx @@ -1,9 +1,9 @@ import { Component, linkEvent } from 'inferno'; import { - PrivateMessage as PrivateMessageI, - DeletePrivateMessageForm, - MarkPrivateMessageAsReadForm, - UserView, + PrivateMessageView, + DeletePrivateMessage, + MarkPrivateMessageAsRead, + UserSafe, } from 'lemmy-js-client'; import { WebSocketService, UserService } from '../services'; import { mdToHtml, toast } from '../utils'; @@ -20,7 +20,7 @@ interface PrivateMessageState { } interface PrivateMessageProps { - privateMessage: PrivateMessageI; + private_message_view: PrivateMessageView; } export class PrivateMessage extends Component< @@ -48,41 +48,16 @@ export class PrivateMessage extends Component< get mine(): boolean { return ( UserService.Instance.user && - UserService.Instance.user.id == this.props.privateMessage.creator_id + UserService.Instance.user.id == this.props.private_message_view.creator.id ); } render() { - let message = this.props.privateMessage; - let userOther: UserView = this.mine - ? { - name: message.recipient_name, - preferred_username: message.recipient_preferred_username, - id: message.recipient_id, - avatar: message.recipient_avatar, - local: message.recipient_local, - actor_id: message.recipient_actor_id, - published: message.published, - number_of_posts: 0, - post_score: 0, - number_of_comments: 0, - comment_score: 0, - banned: false, - } - : { - name: message.creator_name, - preferred_username: message.creator_preferred_username, - id: message.creator_id, - avatar: message.creator_avatar, - local: message.creator_local, - actor_id: message.creator_actor_id, - published: message.published, - number_of_posts: 0, - post_score: 0, - number_of_comments: 0, - comment_score: 0, - banned: false, - }; + let message_view = this.props.private_message_view; + // TODO check this again + let userOther: UserSafe = this.mine + ? message_view.recipient + : message_view.creator; return ( <div class="border-top border-light"> @@ -97,7 +72,7 @@ export class PrivateMessage extends Component< </li> <li className="list-inline-item"> <span> - <MomentTime data={message} /> + <MomentTime data={message_view.private_message} /> </span> </li> <li className="list-inline-item"> @@ -120,7 +95,7 @@ export class PrivateMessage extends Component< {this.state.showEdit && ( <PrivateMessageForm recipient={userOther} - privateMessage={message} + privateMessage={message_view} onEdit={this.handlePrivateMessageEdit} onCreate={this.handlePrivateMessageCreate} onCancel={this.handleReplyCancel} @@ -144,14 +119,14 @@ export class PrivateMessage extends Component< class="btn btn-link btn-animate text-muted" onClick={linkEvent(this, this.handleMarkRead)} data-tippy-content={ - message.read + message_view.private_message.read ? i18n.t('mark_as_unread') : i18n.t('mark_as_read') } > <svg class={`icon icon-inline ${ - message.read && 'text-success' + message_view.private_message.read && 'text-success' }`} > <use xlinkHref="#icon-check"></use> @@ -189,14 +164,15 @@ export class PrivateMessage extends Component< class="btn btn-link btn-animate text-muted" onClick={linkEvent(this, this.handleDeleteClick)} data-tippy-content={ - !message.deleted + !message_view.private_message.deleted ? i18n.t('delete') : i18n.t('restore') } > <svg class={`icon icon-inline ${ - message.deleted && 'text-danger' + message_view.private_message.deleted && + 'text-danger' }`} > <use xlinkHref="#icon-trash"></use> @@ -237,7 +213,7 @@ export class PrivateMessage extends Component< } get messageUnlessRemoved(): string { - let message = this.props.privateMessage; + let message = this.props.private_message_view.private_message; return message.deleted ? `*${i18n.t('deleted')}*` : message.content; } @@ -252,11 +228,12 @@ export class PrivateMessage extends Component< } handleDeleteClick(i: PrivateMessage) { - let form: DeletePrivateMessageForm = { - edit_id: i.props.privateMessage.id, - deleted: !i.props.privateMessage.deleted, + let form: DeletePrivateMessage = { + edit_id: i.props.private_message_view.private_message.id, + deleted: !i.props.private_message_view.private_message.deleted, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.deletePrivateMessage(form); + WebSocketService.Instance.client.deletePrivateMessage(form); } handleReplyCancel() { @@ -266,11 +243,12 @@ export class PrivateMessage extends Component< } handleMarkRead(i: PrivateMessage) { - let form: MarkPrivateMessageAsReadForm = { - edit_id: i.props.privateMessage.id, - read: !i.props.privateMessage.read, + let form: MarkPrivateMessageAsRead = { + edit_id: i.props.private_message_view.private_message.id, + read: !i.props.private_message_view.private_message.read, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.markPrivateMessageAsRead(form); + WebSocketService.Instance.client.markPrivateMessageAsRead(form); } handleMessageCollapse(i: PrivateMessage) { @@ -288,10 +266,10 @@ export class PrivateMessage extends Component< this.setState(this.state); } - handlePrivateMessageCreate(message: PrivateMessageI) { + handlePrivateMessageCreate(message: PrivateMessageView) { if ( UserService.Instance.user && - message.creator_id == UserService.Instance.user.id + message.creator.id == UserService.Instance.user.id ) { this.state.showReply = false; this.setState(this.state); diff --git a/src/shared/components/search.tsx b/src/shared/components/search.tsx index 6796ad3..b4196f7 100644 --- a/src/shared/components/search.tsx +++ b/src/shared/components/search.tsx @@ -2,20 +2,19 @@ import { Component, linkEvent } from 'inferno'; import { Subscription } from 'rxjs'; import { UserOperation, - Post, - Comment, - Community, - UserView, + PostView, + CommentView, + CommunityView, + UserViewSafe, SortType, - SearchForm, + Search as SearchForm, SearchResponse, SearchType, PostResponse, CommentResponse, - WebSocketJsonResponse, Site, } from 'lemmy-js-client'; -import { WebSocketService } from '../services'; +import { UserService, WebSocketService } from '../services'; import { wsJsonToRes, fetchLimit, @@ -27,7 +26,7 @@ import { commentsToFlatNodes, setIsoData, wsSubscribe, - setAuth, + wsUserOp, } from '../utils'; import { PostListing } from './post-listing'; import { HtmlTags } from './html-tags'; @@ -80,7 +79,7 @@ export class Search extends Component<any, SearchState> { users: [], }, loading: false, - site: this.isoData.site.site, + site: this.isoData.site_res.site_view.site, }; static getSearchQueryFromProps(q: string): string { @@ -142,8 +141,8 @@ export class Search extends Component<any, SearchState> { sort: this.getSortTypeFromProps(pathSplit[7]), page: this.getPageFromProps(pathSplit[9]), limit: fetchLimit, + auth: req.auth, }; - setAuth(form, req.auth); if (form.q != '') { promises.push(req.client.search(form)); @@ -252,19 +251,24 @@ export class Search extends Component<any, SearchState> { all() { let combined: { type_: string; - data: Comment | Post | Community | UserView; + data: CommentView | PostView | CommunityView | UserViewSafe; + published: string; }[] = []; let comments = this.state.searchResponse.comments.map(e => { - return { type_: 'comments', data: e }; + return { type_: 'comments', data: e, published: e.comment.published }; }); let posts = this.state.searchResponse.posts.map(e => { - return { type_: 'posts', data: e }; + return { type_: 'posts', data: e, published: e.post.published }; }); let communities = this.state.searchResponse.communities.map(e => { - return { type_: 'communities', data: e }; + return { + type_: 'communities', + data: e, + published: e.community.published, + }; }); let users = this.state.searchResponse.users.map(e => { - return { type_: 'users', data: e }; + return { type_: 'users', data: e, published: e.user.published }; }); combined.push(...comments); @@ -274,16 +278,16 @@ export class Search extends Component<any, SearchState> { // Sort it if (this.state.sort == SortType.New) { - combined.sort((a, b) => b.data.published.localeCompare(a.data.published)); + combined.sort((a, b) => b.published.localeCompare(a.published)); } else { combined.sort( (a, b) => - ((b.data as Comment | Post).score | - (b.data as Community).number_of_subscribers | - (b.data as UserView).comment_score) - - ((a.data as Comment | Post).score | - (a.data as Community).number_of_subscribers | - (a.data as UserView).comment_score) + ((b.data as CommentView | PostView).counts.score | + (b.data as CommunityView).counts.subscribers | + (b.data as UserViewSafe).counts.comment_score) - + ((a.data as CommentView | PostView).counts.score | + (a.data as CommunityView).counts.subscribers | + (a.data as UserViewSafe).counts.comment_score) ); } @@ -294,8 +298,8 @@ export class Search extends Component<any, SearchState> { <div class="col-12"> {i.type_ == 'posts' && ( <PostListing - key={(i.data as Post).id} - post={i.data as Post} + key={(i.data as PostView).post.id} + post_view={i.data as PostView} showCommunity enableDownvotes={this.state.site.enable_downvotes} enableNsfw={this.state.site.enable_nsfw} @@ -303,18 +307,18 @@ export class Search extends Component<any, SearchState> { )} {i.type_ == 'comments' && ( <CommentNodes - key={(i.data as Comment).id} - nodes={[{ comment: i.data as Comment }]} + key={(i.data as CommentView).comment.id} + nodes={[{ comment_view: i.data as CommentView }]} locked noIndent enableDownvotes={this.state.site.enable_downvotes} /> )} {i.type_ == 'communities' && ( - <div>{this.communityListing(i.data as Community)}</div> + <div>{this.communityListing(i.data as CommunityView)}</div> )} {i.type_ == 'users' && ( - <div>{this.userListing(i.data as UserView)}</div> + <div>{this.userListing(i.data as UserViewSafe)}</div> )} </div> </div> @@ -341,7 +345,7 @@ export class Search extends Component<any, SearchState> { <div class="row"> <div class="col-12"> <PostListing - post={post} + post_view={post} showCommunity enableDownvotes={this.state.site.enable_downvotes} enableNsfw={this.state.site.enable_nsfw} @@ -365,28 +369,28 @@ export class Search extends Component<any, SearchState> { ); } - communityListing(community: Community) { + communityListing(community_view: CommunityView) { return ( <> <span> - <CommunityLink community={community} /> + <CommunityLink community={community_view.community} /> </span> - <span>{` - ${community.title} - + <span>{` - ${community_view.community.title} - ${i18n.t('number_of_subscribers', { - count: community.number_of_subscribers, + count: community_view.counts.subscribers, })} `}</span> </> ); } - userListing(user: UserView) { + userListing(user_view: UserViewSafe) { return [ <span> - <UserListing user={user} showApubName /> + <UserListing user={user_view.user} showApubName /> </span>, <span>{` - ${i18n.t('number_of_comments', { - count: user.number_of_comments, + count: user_view.counts.comment_count, })}`}</span>, ]; } @@ -452,10 +456,11 @@ export class Search extends Component<any, SearchState> { sort: this.state.sort, page: this.state.page, limit: fetchLimit, + auth: UserService.Instance.authField(false), }; if (this.state.q != '') { - WebSocketService.Instance.search(form); + WebSocketService.Instance.client.search(form); } } @@ -495,25 +500,28 @@ export class Search extends Component<any, SearchState> { ); } - parseMessage(msg: WebSocketJsonResponse) { + parseMessage(msg: any) { console.log(msg); - let res = wsJsonToRes(msg); + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); return; - } else if (res.op == UserOperation.Search) { - let data = res.data as SearchResponse; + } else if (op == UserOperation.Search) { + let data = wsJsonToRes<SearchResponse>(msg).data; this.state.searchResponse = data; this.state.loading = false; window.scrollTo(0, 0); this.setState(this.state); - } else if (res.op == UserOperation.CreateCommentLike) { - let data = res.data as CommentResponse; - createCommentLikeRes(data, this.state.searchResponse.comments); + } else if (op == UserOperation.CreateCommentLike) { + let data = wsJsonToRes<CommentResponse>(msg).data; + createCommentLikeRes( + data.comment_view, + this.state.searchResponse.comments + ); this.setState(this.state); - } else if (res.op == UserOperation.CreatePostLike) { - let data = res.data as PostResponse; - createPostLikeFindRes(data, this.state.searchResponse.posts); + } else if (op == UserOperation.CreatePostLike) { + let data = wsJsonToRes<PostResponse>(msg).data; + createPostLikeFindRes(data.post_view, this.state.searchResponse.posts); this.setState(this.state); } } diff --git a/src/shared/components/setup.tsx b/src/shared/components/setup.tsx index 33fa443..c139988 100644 --- a/src/shared/components/setup.tsx +++ b/src/shared/components/setup.tsx @@ -2,19 +2,14 @@ import { Component, linkEvent } from 'inferno'; import { Helmet } from 'inferno-helmet'; import { Subscription } from 'rxjs'; import { retryWhen, delay, take } from 'rxjs/operators'; -import { - RegisterForm, - LoginResponse, - UserOperation, - WebSocketJsonResponse, -} from 'lemmy-js-client'; +import { Register, LoginResponse, UserOperation } from 'lemmy-js-client'; import { WebSocketService, UserService } from '../services'; -import { wsJsonToRes, toast } from '../utils'; +import { wsUserOp, wsJsonToRes, toast } from '../utils'; import { SiteForm } from './site-form'; import { i18n } from '../i18next'; interface State { - userForm: RegisterForm; + userForm: Register; doneRegisteringUser: boolean; userLoading: boolean; } @@ -168,7 +163,7 @@ export class Setup extends Component<any, State> { i.state.userLoading = true; i.setState(i.state); event.preventDefault(); - WebSocketService.Instance.register(i.state.userForm); + WebSocketService.Instance.client.register(i.state.userForm); } handleRegisterUsernameChange(i: Setup, event: any) { @@ -191,20 +186,20 @@ export class Setup extends Component<any, State> { i.setState(i.state); } - parseMessage(msg: WebSocketJsonResponse) { - let res = wsJsonToRes(msg); + parseMessage(msg: any) { + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); this.state.userLoading = false; this.setState(this.state); return; - } else if (res.op == UserOperation.Register) { - let data = res.data as LoginResponse; + } else if (op == UserOperation.Register) { + let data = wsJsonToRes<LoginResponse>(msg).data; this.state.userLoading = false; this.state.doneRegisteringUser = true; UserService.Instance.login(data); this.setState(this.state); - } else if (res.op == UserOperation.CreateSite) { + } else if (op == UserOperation.CreateSite) { window.location.href = '/'; } } diff --git a/src/shared/components/sidebar.tsx b/src/shared/components/sidebar.tsx index 82b05af..0934535 100644 --- a/src/shared/components/sidebar.tsx +++ b/src/shared/components/sidebar.tsx @@ -1,13 +1,13 @@ import { Component, linkEvent } from 'inferno'; import { Link } from 'inferno-router'; import { - Community, - CommunityUser, - FollowCommunityForm, - DeleteCommunityForm, - RemoveCommunityForm, - UserView, - AddModToCommunityForm, + CommunityView, + CommunityModeratorView, + FollowCommunity, + DeleteCommunity, + RemoveCommunity, + UserViewSafe, + AddModToCommunity, Category, } from 'lemmy-js-client'; import { WebSocketService, UserService } from '../services'; @@ -19,10 +19,10 @@ import { BannerIconHeader } from './banner-icon-header'; import { i18n } from '../i18next'; interface SidebarProps { - community: Community; + community_view: CommunityView; categories: Category[]; - moderators: CommunityUser[]; - admins: UserView[]; + moderators: CommunityModeratorView[]; + admins: UserViewSafe[]; online: number; enableNsfw: boolean; showIcon?: boolean; @@ -60,7 +60,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { ) : ( <CommunityForm categories={this.props.categories} - community={this.props.community} + community_view={this.props.community_view} onEdit={this.handleEditCommunity} onCancel={this.handleEditCancel} enableNsfw={this.props.enableNsfw} @@ -93,7 +93,8 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { } communityTitle() { - let community = this.props.community; + let community = this.props.community_view.community; + let subscribed = this.props.community_view.subscribed; return ( <div> <h5 className="mb-0"> @@ -101,7 +102,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { <BannerIconHeader icon={community.icon} banner={community.banner} /> )} <span class="mr-2">{community.title}</span> - {community.subscribed && ( + {subscribed && ( <a class="btn btn-secondary btn-sm mr-2" href="#" @@ -141,7 +142,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { } badges() { - let community = this.props.community; + let community_view = this.props.community_view; return ( <ul class="my-1 list-inline"> <li className="list-inline-item badge badge-secondary"> @@ -149,28 +150,28 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { </li> <li className="list-inline-item badge badge-secondary"> {i18n.t('number_of_subscribers', { - count: community.number_of_subscribers, + count: community_view.counts.subscribers, })} </li> <li className="list-inline-item badge badge-secondary"> {i18n.t('number_of_posts', { - count: community.number_of_posts, + count: community_view.counts.posts, })} </li> <li className="list-inline-item badge badge-secondary"> {i18n.t('number_of_comments', { - count: community.number_of_comments, + count: community_view.counts.comments, })} </li> <li className="list-inline-item"> <Link className="badge badge-secondary" to="/communities"> - {community.category_name} + {community_view.category.name} </Link> </li> <li className="list-inline-item"> <Link className="badge badge-secondary" - to={`/modlog/community/${this.props.community.id}`} + to={`/modlog/community/${this.props.community_view.community.id}`} > {i18n.t('modlog')} </Link> @@ -185,16 +186,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { <li class="list-inline-item">{i18n.t('mods')}: </li> {this.props.moderators.map(mod => ( <li class="list-inline-item"> - <UserListing - user={{ - name: mod.user_name, - preferred_username: mod.user_preferred_username, - avatar: mod.avatar, - id: mod.user_id, - local: mod.user_local, - actor_id: mod.user_actor_id, - }} - /> + <UserListing user={mod.moderator} /> </li> ))} </ul> @@ -202,14 +194,16 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { } createPost() { - let community = this.props.community; + let community_view = this.props.community_view; return ( - community.subscribed && ( + community_view.subscribed && ( <Link className={`btn btn-secondary btn-block mb-2 ${ - community.deleted || community.removed ? 'no-click' : '' + community_view.community.deleted || community_view.community.removed + ? 'no-click' + : '' }`} - to={`/create_post?community_id=${community.id}`} + to={`/create_post?community_id=${community_view.community.id}`} > {i18n.t('create_a_post')} </Link> @@ -218,14 +212,17 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { } subscribe() { - let community = this.props.community; + let community_view = this.props.community_view; return ( <div class="mb-2"> - {!community.subscribed && ( + {!community_view.subscribed && ( <a class="btn btn-secondary btn-block" href="#" - onClick={linkEvent(community.id, this.handleSubscribe)} + onClick={linkEvent( + community_view.community.id, + this.handleSubscribe + )} > {i18n.t('subscribe')} </a> @@ -235,19 +232,19 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { } description() { - let community = this.props.community; + let description = this.props.community_view.community.description; return ( - community.description && ( + description && ( <div className="md-div" - dangerouslySetInnerHTML={mdToHtml(community.description)} + dangerouslySetInnerHTML={mdToHtml(description)} /> ) ); } adminButtons() { - let community = this.props.community; + let community_view = this.props.community_view; return ( <> <ul class="list-inline mb-1 text-muted font-weight-bold"> @@ -309,12 +306,14 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { class="pointer" onClick={linkEvent(this, this.handleDeleteClick)} data-tippy-content={ - !community.deleted ? i18n.t('delete') : i18n.t('restore') + !community_view.community.deleted + ? i18n.t('delete') + : i18n.t('restore') } > <svg class={`icon icon-inline ${ - community.deleted && 'text-danger' + community_view.community.deleted && 'text-danger' }`} > <use xlinkHref="#icon-trash"></use> @@ -326,7 +325,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { )} {this.canAdmin && ( <li className="list-inline-item"> - {!this.props.community.removed ? ( + {!this.props.community_view.community.removed ? ( <span class="pointer" onClick={linkEvent(this, this.handleModRemoveShow)} @@ -392,11 +391,12 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { handleDeleteClick(i: Sidebar, event: any) { event.preventDefault(); - let deleteForm: DeleteCommunityForm = { - edit_id: i.props.community.id, - deleted: !i.props.community.deleted, + let deleteForm: DeleteCommunity = { + edit_id: i.props.community_view.community.id, + deleted: !i.props.community_view.community.deleted, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.deleteCommunity(deleteForm); + WebSocketService.Instance.client.deleteCommunity(deleteForm); } handleShowConfirmLeaveModTeamClick(i: Sidebar) { @@ -405,12 +405,13 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { } handleLeaveModTeamClick(i: Sidebar) { - let form: AddModToCommunityForm = { + let form: AddModToCommunity = { user_id: UserService.Instance.user.id, - community_id: i.props.community.id, + community_id: i.props.community_view.community.id, added: false, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.addModToCommunity(form); + WebSocketService.Instance.client.addModToCommunity(form); i.state.showConfirmLeaveModTeam = false; i.setState(i.state); } @@ -422,31 +423,33 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { handleUnsubscribe(communityId: number, event: any) { event.preventDefault(); - let form: FollowCommunityForm = { + let form: FollowCommunity = { community_id: communityId, follow: false, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.followCommunity(form); + WebSocketService.Instance.client.followCommunity(form); } handleSubscribe(communityId: number, event: any) { event.preventDefault(); - let form: FollowCommunityForm = { + let form: FollowCommunity = { community_id: communityId, follow: true, + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.followCommunity(form); + WebSocketService.Instance.client.followCommunity(form); } private get amCreator(): boolean { - return this.props.community.creator_id == UserService.Instance.user.id; + return this.props.community_view.creator.id == UserService.Instance.user.id; } get canMod(): boolean { return ( UserService.Instance.user && this.props.moderators - .map(m => m.user_id) + .map(m => m.moderator.id) .includes(UserService.Instance.user.id) ); } @@ -454,7 +457,9 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { get canAdmin(): boolean { return ( UserService.Instance.user && - this.props.admins.map(a => a.id).includes(UserService.Instance.user.id) + this.props.admins + .map(a => a.user.id) + .includes(UserService.Instance.user.id) ); } @@ -476,13 +481,14 @@ export class Sidebar extends Component<SidebarProps, SidebarState> { handleModRemoveSubmit(i: Sidebar, event: any) { event.preventDefault(); - let removeForm: RemoveCommunityForm = { - edit_id: i.props.community.id, - removed: !i.props.community.removed, + let removeForm: RemoveCommunity = { + edit_id: i.props.community_view.community.id, + removed: !i.props.community_view.community.removed, reason: i.state.removeReason, expires: getUnixTime(i.state.removeExpires), + auth: UserService.Instance.authField(), }; - WebSocketService.Instance.removeCommunity(removeForm); + WebSocketService.Instance.client.removeCommunity(removeForm); i.state.showRemoveDialog = false; i.setState(i.state); diff --git a/src/shared/components/site-form.tsx b/src/shared/components/site-form.tsx index 9b572f5..6547edd 100644 --- a/src/shared/components/site-form.tsx +++ b/src/shared/components/site-form.tsx @@ -2,8 +2,8 @@ import { Component, linkEvent } from 'inferno'; import { Prompt } from 'inferno-router'; import { MarkdownTextArea } from './markdown-textarea'; import { ImageUploadForm } from './image-upload-form'; -import { Site, SiteForm as SiteFormI } from 'lemmy-js-client'; -import { WebSocketService } from '../services'; +import { Site, EditSite } from 'lemmy-js-client'; +import { UserService, WebSocketService } from '../services'; import { capitalizeFirstLetter, randomStr } from '../utils'; import { i18n } from '../i18next'; @@ -13,7 +13,7 @@ interface SiteFormProps { } interface SiteFormState { - siteForm: SiteFormI; + siteForm: EditSite; loading: boolean; } @@ -27,6 +27,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> { name: null, icon: null, banner: null, + auth: UserService.Instance.authField(), }, loading: false, }; @@ -54,6 +55,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> { enable_nsfw: this.props.site.enable_nsfw, icon: this.props.site.icon, banner: this.props.site.banner, + auth: UserService.Instance.authField(), }; } } @@ -242,9 +244,9 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> { event.preventDefault(); i.state.loading = true; if (i.props.site) { - WebSocketService.Instance.editSite(i.state.siteForm); + WebSocketService.Instance.client.editSite(i.state.siteForm); } else { - WebSocketService.Instance.createSite(i.state.siteForm); + WebSocketService.Instance.client.createSite(i.state.siteForm); } i.setState(i.state); } diff --git a/src/shared/components/theme.tsx b/src/shared/components/theme.tsx index 5dfc01c..8da4ee1 100644 --- a/src/shared/components/theme.tsx +++ b/src/shared/components/theme.tsx @@ -1,9 +1,9 @@ -import { User } from 'lemmy-js-client'; +import { User_ } from 'lemmy-js-client'; import { Helmet } from 'inferno-helmet'; import { Component } from 'inferno'; interface Props { - user: User | undefined; + user: User_ | undefined; } export class Theme extends Component<Props> { diff --git a/src/shared/components/user-details.tsx b/src/shared/components/user-details.tsx index 01c9aed..f128158 100644 --- a/src/shared/components/user-details.tsx +++ b/src/shared/components/user-details.tsx @@ -1,13 +1,20 @@ import { Component, linkEvent } from 'inferno'; import { i18n } from '../i18next'; -import { Post, Comment, SortType, UserDetailsResponse } from 'lemmy-js-client'; +import { + PostView, + CommentView, + SortType, + GetUserDetailsResponse, + UserViewSafe, +} from 'lemmy-js-client'; import { UserDetailsView } from '../interfaces'; import { commentsToFlatNodes, setupTippy } from '../utils'; import { PostListing } from './post-listing'; import { CommentNodes } from './comment-nodes'; interface UserDetailsProps { - userRes: UserDetailsResponse; + userRes: GetUserDetailsResponse; + admins: UserViewSafe[]; page: number; limit: number; sort: SortType; @@ -19,6 +26,18 @@ interface UserDetailsProps { interface UserDetailsState {} +enum ItemEnum { + Comment, + Post, +} +type ItemType = { + id: number; + type_: ItemEnum; + view: CommentView | PostView; + published: string; + score: number; +}; + export class UserDetails extends Component<UserDetailsProps, UserDetailsState> { constructor(props: any, context: any) { super(props, context); @@ -60,56 +79,68 @@ export class UserDetails extends Component<UserDetailsProps, UserDetailsState> { } } + renderItemType(i: ItemType) { + switch (i.type_) { + case ItemEnum.Comment: + let c = i.view as CommentView; + return ( + <CommentNodes + key={i.id} + nodes={[{ comment_view: c }]} + admins={this.props.admins} + noBorder + noIndent + showCommunity + showContext + enableDownvotes={this.props.enableDownvotes} + /> + ); + case ItemEnum.Post: + let p = i.view as PostView; + return ( + <PostListing + key={i.id} + post_view={p} + admins={this.props.admins} + showCommunity + enableDownvotes={this.props.enableDownvotes} + enableNsfw={this.props.enableNsfw} + /> + ); + default: + return <div />; + } + } + overview() { - const comments = this.props.userRes.comments.map((c: Comment) => { - return { type: 'comments', data: c }; - }); - const posts = this.props.userRes.posts.map((p: Post) => { - return { type: 'posts', data: p }; - }); - - const combined: { type: string; data: Comment | Post }[] = [ - ...comments, - ...posts, - ]; + let id = 0; + let comments: ItemType[] = this.props.userRes.comments.map(r => ({ + id: id++, + type_: ItemEnum.Comment, + view: r, + published: r.comment.published, + score: r.counts.score, + })); + let posts: ItemType[] = this.props.userRes.posts.map(r => ({ + id: id++, + type_: ItemEnum.Comment, + view: r, + published: r.post.published, + score: r.counts.score, + })); + + let combined = [...comments, ...posts]; // Sort it if (this.props.sort === SortType.New) { - combined.sort((a, b) => b.data.published.localeCompare(a.data.published)); + combined.sort((a, b) => b.published.localeCompare(a.published)); } else { - combined.sort((a, b) => b.data.score - a.data.score); + combined.sort((a, b) => b.score - a.score); } return ( <div> - {combined.map(i => ( - <> - <div> - {i.type === 'posts' ? ( - <PostListing - key={(i.data as Post).id} - post={i.data as Post} - admins={this.props.userRes.admins} - showCommunity - enableDownvotes={this.props.enableDownvotes} - enableNsfw={this.props.enableNsfw} - /> - ) : ( - <CommentNodes - key={(i.data as Comment).id} - nodes={[{ comment: i.data as Comment }]} - admins={this.props.userRes.admins} - noBorder - noIndent - showCommunity - showContext - enableDownvotes={this.props.enableDownvotes} - /> - )} - </div> - <hr class="my-3" /> - </> - ))} + {combined.map(i => [this.renderItemType(i), <hr class="my-3" />])} </div> ); } @@ -119,7 +150,7 @@ export class UserDetails extends Component<UserDetailsProps, UserDetailsState> { <div> <CommentNodes nodes={commentsToFlatNodes(this.props.userRes.comments)} - admins={this.props.userRes.admins} + admins={this.props.admins} noIndent showCommunity showContext @@ -135,8 +166,8 @@ export class UserDetails extends Component<UserDetailsProps, UserDetailsState> { {this.props.userRes.posts.map(post => ( <> <PostListing - post={post} - admins={this.props.userRes.admins} + post_view={post} + admins={this.props.admins} showCommunity enableDownvotes={this.props.enableDownvotes} enableNsfw={this.props.enableNsfw} diff --git a/src/shared/components/user-listing.tsx b/src/shared/components/user-listing.tsx index a8e4025..a20785a 100644 --- a/src/shared/components/user-listing.tsx +++ b/src/shared/components/user-listing.tsx @@ -1,22 +1,12 @@ import { Component } from 'inferno'; import { Link } from 'inferno-router'; -import { UserView } from 'lemmy-js-client'; +import { UserSafe } from 'lemmy-js-client'; import { showAvatars, hostname, isCakeDay } from '../utils'; import { CakeDay } from './cake-day'; import { PictrsImage } from './pictrs-image'; -export interface UserOther { - name: string; - preferred_username?: string; - id?: number; // Necessary if its federated - avatar?: string; - local?: boolean; - actor_id?: string; - published?: string; -} - interface UserListingProps { - user: UserView | UserOther; + user: UserSafe; realLink?: boolean; useApubName?: boolean; muted?: boolean; diff --git a/src/shared/components/user.tsx b/src/shared/components/user.tsx index b03d9dd..814e789 100644 --- a/src/shared/components/user.tsx +++ b/src/shared/components/user.tsx @@ -5,14 +5,13 @@ import { UserOperation, SortType, ListingType, - UserSettingsForm, + SaveUserSettings, LoginResponse, - DeleteAccountForm, - WebSocketJsonResponse, + DeleteAccount, GetSiteResponse, - UserDetailsResponse, + GetUserDetailsResponse, AddAdminResponse, - GetUserDetailsForm, + GetUserDetails, CommentResponse, PostResponse, BanUserResponse, @@ -40,9 +39,9 @@ import { editCommentRes, saveCommentRes, createPostLikeFindRes, - setAuth, previewLines, editPostFindRes, + wsUserOp, } from '../utils'; import { UserListing } from './user-listing'; import { HtmlTags } from './html-tags'; @@ -58,18 +57,18 @@ import { BannerIconHeader } from './banner-icon-header'; import { CommunityLink } from './community-link'; interface UserState { - userRes: UserDetailsResponse; + userRes: GetUserDetailsResponse; userId: number; userName: string; view: UserDetailsView; sort: SortType; page: number; loading: boolean; - userSettingsForm: UserSettingsForm; + userSettingsForm: SaveUserSettings; userSettingsLoading: boolean; deleteAccountLoading: boolean; deleteAccountShowConfirm: boolean; - deleteAccountForm: DeleteAccountForm; + deleteAccountForm: DeleteAccount; siteRes: GetSiteResponse; } @@ -106,17 +105,18 @@ export class User extends Component<any, UserState> { lang: null, show_avatars: null, send_notifications_to_email: null, - auth: null, bio: null, preferred_username: null, + auth: UserService.Instance.authField(), }, userSettingsLoading: null, deleteAccountLoading: null, deleteAccountShowConfirm: false, deleteAccountForm: { password: null, + auth: UserService.Instance.authField(), }, - siteRes: this.isoData.site, + siteRes: this.isoData.site_res, }; constructor(props: any, context: any) { @@ -157,21 +157,22 @@ export class User extends Component<any, UserState> { } fetchUserData() { - let form: GetUserDetailsForm = { + let form: GetUserDetails = { user_id: this.state.userId, username: this.state.userName, sort: this.state.sort, saved_only: this.state.view === UserDetailsView.Saved, page: this.state.page, limit: fetchLimit, + auth: UserService.Instance.authField(false), }; - WebSocketService.Instance.getUserDetails(form); + WebSocketService.Instance.client.getUserDetails(form); } get isCurrentUser() { return ( UserService.Instance.user && - UserService.Instance.user.id == this.state.userRes.user.id + UserService.Instance.user.id == this.state.userRes.user_view.user.id ); } @@ -205,14 +206,14 @@ export class User extends Component<any, UserState> { let sort = this.getSortTypeFromProps(pathSplit[6]); let page = this.getPageFromProps(Number(pathSplit[8])); - let form: GetUserDetailsForm = { + let form: GetUserDetails = { sort, saved_only: view === UserDetailsView.Saved, page, limit: fetchLimit, + auth: req.auth, }; this.setIdOrName(form, user_id, username); - setAuth(form, req.auth); promises.push(req.client.getUserDetails(form)); return promises; } @@ -251,12 +252,12 @@ export class User extends Component<any, UserState> { } get documentTitle(): string { - return `@${this.state.userRes.user.name} - ${this.state.siteRes.site.name}`; + return `@${this.state.userRes.user_view.user.name} - ${this.state.siteRes.site_view.site.name}`; } get bioTag(): string { - return this.state.userRes.user.bio - ? previewLines(this.state.userRes.user.bio) + return this.state.userRes.user_view.user.bio + ? previewLines(this.state.userRes.user_view.user.bio) : undefined; } @@ -277,7 +278,7 @@ export class User extends Component<any, UserState> { title={this.documentTitle} path={this.context.router.route.match.url} description={this.bioTag} - image={this.state.userRes.user.avatar} + image={this.state.userRes.user_view.user.avatar} /> {this.userInfo()} <hr /> @@ -285,11 +286,14 @@ export class User extends Component<any, UserState> { {!this.state.loading && this.selects()} <UserDetails userRes={this.state.userRes} + admins={this.state.siteRes.admins} sort={this.state.sort} page={this.state.page} limit={fetchLimit} - enableDownvotes={this.state.siteRes.site.enable_downvotes} - enableNsfw={this.state.siteRes.site.enable_nsfw} + enableDownvotes={ + this.state.siteRes.site_view.site.enable_downvotes + } + enableNsfw={this.state.siteRes.site_view.site.enable_nsfw} view={this.state.view} onPageChange={this.handlePageChange} /> @@ -391,29 +395,29 @@ export class User extends Component<any, UserState> { } userInfo() { - let user = this.state.userRes.user; + let uv = this.state.userRes.user_view; return ( <div> - <BannerIconHeader banner={user.banner} icon={user.avatar} /> + <BannerIconHeader banner={uv.user.banner} icon={uv.user.avatar} /> <div class="mb-3"> <div class=""> <div class="mb-0 d-flex flex-wrap"> <div> - {user.preferred_username && ( - <h5 class="mb-0">{user.preferred_username}</h5> + {uv.user.preferred_username && ( + <h5 class="mb-0">{uv.user.preferred_username}</h5> )} <ul class="list-inline mb-2"> <li className="list-inline-item"> <UserListing - user={user} + user={uv.user} realLink useApubName muted hideAvatar /> </li> - {user.banned && ( + {uv.user.banned && ( <li className="list-inline-item badge badge-danger"> {i18n.t('banned')} </li> @@ -432,45 +436,45 @@ export class User extends Component<any, UserState> { <> <a className={`d-flex align-self-start btn btn-secondary mr-2 ${ - !user.matrix_user_id && 'invisible' + !uv.user.matrix_user_id && 'invisible' }`} target="_blank" rel="noopener" - href={`https://matrix.to/#/${user.matrix_user_id}`} + href={`https://matrix.to/#/${uv.user.matrix_user_id}`} > {i18n.t('send_secure_message')} </a> <Link className={'d-flex align-self-start btn btn-secondary'} - to={`/create_private_message/recipient/${user.id}`} + to={`/create_private_message/recipient/${uv.user.id}`} > {i18n.t('send_message')} </Link> </> )} </div> - {user.bio && ( + {uv.user.bio && ( <div className="d-flex align-items-center mb-2"> <div className="md-div" - dangerouslySetInnerHTML={mdToHtml(user.bio)} + dangerouslySetInnerHTML={mdToHtml(uv.user.bio)} /> </div> )} <div> <ul class="list-inline mb-2"> <li className="list-inline-item badge badge-light"> - {i18n.t('number_of_posts', { count: user.number_of_posts })} + {i18n.t('number_of_posts', { count: uv.counts.post_count })} </li> <li className="list-inline-item badge badge-light"> {i18n.t('number_of_comments', { - count: user.number_of_comments, + count: uv.counts.comment_count, })} </li> </ul> </div> <div class="text-muted"> - {i18n.t('joined')} <MomentTime data={user} showAgo /> + {i18n.t('joined')} <MomentTime data={uv.user} showAgo /> </div> <div className="d-flex align-items-center text-muted mb-2"> <svg class="icon"> @@ -478,7 +482,7 @@ export class User extends Component<any, UserState> { </svg> <span className="ml-2"> {i18n.t('cake_day_title')}{' '} - {moment.utc(user.published).local().format('MMM DD, YYYY')} + {moment.utc(uv.user.published).local().format('MMM DD, YYYY')} </span> </div> </div> @@ -704,7 +708,7 @@ export class User extends Component<any, UserState> { /> </div> </div> - {this.state.siteRes.site.enable_nsfw && ( + {this.state.siteRes.site_view.site.enable_nsfw && ( <div class="form-group"> <div class="form-check"> <input @@ -840,17 +844,9 @@ export class User extends Component<any, UserState> { <div class="card-body"> <h5>{i18n.t('moderates')}</h5> <ul class="list-unstyled mb-0"> - {this.state.userRes.moderates.map(community => ( + {this.state.userRes.moderates.map(cmv => ( <li> - <CommunityLink - community={{ - name: community.community_name, - id: community.community_id, - local: community.community_local, - actor_id: community.community_actor_id, - icon: community.community_icon, - }} - /> + <CommunityLink community={cmv.community} /> </li> ))} </ul> @@ -869,17 +865,9 @@ export class User extends Component<any, UserState> { <div class="card-body"> <h5>{i18n.t('subscribed')}</h5> <ul class="list-unstyled mb-0"> - {this.state.userRes.follows.map(community => ( + {this.state.userRes.follows.map(cfv => ( <li> - <CommunityLink - community={{ - name: community.community_name, - id: community.community_id, - local: community.community_local, - actor_id: community.community_actor_id, - icon: community.community_icon, - }} - /> + <CommunityLink community={cfv.community} /> </li> ))} </ul> @@ -946,16 +934,12 @@ export class User extends Component<any, UserState> { } handleUserSettingsSortTypeChange(val: SortType) { - this.state.userSettingsForm.default_sort_type = Object.keys( - SortType - ).indexOf(val); + this.state.userSettingsForm.default_sort_type = val; this.setState(this.state); } handleUserSettingsListingTypeChange(val: ListingType) { - this.state.userSettingsForm.default_listing_type = Object.keys( - ListingType - ).indexOf(val); + this.state.userSettingsForm.default_listing_type = val; this.setState(this.state); } @@ -998,7 +982,7 @@ export class User extends Component<any, UserState> { i.state.userSettingsForm.matrix_user_id = event.target.value; if ( i.state.userSettingsForm.matrix_user_id == '' && - !i.state.userRes.user.matrix_user_id + !i.state.userRes.user_view.user.matrix_user_id ) { i.state.userSettingsForm.matrix_user_id = undefined; } @@ -1034,7 +1018,7 @@ export class User extends Component<any, UserState> { i.state.userSettingsLoading = true; i.setState(i.state); - WebSocketService.Instance.saveUserSettings(i.state.userSettingsForm); + WebSocketService.Instance.client.saveUserSettings(i.state.userSettingsForm); } handleDeleteAccountShowConfirmToggle(i: User, event: any) { @@ -1058,7 +1042,7 @@ export class User extends Component<any, UserState> { i.state.deleteAccountLoading = true; i.setState(i.state); - WebSocketService.Instance.deleteAccount(i.state.deleteAccountForm); + WebSocketService.Instance.client.deleteAccount(i.state.deleteAccountForm); i.handleLogoutClick(i); } @@ -1089,9 +1073,9 @@ export class User extends Component<any, UserState> { } } - parseMessage(msg: WebSocketJsonResponse) { + parseMessage(msg: any) { console.log(msg); - const res = wsJsonToRes(msg); + let op = wsUserOp(msg); if (msg.error) { toast(i18n.t(msg.error), 'danger'); if (msg.error == 'couldnt_find_that_username_or_email') { @@ -1104,83 +1088,83 @@ export class User extends Component<any, UserState> { return; } else if (msg.reconnect) { this.fetchUserData(); - } else if (res.op == UserOperation.GetUserDetails) { + } else if (op == UserOperation.GetUserDetails) { // Since the UserDetails contains posts/comments as well as some general user info we listen here as well // and set the parent state if it is not set or differs // TODO this might need to get abstracted - const data = res.data as UserDetailsResponse; + let data = wsJsonToRes<GetUserDetailsResponse>(msg).data; this.state.userRes = data; this.setUserInfo(); this.state.loading = false; this.setState(this.state); - } else if (res.op == UserOperation.SaveUserSettings) { - const data = res.data as LoginResponse; + } else if (op == UserOperation.SaveUserSettings) { + let data = wsJsonToRes<LoginResponse>(msg).data; UserService.Instance.login(data); - this.state.userRes.user.bio = this.state.userSettingsForm.bio; - this.state.userRes.user.preferred_username = this.state.userSettingsForm.preferred_username; - this.state.userRes.user.banner = this.state.userSettingsForm.banner; - this.state.userRes.user.avatar = this.state.userSettingsForm.avatar; + this.state.userRes.user_view.user.bio = this.state.userSettingsForm.bio; + this.state.userRes.user_view.user.preferred_username = this.state.userSettingsForm.preferred_username; + this.state.userRes.user_view.user.banner = this.state.userSettingsForm.banner; + this.state.userRes.user_view.user.avatar = this.state.userSettingsForm.avatar; this.state.userSettingsLoading = false; this.setState(this.state); window.scrollTo(0, 0); - } else if (res.op == UserOperation.DeleteAccount) { + } else if (op == UserOperation.DeleteAccount) { this.setState({ deleteAccountLoading: false, deleteAccountShowConfirm: false, }); this.context.router.history.push('/'); - } else if (res.op == UserOperation.AddAdmin) { - const data = res.data as AddAdminResponse; + } else if (op == UserOperation.AddAdmin) { + let data = wsJsonToRes<AddAdminResponse>(msg).data; this.state.siteRes.admins = data.admins; this.setState(this.state); - } else if (res.op == UserOperation.CreateCommentLike) { - const data = res.data as CommentResponse; - createCommentLikeRes(data, this.state.userRes.comments); + } else if (op == UserOperation.CreateCommentLike) { + let data = wsJsonToRes<CommentResponse>(msg).data; + createCommentLikeRes(data.comment_view, this.state.userRes.comments); this.setState(this.state); } else if ( - res.op == UserOperation.EditComment || - res.op == UserOperation.DeleteComment || - res.op == UserOperation.RemoveComment + op == UserOperation.EditComment || + op == UserOperation.DeleteComment || + op == UserOperation.RemoveComment ) { - const data = res.data as CommentResponse; - editCommentRes(data, this.state.userRes.comments); + let data = wsJsonToRes<CommentResponse>(msg).data; + editCommentRes(data.comment_view, this.state.userRes.comments); this.setState(this.state); - } else if (res.op == UserOperation.CreateComment) { - const data = res.data as CommentResponse; + } else if (op == UserOperation.CreateComment) { + let data = wsJsonToRes<CommentResponse>(msg).data; if ( UserService.Instance.user && - data.comment.creator_id == UserService.Instance.user.id + data.comment_view.creator.id == UserService.Instance.user.id ) { toast(i18n.t('reply_sent')); } - } else if (res.op == UserOperation.SaveComment) { - const data = res.data as CommentResponse; - saveCommentRes(data, this.state.userRes.comments); + } else if (op == UserOperation.SaveComment) { + let data = wsJsonToRes<CommentResponse>(msg).data; + saveCommentRes(data.comment_view, this.state.userRes.comments); this.setState(this.state); } else if ( - res.op == UserOperation.EditPost || - res.op == UserOperation.DeletePost || - res.op == UserOperation.RemovePost || - res.op == UserOperation.LockPost || - res.op == UserOperation.StickyPost || - res.op == UserOperation.SavePost + op == UserOperation.EditPost || + op == UserOperation.DeletePost || + op == UserOperation.RemovePost || + op == UserOperation.LockPost || + op == UserOperation.StickyPost || + op == UserOperation.SavePost ) { - let data = res.data as PostResponse; - editPostFindRes(data, this.state.userRes.posts); + let data = wsJsonToRes<PostResponse>(msg).data; + editPostFindRes(data.post_view, this.state.userRes.posts); this.setState(this.state); - } else if (res.op == UserOperation.CreatePostLike) { - const data = res.data as PostResponse; - createPostLikeFindRes(data, this.state.userRes.posts); + } else if (op == UserOperation.CreatePostLike) { + let data = wsJsonToRes<PostResponse>(msg).data; + createPostLikeFindRes(data.post_view, this.state.userRes.posts); this.setState(this.state); - } else if (res.op == UserOperation.BanUser) { - const data = res.data as BanUserResponse; + } else if (op == UserOperation.BanUser) { + let data = wsJsonToRes<BanUserResponse>(msg).data; this.state.userRes.comments - .filter(c => c.creator_id == data.user.id) - .forEach(c => (c.banned = data.banned)); + .filter(c => c.creator.id == data.user_view.user.id) + .forEach(c => (c.creator.banned = data.banned)); this.state.userRes.posts - .filter(c => c.creator_id == data.user.id) - .forEach(c => (c.banned = data.banned)); + .filter(c => c.creator.id == data.user_view.user.id) + .forEach(c => (c.creator.banned = data.banned)); this.setState(this.state); } } diff --git a/src/shared/interfaces.ts b/src/shared/interfaces.ts index df083b9..116f5d1 100644 --- a/src/shared/interfaces.ts +++ b/src/shared/interfaces.ts @@ -1,9 +1,14 @@ -import { GetSiteResponse, LemmyHttp } from 'lemmy-js-client'; +import { + CommentView, + GetSiteResponse, + LemmyHttp, + UserMentionView, +} from 'lemmy-js-client'; export interface IsoData { path: string; routeData: any[]; - site: GetSiteResponse; + site_res: GetSiteResponse; // Lang and theme lang: string; // communities?: ListCommunitiesResponse; @@ -21,6 +26,20 @@ export interface InitialFetchRequest { client: LemmyHttp; } +export interface CommentNode { + comment_view: CommentView | UserMentionView; + children?: CommentNode[]; + depth?: number; +} + +export interface PostFormParams { + name: string; + url?: string; + body?: string; + community_name?: string; + community_id?: number; +} + export enum CommentSortType { Hot, Top, diff --git a/src/shared/services/UserService.ts b/src/shared/services/UserService.ts index 72066ad..391a5ef 100644 --- a/src/shared/services/UserService.ts +++ b/src/shared/services/UserService.ts @@ -1,8 +1,10 @@ // import Cookies from 'js-cookie'; import IsomorphicCookie from 'isomorphic-cookie'; -import { User, LoginResponse } from 'lemmy-js-client'; +import { User_, LoginResponse } from 'lemmy-js-client'; import jwt_decode from 'jwt-decode'; import { Subject, BehaviorSubject } from 'rxjs'; +import { i18n } from '../i18next'; +import { toast } from '../utils'; interface Claims { id: number; @@ -11,7 +13,7 @@ interface Claims { export class UserService { private static _instance: UserService; - public user: User; + public user: User_; public claims: Claims; public jwtSub: Subject<string> = new Subject<string>(); public unreadCountSub: BehaviorSubject<number> = new BehaviorSubject<number>( @@ -48,6 +50,15 @@ export class UserService { return IsomorphicCookie.load('jwt'); } + public authField(throwErr: boolean = true): string { + if (this.auth == null && throwErr) { + toast(i18n.t('not_logged_in'), 'danger'); + throw 'Not logged in'; + } else { + return this.auth; + } + } + private setClaims(jwt: string) { this.claims = jwt_decode(jwt); this.jwtSub.next(jwt); diff --git a/src/shared/services/WebSocketService.ts b/src/shared/services/WebSocketService.ts index 53af6d1..868bd40 100644 --- a/src/shared/services/WebSocketService.ts +++ b/src/shared/services/WebSocketService.ts @@ -1,66 +1,10 @@ import { wsUri } from '../env'; import { LemmyWebsocket, - LoginForm, - RegisterForm, - CommunityForm, - DeleteCommunityForm, - RemoveCommunityForm, - PostForm, - DeletePostForm, - RemovePostForm, - LockPostForm, - StickyPostForm, - SavePostForm, - CommentForm, - DeleteCommentForm, - RemoveCommentForm, - MarkCommentAsReadForm, - SaveCommentForm, - CommentLikeForm, - GetPostForm, - GetPostsForm, - CreatePostLikeForm, - GetCommunityForm, - FollowCommunityForm, - GetFollowedCommunitiesForm, - GetUserDetailsForm, - ListCommunitiesForm, - GetModlogForm, - BanFromCommunityForm, - AddModToCommunityForm, - TransferCommunityForm, - AddAdminForm, - TransferSiteForm, - BanUserForm, - SiteForm, - UserView, - GetRepliesForm, - GetUserMentionsForm, - MarkUserMentionAsReadForm, - SearchForm, - UserSettingsForm, - DeleteAccountForm, - PasswordResetForm, - PasswordChangeForm, - PrivateMessageForm, - EditPrivateMessageForm, - DeletePrivateMessageForm, - MarkPrivateMessageAsReadForm, - GetPrivateMessagesForm, - GetCommentsForm, - UserJoinForm, - GetSiteConfig, - GetSiteForm, - SiteConfigForm, - MarkAllAsReadForm, + UserViewSafe, WebSocketJsonResponse, - CommunityJoinForm, - PostJoinForm, } from 'lemmy-js-client'; -import { UserService } from './'; -import { i18n } from '../i18next'; -import { toast, isBrowser } from '../utils'; +import { isBrowser } from '../utils'; import { Observable } from 'rxjs'; import { share } from 'rxjs/operators'; import { @@ -77,9 +21,9 @@ export class WebSocketService { }; public subject: Observable<any>; - public admins: UserView[]; - public banned: UserView[]; - private client = new LemmyWebsocket(); + public admins: UserViewSafe[]; + public banned: UserViewSafe[]; + public client = new LemmyWebsocket(); private constructor() { this.ws = new ReconnectingWebSocket(wsUri, [], this.wsOptions); @@ -93,7 +37,7 @@ export class WebSocketService { console.log(`Connected to ${wsUri}`); if (!firstConnect) { - let res: WebSocketJsonResponse = { + let res: WebSocketJsonResponse<any> = { reconnect: true, }; obs.next(res); @@ -107,307 +51,6 @@ export class WebSocketService { public static get Instance() { return this._instance || (this._instance = new this()); } - - public userJoin() { - let form: UserJoinForm = { auth: UserService.Instance.auth }; - this.ws.send(this.client.userJoin(form)); - } - - public postJoin(form: PostJoinForm) { - this.ws.send(this.client.postJoin(form)); - } - - public communityJoin(form: CommunityJoinForm) { - this.ws.send(this.client.communityJoin(form)); - } - - public login(form: LoginForm) { - this.ws.send(this.client.login(form)); - } - - public register(form: RegisterForm) { - this.ws.send(this.client.register(form)); - } - - public getCaptcha() { - this.ws.send(this.client.getCaptcha()); - } - - public createCommunity(form: CommunityForm) { - this.setAuth(form); // TODO all these setauths at some point would be good to make required - this.ws.send(this.client.createCommunity(form)); - } - - public editCommunity(form: CommunityForm) { - this.setAuth(form); - this.ws.send(this.client.editCommunity(form)); - } - - public deleteCommunity(form: DeleteCommunityForm) { - this.setAuth(form); - this.ws.send(this.client.deleteCommunity(form)); - } - - public removeCommunity(form: RemoveCommunityForm) { - this.setAuth(form); - this.ws.send(this.client.removeCommunity(form)); - } - - public followCommunity(form: FollowCommunityForm) { - this.setAuth(form); - this.ws.send(this.client.followCommunity(form)); - } - - public listCommunities(form: ListCommunitiesForm) { - this.setAuth(form, false); - this.ws.send(this.client.listCommunities(form)); - } - - public getFollowedCommunities() { - let form: GetFollowedCommunitiesForm = { auth: UserService.Instance.auth }; - this.ws.send(this.client.getFollowedCommunities(form)); - } - - public listCategories() { - this.ws.send(this.client.listCategories()); - } - - public createPost(form: PostForm) { - this.setAuth(form); - this.ws.send(this.client.createPost(form)); - } - - public getPost(form: GetPostForm) { - this.setAuth(form, false); - this.ws.send(this.client.getPost(form)); - } - - public getCommunity(form: GetCommunityForm) { - this.setAuth(form, false); - this.ws.send(this.client.getCommunity(form)); - } - - public createComment(form: CommentForm) { - this.setAuth(form); - this.ws.send(this.client.createComment(form)); - } - - public editComment(form: CommentForm) { - this.setAuth(form); - this.ws.send(this.client.editComment(form)); - } - - public deleteComment(form: DeleteCommentForm) { - this.setAuth(form); - this.ws.send(this.client.deleteComment(form)); - } - - public removeComment(form: RemoveCommentForm) { - this.setAuth(form); - this.ws.send(this.client.removeComment(form)); - } - - public markCommentAsRead(form: MarkCommentAsReadForm) { - this.setAuth(form); - this.ws.send(this.client.markCommentAsRead(form)); - } - - public likeComment(form: CommentLikeForm) { - this.setAuth(form); - this.ws.send(this.client.likeComment(form)); - } - - public saveComment(form: SaveCommentForm) { - this.setAuth(form); - this.ws.send(this.client.saveComment(form)); - } - - public getPosts(form: GetPostsForm) { - this.setAuth(form, false); - this.ws.send(this.client.getPosts(form)); - } - - public getComments(form: GetCommentsForm) { - this.setAuth(form, false); - this.ws.send(this.client.getComments(form)); - } - - public likePost(form: CreatePostLikeForm) { - this.setAuth(form); - this.ws.send(this.client.likePost(form)); - } - - public editPost(form: PostForm) { - this.setAuth(form); - this.ws.send(this.client.editPost(form)); - } - - public deletePost(form: DeletePostForm) { - this.setAuth(form); - this.ws.send(this.client.deletePost(form)); - } - - public removePost(form: RemovePostForm) { - this.setAuth(form); - this.ws.send(this.client.removePost(form)); - } - - public lockPost(form: LockPostForm) { - this.setAuth(form); - this.ws.send(this.client.lockPost(form)); - } - - public stickyPost(form: StickyPostForm) { - this.setAuth(form); - this.ws.send(this.client.stickyPost(form)); - } - - public savePost(form: SavePostForm) { - this.setAuth(form); - this.ws.send(this.client.savePost(form)); - } - - public banFromCommunity(form: BanFromCommunityForm) { - this.setAuth(form); - this.ws.send(this.client.banFromCommunity(form)); - } - - public addModToCommunity(form: AddModToCommunityForm) { - this.setAuth(form); - this.ws.send(this.client.addModToCommunity(form)); - } - - public transferCommunity(form: TransferCommunityForm) { - this.setAuth(form); - this.ws.send(this.client.transferCommunity(form)); - } - - public transferSite(form: TransferSiteForm) { - this.setAuth(form); - this.ws.send(this.client.transferSite(form)); - } - - public banUser(form: BanUserForm) { - this.setAuth(form); - this.ws.send(this.client.banUser(form)); - } - - public addAdmin(form: AddAdminForm) { - this.setAuth(form); - this.ws.send(this.client.addAdmin(form)); - } - - public getUserDetails(form: GetUserDetailsForm) { - this.setAuth(form, false); - this.ws.send(this.client.getUserDetails(form)); - } - - public getReplies(form: GetRepliesForm) { - this.setAuth(form); - this.ws.send(this.client.getReplies(form)); - } - - public getUserMentions(form: GetUserMentionsForm) { - this.setAuth(form); - this.ws.send(this.client.getUserMentions(form)); - } - - public markUserMentionAsRead(form: MarkUserMentionAsReadForm) { - this.setAuth(form); - this.ws.send(this.client.markUserMentionAsRead(form)); - } - - public getModlog(form: GetModlogForm) { - this.ws.send(this.client.getModlog(form)); - } - - public createSite(form: SiteForm) { - this.setAuth(form); - this.ws.send(this.client.createSite(form)); - } - - public editSite(form: SiteForm) { - this.setAuth(form); - this.ws.send(this.client.editSite(form)); - } - - public getSite(form: GetSiteForm = {}) { - this.setAuth(form, false); - this.ws.send(this.client.getSite(form)); - } - - public getSiteConfig() { - let form: GetSiteConfig = {}; - this.setAuth(form); - this.ws.send(this.client.getSiteConfig(form)); - } - - public search(form: SearchForm) { - this.setAuth(form, false); - this.ws.send(this.client.search(form)); - } - - public markAllAsRead() { - let form: MarkAllAsReadForm = { auth: null }; - this.setAuth(form); - this.ws.send(this.client.markAllAsRead(form)); - } - - public saveUserSettings(form: UserSettingsForm) { - this.setAuth(form); - this.ws.send(this.client.saveUserSettings(form)); - } - - public deleteAccount(form: DeleteAccountForm) { - this.setAuth(form); - this.ws.send(this.client.deleteAccount(form)); - } - - public passwordReset(form: PasswordResetForm) { - this.ws.send(this.client.passwordReset(form)); - } - - public passwordChange(form: PasswordChangeForm) { - this.ws.send(this.client.passwordChange(form)); - } - - public createPrivateMessage(form: PrivateMessageForm) { - this.setAuth(form); - this.ws.send(this.client.createPrivateMessage(form)); - } - - public editPrivateMessage(form: EditPrivateMessageForm) { - this.setAuth(form); - this.ws.send(this.client.editPrivateMessage(form)); - } - - public deletePrivateMessage(form: DeletePrivateMessageForm) { - this.setAuth(form); - this.ws.send(this.client.deletePrivateMessage(form)); - } - - public markPrivateMessageAsRead(form: MarkPrivateMessageAsReadForm) { - this.setAuth(form); - this.ws.send(this.client.markPrivateMessageAsRead(form)); - } - - public getPrivateMessages(form: GetPrivateMessagesForm) { - this.setAuth(form); - this.ws.send(this.client.getPrivateMessages(form)); - } - - public saveSiteConfig(form: SiteConfigForm) { - this.setAuth(form); - this.ws.send(this.client.saveSiteConfig(form)); - } - - public setAuth(obj: any, throwErr: boolean = true) { - obj.auth = UserService.Instance.auth; - if (obj.auth == null && throwErr) { - toast(i18n.t('not_logged_in'), 'danger'); - throw 'Not logged in'; - } - } } if (isBrowser()) { diff --git a/src/shared/utils.ts b/src/shared/utils.ts index c823901..603512b 100644 --- a/src/shared/utils.ts +++ b/src/shared/utils.ts @@ -30,23 +30,25 @@ import 'moment/locale/da'; import { UserOperation, - Comment, - CommentNode as CommentNodeI, - Post, - PrivateMessage, - User, + CommentView, + User_, SortType, ListingType, SearchType, WebSocketResponse, WebSocketJsonResponse, - SearchForm, + Search, SearchResponse, - CommentResponse, - PostResponse, + PostView, + PrivateMessageView, } from 'lemmy-js-client'; -import { CommentSortType, DataType, IsoData } from './interfaces'; +import { + CommentSortType, + DataType, + IsoData, + CommentNode as CommentNodeI, +} from './interfaces'; import { UserService, WebSocketService } from './services'; var Tribute; @@ -154,14 +156,20 @@ export function randomStr( .join(''); } -export function wsJsonToRes(msg: WebSocketJsonResponse): WebSocketResponse { - let opStr: string = msg.op; +export function wsJsonToRes<ResponseType>( + msg: WebSocketJsonResponse<ResponseType> +): WebSocketResponse<ResponseType> { return { - op: UserOperation[opStr], + op: wsUserOp(msg), data: msg.data, }; } +export function wsUserOp(msg: any): UserOperation { + let opStr: string = msg.op; + return UserOperation[opStr]; +} + export const md = new markdown_it({ html: false, linkify: true, @@ -190,12 +198,16 @@ export const md = new markdown_it({ defs: objectFlip(emojiShortName), }); -export function hotRankComment(comment: Comment): number { - return hotRank(comment.score, comment.published); +export function hotRankComment(comment_view: CommentView): number { + return hotRank(comment_view.counts.score, comment_view.comment.published); +} + +export function hotRankActivePost(post_view: PostView): number { + return hotRank(post_view.counts.score, post_view.counts.newest_comment_time); } -export function hotRankPost(post: Post): number { - return hotRank(post.score, post.newest_activity_time); +export function hotRankPost(post_view: PostView): number { + return hotRank(post_view.counts.score, post_view.post.published); } export function hotRank(score: number, timeStr: string): number { @@ -221,17 +233,8 @@ export function getUnixTime(text: string): number { return text ? new Date(text).getTime() / 1000 : undefined; } -export function addTypeInfo<T>( - arr: T[], - name: string -): { type_: string; data: T }[] { - return arr.map(e => { - return { type_: name, data: e }; - }); -} - export function canMod( - user: User, + user: User_, modIds: number[], creator_id: number, onSelf: boolean = false @@ -509,21 +512,6 @@ export function isCakeDay(published: string): boolean { ); } -export function isCommentType( - item: Comment | PrivateMessage | Post -): item is Comment { - return ( - (item as Comment).community_id !== undefined && - (item as Comment).content !== undefined - ); -} - -export function isPostType( - item: Comment | PrivateMessage | Post -): item is Post { - return (item as Post).stickied !== undefined; -} - export function toast(text: string, background: string = 'success') { if (isBrowser()) { let backgroundColor = `var(--${background})`; @@ -592,32 +580,34 @@ export function messageToastify(info: NotifyInfo, router: any) { } } -export function notifyPost(post: Post, router: any) { +export function notifyPost(post_view: PostView, router: any) { let info: NotifyInfo = { - name: post.community_name, - icon: post.community_icon ? post.community_icon : defaultFavIcon, - link: `/post/${post.id}`, - body: post.name, + name: post_view.community.name, + icon: post_view.community.icon ? post_view.community.icon : defaultFavIcon, + link: `/post/${post_view.post.id}`, + body: post_view.post.name, }; notify(info, router); } -export function notifyComment(comment: Comment, router: any) { +export function notifyComment(comment_view: CommentView, router: any) { let info: NotifyInfo = { - name: comment.creator_name, - icon: comment.creator_avatar ? comment.creator_avatar : defaultFavIcon, - link: `/post/${comment.post_id}/comment/${comment.id}`, - body: comment.content, + name: comment_view.creator.name, + icon: comment_view.creator.avatar + ? comment_view.creator.avatar + : defaultFavIcon, + link: `/post/${comment_view.post.id}/comment/${comment_view.comment.id}`, + body: comment_view.comment.content, }; notify(info, router); } -export function notifyPrivateMessage(pm: PrivateMessage, router: any) { +export function notifyPrivateMessage(pmv: PrivateMessageView, router: any) { let info: NotifyInfo = { - name: pm.creator_name, - icon: pm.creator_avatar ? pm.creator_avatar : defaultFavIcon, + name: pmv.creator.name, + icon: pmv.creator.avatar ? pmv.creator.avatar : defaultFavIcon, link: `/inbox`, - body: pm.content, + body: pmv.private_message.content, }; notify(info, router); } @@ -723,27 +713,28 @@ export function setupTippy() { function userSearch(text: string, cb: any) { if (text) { - let form: SearchForm = { + let form: Search = { q: text, type_: SearchType.Users, sort: SortType.TopAll, page: 1, limit: mentionDropdownFetchLimit, + auth: UserService.Instance.authField(false), }; - WebSocketService.Instance.search(form); + WebSocketService.Instance.client.search(form); let userSub = WebSocketService.Instance.subject.subscribe( msg => { let res = wsJsonToRes(msg); if (res.op == UserOperation.Search) { let data = res.data as SearchResponse; - let users = data.users.map(u => { + let users = data.users.map(uv => { return { - key: `@${u.name}@${hostname(u.actor_id)}`, - name: u.name, - local: u.local, - id: u.id, + key: `@${uv.user.name}@${hostname(uv.user.actor_id)}`, + name: uv.user.name, + local: uv.user.local, + id: uv.user.id, }; }); cb(users); @@ -760,27 +751,28 @@ function userSearch(text: string, cb: any) { function communitySearch(text: string, cb: any) { if (text) { - let form: SearchForm = { + let form: Search = { q: text, type_: SearchType.Communities, sort: SortType.TopAll, page: 1, limit: mentionDropdownFetchLimit, + auth: UserService.Instance.authField(false), }; - WebSocketService.Instance.search(form); + WebSocketService.Instance.client.search(form); let communitySub = WebSocketService.Instance.subject.subscribe( msg => { let res = wsJsonToRes(msg); if (res.op == UserOperation.Search) { let data = res.data as SearchResponse; - let communities = data.communities.map(c => { + let communities = data.communities.map(cv => { return { - key: `!${c.name}@${hostname(c.actor_id)}`, - name: c.name, - local: c.local, - id: c.id, + key: `!${cv.community.name}@${hostname(cv.community.actor_id)}`, + name: cv.community.name, + local: cv.community.local, + id: cv.community.id, }; }); cb(communities); @@ -840,84 +832,84 @@ export function getUsernameFromProps(props: any): string { return props.match.params.username; } -export function editCommentRes(data: CommentResponse, comments: Comment[]) { - let found = comments.find(c => c.id == data.comment.id); +export function editCommentRes(data: CommentView, comments: CommentView[]) { + let found = comments.find(c => c.comment.id == data.comment.id); if (found) { - found.content = data.comment.content; - found.updated = data.comment.updated; - found.removed = data.comment.removed; - found.deleted = data.comment.deleted; - found.upvotes = data.comment.upvotes; - found.downvotes = data.comment.downvotes; - found.score = data.comment.score; + found.comment.content = data.comment.content; + found.comment.updated = data.comment.updated; + found.comment.removed = data.comment.removed; + found.comment.deleted = data.comment.deleted; + found.counts.upvotes = data.counts.upvotes; + found.counts.downvotes = data.counts.downvotes; + found.counts.score = data.counts.score; } } -export function saveCommentRes(data: CommentResponse, comments: Comment[]) { - let found = comments.find(c => c.id == data.comment.id); +export function saveCommentRes(data: CommentView, comments: CommentView[]) { + let found = comments.find(c => c.comment.id == data.comment.id); if (found) { - found.saved = data.comment.saved; + found.saved = data.saved; } } export function createCommentLikeRes( - data: CommentResponse, - comments: Comment[] + data: CommentView, + comments: CommentView[] ) { - let found: Comment = comments.find(c => c.id === data.comment.id); + let found = comments.find(c => c.comment.id === data.comment.id); if (found) { - found.score = data.comment.score; - found.upvotes = data.comment.upvotes; - found.downvotes = data.comment.downvotes; - if (data.comment.my_vote !== null) { - found.my_vote = data.comment.my_vote; + found.counts.score = data.counts.score; + found.counts.upvotes = data.counts.upvotes; + found.counts.downvotes = data.counts.downvotes; + if (data.my_vote !== null) { + found.my_vote = data.my_vote; } } } -export function createPostLikeFindRes(data: PostResponse, posts: Post[]) { - let found = posts.find(c => c.id == data.post.id); +export function createPostLikeFindRes(data: PostView, posts: PostView[]) { + let found = posts.find(p => p.post.id == data.post.id); if (found) { createPostLikeRes(data, found); } } -export function createPostLikeRes(data: PostResponse, post: Post) { - if (post) { - post.score = data.post.score; - post.upvotes = data.post.upvotes; - post.downvotes = data.post.downvotes; - if (data.post.my_vote !== null) { - post.my_vote = data.post.my_vote; +export function createPostLikeRes(data: PostView, post_view: PostView) { + if (post_view) { + post_view.counts.score = data.counts.score; + post_view.counts.upvotes = data.counts.upvotes; + post_view.counts.downvotes = data.counts.downvotes; + if (data.my_vote !== null) { + post_view.my_vote = data.my_vote; } } } -export function editPostFindRes(data: PostResponse, posts: Post[]) { - let found = posts.find(c => c.id == data.post.id); +export function editPostFindRes(data: PostView, posts: PostView[]) { + let found = posts.find(p => p.post.id == data.post.id); if (found) { editPostRes(data, found); } } -export function editPostRes(data: PostResponse, post: Post) { +export function editPostRes(data: PostView, post: PostView) { if (post) { - post.url = data.post.url; - post.name = data.post.name; - post.nsfw = data.post.nsfw; - post.deleted = data.post.deleted; - post.removed = data.post.removed; - post.stickied = data.post.stickied; - post.body = data.post.body; - post.locked = data.post.locked; - post.saved = data.post.saved; + post.post.url = data.post.url; + post.post.name = data.post.name; + post.post.nsfw = data.post.nsfw; + post.post.deleted = data.post.deleted; + post.post.removed = data.post.removed; + post.post.stickied = data.post.stickied; + post.post.body = data.post.body; + post.post.locked = data.post.locked; + post.saved = data.saved; } } -export function commentsToFlatNodes(comments: Comment[]): CommentNodeI[] { +export function commentsToFlatNodes(comments: CommentView[]): CommentNodeI[] { let nodes: CommentNodeI[] = []; for (let comment of comments) { - nodes.push({ comment: comment }); + nodes.push({ comment_view: comment }); } return nodes; } @@ -927,30 +919,34 @@ export function commentSort(tree: CommentNodeI[], sort: CommentSortType) { if (sort == CommentSortType.Top) { tree.sort( (a, b) => - +a.comment.removed - +b.comment.removed || - +a.comment.deleted - +b.comment.deleted || - b.comment.score - a.comment.score + +a.comment_view.comment.removed - +b.comment_view.comment.removed || + +a.comment_view.comment.deleted - +b.comment_view.comment.deleted || + b.comment_view.counts.score - a.comment_view.counts.score ); } else if (sort == CommentSortType.New) { tree.sort( (a, b) => - +a.comment.removed - +b.comment.removed || - +a.comment.deleted - +b.comment.deleted || - b.comment.published.localeCompare(a.comment.published) + +a.comment_view.comment.removed - +b.comment_view.comment.removed || + +a.comment_view.comment.deleted - +b.comment_view.comment.deleted || + b.comment_view.comment.published.localeCompare( + a.comment_view.comment.published + ) ); } else if (sort == CommentSortType.Old) { tree.sort( (a, b) => - +a.comment.removed - +b.comment.removed || - +a.comment.deleted - +b.comment.deleted || - a.comment.published.localeCompare(b.comment.published) + +a.comment_view.comment.removed - +b.comment_view.comment.removed || + +a.comment_view.comment.deleted - +b.comment_view.comment.deleted || + a.comment_view.comment.published.localeCompare( + b.comment_view.comment.published + ) ); } else if (sort == CommentSortType.Hot) { tree.sort( (a, b) => - +a.comment.removed - +b.comment.removed || - +a.comment.deleted - +b.comment.deleted || - hotRankComment(b.comment) - hotRankComment(a.comment) + +a.comment_view.comment.removed - +b.comment_view.comment.removed || + +a.comment_view.comment.deleted - +b.comment_view.comment.deleted || + hotRankComment(b.comment_view) - hotRankComment(a.comment_view) ); } @@ -985,7 +981,7 @@ function convertCommentSortType(sort: SortType): CommentSortType { } export function postSort( - posts: Post[], + posts: PostView[], sort: SortType, communityType: boolean ) { @@ -999,34 +995,34 @@ export function postSort( ) { posts.sort( (a, b) => - +a.removed - +b.removed || - +a.deleted - +b.deleted || - (communityType && +b.stickied - +a.stickied) || - b.score - a.score + +a.post.removed - +b.post.removed || + +a.post.deleted - +b.post.deleted || + (communityType && +b.post.stickied - +a.post.stickied) || + b.counts.score - a.counts.score ); } else if (sort == SortType.New) { posts.sort( (a, b) => - +a.removed - +b.removed || - +a.deleted - +b.deleted || - (communityType && +b.stickied - +a.stickied) || - b.published.localeCompare(a.published) + +a.post.removed - +b.post.removed || + +a.post.deleted - +b.post.deleted || + (communityType && +b.post.stickied - +a.post.stickied) || + b.post.published.localeCompare(a.post.published) ); } else if (sort == SortType.Hot) { posts.sort( (a, b) => - +a.removed - +b.removed || - +a.deleted - +b.deleted || - (communityType && +b.stickied - +a.stickied) || - b.hot_rank - a.hot_rank + +a.post.removed - +b.post.removed || + +a.post.deleted - +b.post.deleted || + (communityType && +b.post.stickied - +a.post.stickied) || + hotRankPost(b) - hotRankPost(a) ); } else if (sort == SortType.Active) { posts.sort( (a, b) => - +a.removed - +b.removed || - +a.deleted - +b.deleted || - (communityType && +b.stickied - +a.stickied) || - b.hot_rank_active - a.hot_rank_active + +a.post.removed - +b.post.removed || + +a.post.deleted - +b.post.deleted || + (communityType && +b.post.stickied - +a.post.stickied) || + hotRankActivePost(b) - hotRankActivePost(a) ); } } @@ -1094,12 +1090,6 @@ export function isBrowser() { return typeof window !== 'undefined'; } -export function setAuth(obj: any, auth: string) { - if (auth) { - obj.auth = auth; - } -} - export function setIsoData(context: any): IsoData { let isoData: IsoData = isBrowser() ? window.isoData diff --git a/yarn.lock b/yarn.lock index 0e54c52..292a3c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5518,10 +5518,10 @@ lcid@^1.0.0: dependencies: invert-kv "^1.0.0" -lemmy-js-client@^1.0.16: - version "1.0.16" - resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-1.0.16.tgz#84bf094c246d987f2f192bfac75340430821fee9" - integrity sha512-WvEEGrYNA2dzfzlufWB9LhUcll0O06WaUjSfBn5lYY/SFFsvBW5ImD42P/QwvN8Sgj6xVQiboe+Z8T++iAjKVw== +lemmy-js-client@1.0.17-beta5: + version "1.0.17-beta5" + resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-1.0.17-beta5.tgz#b8d128ef3a6a17bc3ac4eea8e30618b68ea4f2df" + integrity sha512-Z/8HV8tG9aB75GjDX1U2b3pFZnysGIymeVO+oepOkYfhHRB8SKmLS9ATuIw9OW1NjJduxbpGGgDH+bkf0Sx7dA== leven@^3.1.0: version "3.1.0" -- 2.44.1