First pass at v2_api

This commit is contained in:
Dessalines 2020-12-23 20:58:27 -05:00
parent 6ffe0c530d
commit ea317af269
41 changed files with 2217 additions and 2466 deletions

View file

@ -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",

View file

@ -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>
);

View file

@ -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) {

View file

@ -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;

View file

@ -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>
</>

View file

@ -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 });
}
}

View file

@ -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);
}

View file

@ -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}

View file

@ -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} />
<CommunityLink community={cv.community} />
</td>
<td>{community.category_name}</td>
<td class="text-right">
{community.number_of_subscribers}
<td>{cv.category.name}</td>
<td class="text-right">{cv.counts.subscribers}</td>
<td class="text-right d-none d-lg-table-cell">
{cv.counts.posts}
</td>
<td class="text-right d-none d-lg-table-cell">
{community.number_of_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);
}
}

View file

@ -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);
}
}
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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);

View file

@ -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);

View file

@ -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);
}

View file

@ -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;
} else if (op == UserOperation.MarkUserMentionAsRead) {
let data = wsJsonToRes<UserMentionResponse>(msg).data;
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;
// 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
);
}

View file

@ -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() {

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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'
);
let added = addTypeInfo(res.added, 'added');
let banned = addTypeInfo(res.banned, 'banned');
this.state.combined = [];
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_,
}));
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);
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_,
}));
if (this.state.communityId && this.state.combined.length > 0) {
this.state.communityName = (this.state.combined[0]
.data as ModRemovePost).community_name;
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_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} />
<MomentTime data={i} />
</td>
<td>
<Link to={`/u/${i.data.mod_user_name}`}>
{i.data.mod_user_name}
</Link>
</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);
}
}

View file

@ -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)
);
}

View file

@ -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);

View file

@ -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;

View file

@ -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);
}

View file

@ -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 {

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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 = '/';
}
}

View file

@ -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);

View file

@ -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);
}

View file

@ -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> {

View file

@ -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> {
}
}
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 };
});
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 />;
}
}
const combined: { type: string; data: Comment | Post }[] = [
...comments,
...posts,
];
overview() {
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}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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,

View file

@ -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);

View file

@ -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()) {

View file

@ -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 hotRankPost(post: Post): number {
return hotRank(post.score, post.newest_activity_time);
export function hotRankActivePost(post_view: PostView): number {
return hotRank(post_view.counts.score, post_view.counts.newest_comment_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

View file

@ -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"