Merge pull request #221 from LemmyNet/split_user_table

user_ -> person table migration.
This commit is contained in:
Dessalines 2021-03-25 11:42:46 -04:00 committed by GitHub
commit 7ddcac5fc0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 631 additions and 526 deletions

@ -1 +1 @@
Subproject commit a15d6bcb944b63478e9499ca77d6023a675cb002
Subproject commit 4b82eeaaf4762e7a282548b675f40352c1f5ef33

View file

@ -67,7 +67,8 @@
"eslint": "^7.20.0",
"eslint-plugin-prettier": "^3.3.1",
"husky": "^5.1.0",
"lemmy-js-client": "0.10.0-rc.1",
"iso-639-1": "^2.1.9",
"lemmy-js-client": "0.10.0-rc.10",
"lint-staged": "^10.5.4",
"mini-css-extract-plugin": "^1.3.8",
"node-fetch": "^2.6.1",

View file

@ -12,7 +12,7 @@ import {
} from "../shared/interfaces";
import { routes } from "../shared/routes";
import IsomorphicCookie from "isomorphic-cookie";
import { GetSite, LemmyHttp } from "lemmy-js-client";
import { GetSite, GetSiteResponse, LemmyHttp } from "lemmy-js-client";
import process from "process";
import { Helmet } from "inferno-helmet";
import { initializeSite } from "../shared/initialize";
@ -48,7 +48,18 @@ server.get("/*", async (req, res) => {
};
// Get site data first
let site = await initialFetchReq.client.getSite(getSiteForm);
// This bypasses errors, so that the client can hit the error on its own,
// in order to remove the jwt on the browser. Necessary for wrong jwts
let try_site: any = await initialFetchReq.client.getSite(getSiteForm);
if (try_site.error == "not_logged_in") {
console.error(
"Incorrect JWT token, skipping auth so frontend can remove jwt cookie"
);
delete getSiteForm.auth;
delete initialFetchReq.auth;
try_site = await initialFetchReq.client.getSite(getSiteForm);
}
let site: GetSiteResponse = try_site;
initializeSite(site);
if (activeRoute.fetchInitialData) {
@ -67,7 +78,7 @@ server.get("/*", async (req, res) => {
? req.headers["accept-language"].split(",")[0]
: "en";
let lang = site.my_user
? site.my_user.lang == "browser"
? site.my_user.local_user.lang == "browser"
? acceptLang
: "en"
: acceptLang;

View file

@ -23,7 +23,7 @@ import {
} from "../utils";
import autosize from "autosize";
import { SiteForm } from "./site-form";
import { UserListing } from "./user-listing";
import { PersonListing } from "./person-listing";
import { HtmlTags } from "./html-tags";
import { Spinner } from "./icon";
import { i18n } from "../i18next";
@ -135,7 +135,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={admin.user} />
<PersonListing person={admin.person} />
</li>
))}
</ul>
@ -150,7 +150,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={banned.user} />
<PersonListing person={banned.person} />
</li>
))}
</ul>

View file

@ -26,7 +26,7 @@ export class App extends Component<AppProps, any> {
<>
<Provider i18next={i18n}>
<div>
<Theme user={siteRes.my_user} />
<Theme localUserView={siteRes.my_user} />
{siteRes &&
siteRes.site_view &&
this.props.siteRes.site_view.site.icon && (

View file

@ -68,7 +68,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
render() {
return (
<div class="mb-3">
{UserService.Instance.user ? (
{UserService.Instance.localUserView ? (
<MarkdownTextArea
initialContent={
this.props.edit
@ -133,7 +133,7 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
let op = wsUserOp(msg);
// Only do the showing and hiding if logged in
if (UserService.Instance.user) {
if (UserService.Instance.localUserView) {
if (
op == UserOperation.CreateComment ||
op == UserOperation.EditComment

View file

@ -5,18 +5,18 @@ import {
DeleteComment,
RemoveComment,
MarkCommentAsRead,
MarkUserMentionAsRead,
MarkPersonMentionAsRead,
SaveComment,
BanFromCommunity,
BanUser,
BanPerson,
CommunityModeratorView,
UserViewSafe,
PersonViewSafe,
AddModToCommunity,
AddAdmin,
TransferCommunity,
TransferSite,
CommentView,
UserMentionView,
PersonMentionView,
} from "lemmy-js-client";
import { CommentNode as CommentNodeI, BanType } from "../interfaces";
import { WebSocketService, UserService } from "../services";
@ -34,7 +34,7 @@ import moment from "moment";
import { MomentTime } from "./moment-time";
import { CommentForm } from "./comment-form";
import { CommentNodes } from "./comment-nodes";
import { UserListing } from "./user-listing";
import { PersonListing } from "./person-listing";
import { CommunityLink } from "./community-link";
import { Icon, Spinner } from "./icon";
import { i18n } from "../i18next";
@ -74,7 +74,7 @@ interface CommentNodeProps {
markable?: boolean;
showContext?: boolean;
moderators: CommunityModeratorView[];
admins: UserViewSafe[];
admins: PersonViewSafe[];
// TODO is this necessary, can't I get it from the node itself?
postCreatorId?: number;
showCommunity?: boolean;
@ -156,7 +156,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
>
<div class="d-flex flex-wrap align-items-center text-muted small">
<span class="mr-2">
<UserListing user={cv.creator} />
<PersonListing person={cv.creator} />
</span>
{this.isMod && (
@ -270,7 +270,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
)}
</button>
)}
{UserService.Instance.user && !this.props.viewOnly && (
{UserService.Instance.localUserView && !this.props.viewOnly && (
<>
<button
className={`btn btn-link btn-animate ${
@ -791,7 +791,9 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
get commentOrMentionRead() {
let cv = this.props.node.comment_view;
return this.isUserMentionType(cv) ? cv.user_mention.read : cv.comment.read;
return this.isPersonMentionType(cv)
? cv.person_mention.read
: cv.comment.read;
}
get linkBtn() {
@ -813,8 +815,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
get myComment(): boolean {
return (
UserService.Instance.user &&
this.props.node.comment_view.creator.id == UserService.Instance.user.id
this.props.node.comment_view.creator.id ==
UserService.Instance.localUserView?.person.id
);
}
@ -832,7 +834,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
return (
this.props.admins &&
isMod(
this.props.admins.map(a => a.user.id),
this.props.admins.map(a => a.person.id),
this.props.node.comment_view.creator.id
)
);
@ -845,11 +847,11 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
get canMod(): boolean {
if (this.props.admins && this.props.moderators) {
let adminsThenMods = this.props.admins
.map(a => a.user.id)
.map(a => a.person.id)
.concat(this.props.moderators.map(m => m.moderator.id));
return canMod(
UserService.Instance.user,
UserService.Instance.localUserView,
adminsThenMods,
this.props.node.comment_view.creator.id
);
@ -862,8 +864,8 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
return (
this.props.admins &&
canMod(
UserService.Instance.user,
this.props.admins.map(a => a.user.id),
UserService.Instance.localUserView,
this.props.admins.map(a => a.person.id),
this.props.node.comment_view.creator.id
)
);
@ -872,18 +874,22 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
get amCommunityCreator(): boolean {
return (
this.props.moderators &&
UserService.Instance.user &&
this.props.node.comment_view.creator.id != UserService.Instance.user.id &&
UserService.Instance.user.id == this.props.moderators[0].moderator.id
UserService.Instance.localUserView &&
this.props.node.comment_view.creator.id !=
UserService.Instance.localUserView.person.id &&
UserService.Instance.localUserView.person.id ==
this.props.moderators[0].moderator.id
);
}
get amSiteCreator(): boolean {
return (
this.props.admins &&
UserService.Instance.user &&
this.props.node.comment_view.creator.id != UserService.Instance.user.id &&
UserService.Instance.user.id == this.props.admins[0].user.id
UserService.Instance.localUserView &&
this.props.node.comment_view.creator.id !=
UserService.Instance.localUserView.person.id &&
UserService.Instance.localUserView.person.id ==
this.props.admins[0].person.id
);
}
@ -1024,20 +1030,20 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
i.setState(i.state);
}
isUserMentionType(
item: CommentView | UserMentionView
): item is UserMentionView {
return (item as UserMentionView).user_mention?.id !== undefined;
isPersonMentionType(
item: CommentView | PersonMentionView
): item is PersonMentionView {
return (item as PersonMentionView).person_mention?.id !== undefined;
}
handleMarkRead(i: CommentNode) {
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,
if (i.isPersonMentionType(i.props.node.comment_view)) {
let form: MarkPersonMentionAsRead = {
person_mention_id: i.props.node.comment_view.person_mention.id,
read: !i.props.node.comment_view.person_mention.read,
auth: authField(),
};
WebSocketService.Instance.send(wsClient.markUserMentionAsRead(form));
WebSocketService.Instance.send(wsClient.markPersonMentionAsRead(form));
} else {
let form: MarkCommentAsRead = {
comment_id: i.props.node.comment_view.comment.id,
@ -1095,7 +1101,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
i.state.removeData = false;
}
let form: BanFromCommunity = {
user_id: cv.creator.id,
person_id: cv.creator.id,
community_id: cv.community.id,
ban,
remove_data: i.state.removeData,
@ -1110,15 +1116,15 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
if (ban == false) {
i.state.removeData = false;
}
let form: BanUser = {
user_id: cv.creator.id,
let form: BanPerson = {
person_id: cv.creator.id,
ban,
remove_data: i.state.removeData,
reason: i.state.banReason,
expires: getUnixTime(i.state.banExpires),
auth: authField(),
};
WebSocketService.Instance.send(wsClient.banUser(form));
WebSocketService.Instance.send(wsClient.banPerson(form));
}
i.state.showBanDialog = false;
@ -1138,7 +1144,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
handleAddModToCommunity(i: CommentNode) {
let cv = i.props.node.comment_view;
let form: AddModToCommunity = {
user_id: cv.creator.id,
person_id: cv.creator.id,
community_id: cv.community.id,
added: !i.isMod,
auth: authField(),
@ -1160,7 +1166,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
handleAddAdmin(i: CommentNode) {
let form: AddAdmin = {
user_id: i.props.node.comment_view.creator.id,
person_id: i.props.node.comment_view.creator.id,
added: !i.isAdmin,
auth: authField(),
};
@ -1183,7 +1189,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
let cv = i.props.node.comment_view;
let form: TransferCommunity = {
community_id: cv.community.id,
user_id: cv.creator.id,
person_id: cv.creator.id,
auth: authField(),
};
WebSocketService.Instance.send(wsClient.transferCommunity(form));
@ -1203,7 +1209,7 @@ export class CommentNode extends Component<CommentNodeProps, CommentNodeState> {
handleTransferSite(i: CommentNode) {
let form: TransferSite = {
user_id: i.props.node.comment_view.creator.id,
person_id: i.props.node.comment_view.creator.id,
auth: authField(),
};
WebSocketService.Instance.send(wsClient.transferSite(form));

View file

@ -1,12 +1,12 @@
import { Component } from "inferno";
import { CommentNode as CommentNodeI } from "../interfaces";
import { CommunityModeratorView, UserViewSafe } from "lemmy-js-client";
import { CommunityModeratorView, PersonViewSafe } from "lemmy-js-client";
import { CommentNode } from "./comment-node";
interface CommentNodesProps {
nodes: CommentNodeI[];
moderators?: CommunityModeratorView[];
admins?: UserViewSafe[];
admins?: PersonViewSafe[];
postCreatorId?: number;
noBorder?: boolean;
noIndent?: boolean;

View file

@ -177,8 +177,10 @@ export class Community extends Component<any, State> {
let sort: SortType = pathSplit[6]
? SortType[pathSplit[6]]
: UserService.Instance.user
? Object.values(SortType)[UserService.Instance.user.default_sort_type]
: UserService.Instance.localUserView
? Object.values(SortType)[
UserService.Instance.localUserView.local_user.default_sort_type
]
: SortType.Active;
let page = pathSplit[8] ? Number(pathSplit[8]) : 1;
@ -189,6 +191,7 @@ export class Community extends Component<any, State> {
limit: fetchLimit,
sort,
type_: ListingType.Community,
saved_only: false,
};
setOptionalAuth(getPostsForm, req.auth);
this.setIdOrName(getPostsForm, id, name_);
@ -199,6 +202,7 @@ export class Community extends Component<any, State> {
limit: fetchLimit,
sort,
type_: ListingType.Community,
saved_only: false,
};
setOptionalAuth(getCommentsForm, req.auth);
this.setIdOrName(getCommentsForm, id, name_);
@ -407,6 +411,7 @@ export class Community extends Component<any, State> {
type_: ListingType.Community,
community_id: this.state.communityId,
community_name: this.state.communityName,
saved_only: false,
auth: authField(false),
};
WebSocketService.Instance.send(wsClient.getPosts(form));
@ -418,6 +423,7 @@ export class Community extends Component<any, State> {
type_: ListingType.Community,
community_id: this.state.communityId,
community_name: this.state.communityName,
saved_only: false,
auth: authField(false),
};
WebSocketService.Instance.send(wsClient.getComments(form));
@ -499,7 +505,7 @@ export class Community extends Component<any, State> {
// TODO this might be incorrect
this.state.posts
.filter(p => p.creator.id == data.user_view.user.id)
.filter(p => p.creator.id == data.person_view.person.id)
.forEach(p => (p.creator_banned_from_community = data.banned));
this.setState(this.state);

View file

@ -28,7 +28,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
this.parseMessage = this.parseMessage.bind(this);
this.subscription = wsSubscribe(this.parseMessage);
if (!UserService.Instance.user && isBrowser()) {
if (!UserService.Instance.localUserView && isBrowser()) {
toast(i18n.t("not_logged_in"), "danger");
this.context.router.history.push(`/login`);
}

View file

@ -48,7 +48,7 @@ export class CreatePost extends Component<any, CreatePostState> {
this.handlePostCreate = this.handlePostCreate.bind(this);
this.state = this.emptyState;
if (!UserService.Instance.user && isBrowser()) {
if (!UserService.Instance.localUserView && isBrowser()) {
toast(i18n.t("not_logged_in"), "danger");
this.context.router.history.push(`/login`);
}

View file

@ -7,10 +7,10 @@ import { UserService, WebSocketService } from "../services";
import {
SiteView,
UserOperation,
GetUserDetailsResponse,
UserViewSafe,
GetPersonDetailsResponse,
PersonViewSafe,
SortType,
GetUserDetails,
GetPersonDetails,
} from "lemmy-js-client";
import {
authField,
@ -28,7 +28,7 @@ import { InitialFetchRequest } from "shared/interfaces";
interface CreatePrivateMessageState {
site_view: SiteView;
recipient: UserViewSafe;
recipient: PersonViewSafe;
recipient_id: number;
loading: boolean;
}
@ -55,7 +55,7 @@ export class CreatePrivateMessage extends Component<
this.parseMessage = this.parseMessage.bind(this);
this.subscription = wsSubscribe(this.parseMessage);
if (!UserService.Instance.user) {
if (!UserService.Instance.localUserView) {
toast(i18n.t("not_logged_in"), "danger");
this.context.router.history.push(`/login`);
}
@ -65,29 +65,29 @@ export class CreatePrivateMessage extends Component<
this.state.recipient = this.isoData.routeData[0].user;
this.state.loading = false;
} else {
this.fetchUserDetails();
this.fetchPersonDetails();
}
}
fetchUserDetails() {
let form: GetUserDetails = {
user_id: this.state.recipient_id,
fetchPersonDetails() {
let form: GetPersonDetails = {
person_id: this.state.recipient_id,
sort: SortType.New,
saved_only: false,
auth: authField(false),
};
WebSocketService.Instance.send(wsClient.getUserDetails(form));
WebSocketService.Instance.send(wsClient.getPersonDetails(form));
}
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
let user_id = Number(req.path.split("/").pop());
let form: GetUserDetails = {
user_id,
let person_id = Number(req.path.split("/").pop());
let form: GetPersonDetails = {
person_id,
sort: SortType.New,
saved_only: false,
auth: req.auth,
};
return [req.client.getUserDetails(form)];
return [req.client.getPersonDetails(form)];
}
get documentTitle(): string {
@ -119,7 +119,7 @@ export class CreatePrivateMessage extends Component<
<h5>{i18n.t("create_private_message")}</h5>
<PrivateMessageForm
onCreate={this.handlePrivateMessageCreate}
recipient={this.state.recipient.user}
recipient={this.state.recipient.person}
/>
</div>
</div>
@ -142,9 +142,9 @@ export class CreatePrivateMessage extends Component<
this.state.loading = false;
this.setState(this.state);
return;
} else if (op == UserOperation.GetUserDetails) {
let data = wsJsonToRes<GetUserDetailsResponse>(msg).data;
this.state.recipient = data.user_view;
} else if (op == UserOperation.GetPersonDetails) {
let data = wsJsonToRes<GetPersonDetailsResponse>(msg).data;
this.state.recipient = data.person_view;
this.state.loading = false;
this.setState(this.state);
}

View file

@ -13,7 +13,9 @@ export class Icon extends Component<IconProps, any> {
render() {
return (
<svg class={`icon ${this.props.classes}`}>
<div class="sr-only">
<title>{this.props.icon}</title>
</div>
<use xlinkHref={`#icon-${this.props.icon}`}></use>
</svg>
);

View file

@ -65,7 +65,7 @@ export class ImageUploadForm extends Component<
accept="image/*,video/*"
name={this.id}
class="d-none"
disabled={!UserService.Instance.user}
disabled={!UserService.Instance.localUserView}
onChange={linkEvent(this, this.handleImageUpload)}
/>
</form>

View file

@ -6,16 +6,16 @@ import {
SortType,
GetReplies,
GetRepliesResponse,
GetUserMentions,
GetUserMentionsResponse,
UserMentionResponse,
GetPersonMentions,
GetPersonMentionsResponse,
PersonMentionResponse,
CommentResponse,
PrivateMessageView,
GetPrivateMessages,
PrivateMessagesResponse,
PrivateMessageResponse,
SiteView,
UserMentionView,
PersonMentionView,
} from "lemmy-js-client";
import { WebSocketService, UserService } from "../services";
import {
@ -62,7 +62,7 @@ enum ReplyEnum {
type ReplyType = {
id: number;
type_: ReplyEnum;
view: CommentView | PrivateMessageView | UserMentionView;
view: CommentView | PrivateMessageView | PersonMentionView;
published: string;
};
@ -70,7 +70,7 @@ interface InboxState {
unreadOrAll: UnreadOrAll;
messageType: MessageType;
replies: CommentView[];
mentions: UserMentionView[];
mentions: PersonMentionView[];
messages: PrivateMessageView[];
combined: ReplyType[];
sort: SortType;
@ -101,7 +101,7 @@ export class Inbox extends Component<any, InboxState> {
this.state = this.emptyState;
this.handleSortChange = this.handleSortChange.bind(this);
if (!UserService.Instance.user && isBrowser()) {
if (!UserService.Instance.localUserView && isBrowser()) {
toast(i18n.t("not_logged_in"), "danger");
this.context.router.history.push(`/login`);
}
@ -128,9 +128,9 @@ export class Inbox extends Component<any, InboxState> {
}
get documentTitle(): string {
return `@${UserService.Instance.user.name} ${i18n.t("inbox")} - ${
this.state.site_view.site.name
}`;
return `@${UserService.Instance.localUserView.person.name} ${i18n.t(
"inbox"
)} - ${this.state.site_view.site.name}`;
}
render() {
@ -307,9 +307,9 @@ export class Inbox extends Component<any, InboxState> {
};
}
mentionToReplyType(r: UserMentionView): ReplyType {
mentionToReplyType(r: PersonMentionView): ReplyType {
return {
id: r.user_mention.id,
id: r.person_mention.id,
type_: ReplyEnum.Mention,
view: r,
published: r.comment.published,
@ -359,7 +359,7 @@ export class Inbox extends Component<any, InboxState> {
return (
<CommentNodes
key={i.id}
nodes={[{ comment_view: i.view as UserMentionView }]}
nodes={[{ comment_view: i.view as PersonMentionView }]}
noIndent
markable
showCommunity
@ -403,7 +403,7 @@ export class Inbox extends Component<any, InboxState> {
<div>
{this.state.mentions.map(umv => (
<CommentNodes
key={umv.user_mention.id}
key={umv.person_mention.id}
nodes={[{ comment_view: umv }]}
noIndent
markable
@ -491,14 +491,14 @@ export class Inbox extends Component<any, InboxState> {
};
promises.push(req.client.getReplies(repliesForm));
let userMentionsForm: GetUserMentions = {
let personMentionsForm: GetPersonMentions = {
sort: SortType.New,
unread_only: true,
page: 1,
limit: fetchLimit,
auth: req.auth,
};
promises.push(req.client.getUserMentions(userMentionsForm));
promises.push(req.client.getPersonMentions(personMentionsForm));
let privateMessagesForm: GetPrivateMessages = {
unread_only: true,
@ -521,14 +521,16 @@ export class Inbox extends Component<any, InboxState> {
};
WebSocketService.Instance.send(wsClient.getReplies(repliesForm));
let userMentionsForm: GetUserMentions = {
let personMentionsForm: GetPersonMentions = {
sort: this.state.sort,
unread_only: this.state.unreadOrAll == UnreadOrAll.Unread,
page: this.state.page,
limit: fetchLimit,
auth: authField(),
};
WebSocketService.Instance.send(wsClient.getUserMentions(userMentionsForm));
WebSocketService.Instance.send(
wsClient.getPersonMentions(personMentionsForm)
);
let privateMessagesForm: GetPrivateMessages = {
unread_only: this.state.unreadOrAll == UnreadOrAll.Unread,
@ -579,8 +581,8 @@ export class Inbox extends Component<any, InboxState> {
window.scrollTo(0, 0);
this.setState(this.state);
setupTippy();
} else if (op == UserOperation.GetUserMentions) {
let data = wsJsonToRes<GetUserMentionsResponse>(msg).data;
} else if (op == UserOperation.GetPersonMentions) {
let data = wsJsonToRes<GetPersonMentionsResponse>(msg).data;
this.state.mentions = data.mentions;
this.state.combined = this.buildCombined();
this.sendUnreadCount();
@ -698,48 +700,49 @@ export class Inbox extends Component<any, InboxState> {
this.sendUnreadCount();
this.setState(this.state);
setupTippy();
} else if (op == UserOperation.MarkUserMentionAsRead) {
let data = wsJsonToRes<UserMentionResponse>(msg).data;
} else if (op == UserOperation.MarkPersonMentionAsRead) {
let data = wsJsonToRes<PersonMentionResponse>(msg).data;
// TODO this might not be correct, it might need to use the comment id
let found = this.state.mentions.find(
c => c.user_mention.id == data.user_mention_view.user_mention.id
c => c.person_mention.id == data.person_mention_view.person_mention.id
);
if (found) {
let combinedView = this.state.combined.find(
i => i.id == data.user_mention_view.user_mention.id
).view as UserMentionView;
i => i.id == data.person_mention_view.person_mention.id
).view as PersonMentionView;
found.comment.content = combinedView.comment.content =
data.user_mention_view.comment.content;
data.person_mention_view.comment.content;
found.comment.updated = combinedView.comment.updated =
data.user_mention_view.comment.updated;
data.person_mention_view.comment.updated;
found.comment.removed = combinedView.comment.removed =
data.user_mention_view.comment.removed;
data.person_mention_view.comment.removed;
found.comment.deleted = combinedView.comment.deleted =
data.user_mention_view.comment.deleted;
data.person_mention_view.comment.deleted;
found.counts.upvotes = combinedView.counts.upvotes =
data.user_mention_view.counts.upvotes;
data.person_mention_view.counts.upvotes;
found.counts.downvotes = combinedView.counts.downvotes =
data.user_mention_view.counts.downvotes;
data.person_mention_view.counts.downvotes;
found.counts.score = combinedView.counts.score =
data.user_mention_view.counts.score;
data.person_mention_view.counts.score;
// If youre in the unread view, just remove it from the list
if (
this.state.unreadOrAll == UnreadOrAll.Unread &&
data.user_mention_view.user_mention.read
data.person_mention_view.person_mention.read
) {
this.state.mentions = this.state.mentions.filter(
r => r.user_mention.id !== data.user_mention_view.user_mention.id
r =>
r.person_mention.id !== data.person_mention_view.person_mention.id
);
this.state.combined = this.state.combined.filter(
r => r.id !== data.user_mention_view.user_mention.id
r => r.id !== data.person_mention_view.person_mention.id
);
} else {
// TODO test to make sure these mentions are getting marked as read
found.user_mention.read = combinedView.user_mention.read =
data.user_mention_view.user_mention.read;
found.person_mention.read = combinedView.person_mention.read =
data.person_mention_view.person_mention.read;
}
}
this.sendUnreadCount();
@ -747,18 +750,26 @@ export class Inbox extends Component<any, InboxState> {
} else if (op == UserOperation.CreateComment) {
let data = wsJsonToRes<CommentResponse>(msg).data;
if (data.recipient_ids.includes(UserService.Instance.user.id)) {
if (
data.recipient_ids.includes(
UserService.Instance.localUserView.local_user.id
)
) {
this.state.replies.unshift(data.comment_view);
this.state.combined.unshift(this.replyToReplyType(data.comment_view));
this.setState(this.state);
} else if (data.comment_view.creator.id == UserService.Instance.user.id) {
} else if (
data.comment_view.creator.id ==
UserService.Instance.localUserView.person.id
) {
// TODO this seems wrong, you should be using form_id
toast(i18n.t("reply_sent"));
}
} else if (op == UserOperation.CreatePrivateMessage) {
let data = wsJsonToRes<PrivateMessageResponse>(msg).data;
if (
data.private_message_view.recipient.id == UserService.Instance.user.id
data.private_message_view.recipient.id ==
UserService.Instance.localUserView.person.id
) {
this.state.messages.unshift(data.private_message_view);
this.state.combined.unshift(
@ -785,13 +796,13 @@ export class Inbox extends Component<any, InboxState> {
unreadCount(): number {
return (
this.state.replies.filter(r => !r.comment.read).length +
this.state.mentions.filter(r => !r.user_mention.read).length +
this.state.mentions.filter(r => !r.person_mention.read).length +
this.state.messages.filter(
r =>
UserService.Instance.user &&
UserService.Instance.localUserView &&
!r.private_message.read &&
// TODO also seems very strang and wrong
r.creator.id !== UserService.Instance.user.id
// TODO also seems very strange and wrong
r.creator.id !== UserService.Instance.localUserView.person.id
).length
);
}

View file

@ -42,7 +42,11 @@ export class ListingTypeSelect extends Component<
<label
className={`btn btn-outline-secondary
${this.state.type_ == ListingType.Subscribed && "active"}
${UserService.Instance.user == undefined ? "disabled" : "pointer"}
${
UserService.Instance.localUserView == undefined
? "disabled"
: "pointer"
}
`}
>
<input
@ -51,7 +55,7 @@ export class ListingTypeSelect extends Component<
value={ListingType.Subscribed}
checked={this.state.type_ == ListingType.Subscribed}
onChange={linkEvent(this, this.handleTypeChange)}
disabled={UserService.Instance.user == undefined}
disabled={UserService.Instance.localUserView == undefined}
/>
{i18n.t("subscribed")}
</label>

View file

@ -21,7 +21,7 @@ import {
GetCommentsResponse,
CommentResponse,
AddAdminResponse,
BanUserResponse,
BanPersonResponse,
} from "lemmy-js-client";
import { DataType, InitialFetchRequest } from "../interfaces";
import { WebSocketService, UserService } from "../services";
@ -31,7 +31,7 @@ import { SortSelect } from "./sort-select";
import { ListingTypeSelect } from "./listing-type-select";
import { DataTypeSelect } from "./data-type-select";
import { SiteForm } from "./site-form";
import { UserListing } from "./user-listing";
import { PersonListing } from "./person-listing";
import { CommunityLink } from "./community-link";
import { BannerIconHeader } from "./banner-icon-header";
import { Icon, Spinner } from "./icon";
@ -130,14 +130,14 @@ export class Main extends Component<any, MainState> {
this.state.comments = this.isoData.routeData[0].comments;
}
this.state.trendingCommunities = this.isoData.routeData[1].communities;
if (UserService.Instance.user) {
if (UserService.Instance.localUserView) {
this.state.subscribedCommunities = this.isoData.routeData[2].communities;
}
this.state.loading = false;
} else {
this.fetchTrendingCommunities();
this.fetchData();
if (UserService.Instance.user) {
if (UserService.Instance.localUserView) {
WebSocketService.Instance.send(
wsClient.getFollowedCommunities({
auth: authField(),
@ -194,15 +194,17 @@ export class Main extends Component<any, MainState> {
// TODO figure out auth default_listingType, default_sort_type
let type_: ListingType = pathSplit[5]
? ListingType[pathSplit[5]]
: UserService.Instance.user
: UserService.Instance.localUserView
? Object.values(ListingType)[
UserService.Instance.user.default_listing_type
UserService.Instance.localUserView.local_user.default_listing_type
]
: ListingType.Local;
let sort: SortType = pathSplit[7]
? SortType[pathSplit[7]]
: UserService.Instance.user
? Object.values(SortType)[UserService.Instance.user.default_sort_type]
: UserService.Instance.localUserView
? Object.values(SortType)[
UserService.Instance.localUserView.local_user.default_sort_type
]
: SortType.Active;
let page = pathSplit[9] ? Number(pathSplit[9]) : 1;
@ -215,6 +217,7 @@ export class Main extends Component<any, MainState> {
limit: fetchLimit,
sort,
type_,
saved_only: false,
};
setOptionalAuth(getPostsForm, req.auth);
promises.push(req.client.getPosts(getPostsForm));
@ -224,6 +227,7 @@ export class Main extends Component<any, MainState> {
limit: fetchLimit,
sort,
type_,
saved_only: false,
};
setOptionalAuth(getCommentsForm, req.auth);
promises.push(req.client.getComments(getCommentsForm));
@ -294,7 +298,7 @@ export class Main extends Component<any, MainState> {
</div>
</div>
{UserService.Instance.user &&
{UserService.Instance.localUserView &&
this.state.subscribedCommunities.length > 0 && (
<div class="card border-secondary mb-3">
<div class="card-body">{this.subscribedCommunities()}</div>
@ -413,7 +417,7 @@ export class Main extends Component<any, MainState> {
<li class="list-inline-item">{i18n.t("admins")}:</li>
{this.state.siteRes.admins.map(av => (
<li class="list-inline-item">
<UserListing user={av.user} />
<PersonListing person={av.person} />
</li>
))}
</ul>
@ -609,7 +613,7 @@ export class Main extends Component<any, MainState> {
<Icon icon="rss" classes="text-muted small" />
</a>
)}
{UserService.Instance.user &&
{UserService.Instance.localUserView &&
this.state.listingType == ListingType.Subscribed && (
<a
href={`/feeds/front/${UserService.Instance.auth}.xml?sort=${this.state.sort}`}
@ -652,10 +656,10 @@ export class Main extends Component<any, MainState> {
get canAdmin(): boolean {
return (
UserService.Instance.user &&
UserService.Instance.localUserView &&
this.state.siteRes.admins
.map(a => a.user.id)
.includes(UserService.Instance.user.id)
.map(a => a.person.id)
.includes(UserService.Instance.localUserView.person.id)
);
}
@ -701,6 +705,7 @@ export class Main extends Component<any, MainState> {
limit: fetchLimit,
sort: this.state.sort,
type_: this.state.listingType,
saved_only: false,
auth: authField(false),
};
WebSocketService.Instance.send(wsClient.getPosts(getPostsForm));
@ -710,6 +715,7 @@ export class Main extends Component<any, MainState> {
limit: fetchLimit,
sort: this.state.sort,
type_: this.state.listingType,
saved_only: false,
auth: authField(false),
};
WebSocketService.Instance.send(wsClient.getComments(getCommentsForm));
@ -755,8 +761,8 @@ export class Main extends Component<any, MainState> {
let nsfwCheck =
!nsfw ||
(nsfw &&
UserService.Instance.user &&
UserService.Instance.user.show_nsfw);
UserService.Instance.localUserView &&
UserService.Instance.localUserView.local_user.show_nsfw);
// Only push these if you're on the first page, and you pass the nsfw check
if (this.state.page == 1 && nsfwCheck) {
@ -801,23 +807,23 @@ export class Main extends Component<any, MainState> {
let data = wsJsonToRes<AddAdminResponse>(msg).data;
this.state.siteRes.admins = data.admins;
this.setState(this.state);
} else if (op == UserOperation.BanUser) {
let data = wsJsonToRes<BanUserResponse>(msg).data;
} else if (op == UserOperation.BanPerson) {
let data = wsJsonToRes<BanPersonResponse>(msg).data;
let found = this.state.siteRes.banned.find(
u => (u.user.id = data.user_view.user.id)
p => (p.person.id = data.person_view.person.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.user.id !== data.user_view.user.id
i => i.person.id !== data.person_view.person.id
);
} else {
this.state.siteRes.banned.push(data.user_view);
this.state.siteRes.banned.push(data.person_view);
}
this.state.posts
.filter(p => p.creator.id == data.user_view.user.id)
.filter(p => p.creator.id == data.person_view.person.id)
.forEach(p => (p.creator.banned = data.banned));
this.setState(this.state);

View file

@ -206,7 +206,9 @@ export class MarkdownTextArea extends Component<
<form class="btn btn-sm text-muted font-weight-bold">
<label
htmlFor={`file-upload-${this.id}`}
className={`mb-0 ${UserService.Instance.user && "pointer"}`}
className={`mb-0 ${
UserService.Instance.localUserView && "pointer"
}`}
data-tippy-content={i18n.t("upload_image")}
>
{this.state.imageLoading ? (
@ -221,7 +223,7 @@ export class MarkdownTextArea extends Component<
accept="image/*,video/*"
name="file"
class="d-none"
disabled={!UserService.Instance.user}
disabled={!UserService.Instance.localUserView}
onChange={linkEvent(this, this.handleImageUpload)}
/>
</form>

View file

@ -32,7 +32,7 @@ import { HtmlTags } from "./html-tags";
import moment from "moment";
import { i18n } from "../i18next";
import { InitialFetchRequest } from "shared/interfaces";
import { UserListing } from "./user-listing";
import { PersonListing } from "./person-listing";
import { CommunityLink } from "./community-link";
import { Spinner } from "./icon";
@ -252,7 +252,7 @@ export class Modlog extends Component<any, ModlogState> {
</span>,
<span>
{" "}
by <UserListing user={mrc.commenter} />
by <PersonListing person={mrc.commenter} />
</span>,
mrc.mod_remove_comment.reason &&
` reason: ${mrc.mod_remove_comment.reason}`,
@ -280,7 +280,7 @@ export class Modlog extends Component<any, ModlogState> {
{mbfc.mod_ban_from_community.banned ? "Banned " : "Unbanned "}{" "}
</span>,
<span>
<UserListing user={mbfc.banned_user} />
<PersonListing person={mbfc.banned_person} />
</span>,
<span> from the community </span>,
<span>
@ -305,7 +305,7 @@ export class Modlog extends Component<any, ModlogState> {
{mac.mod_add_community.removed ? "Removed " : "Appointed "}{" "}
</span>,
<span>
<UserListing user={mac.modded_user} />
<PersonListing person={mac.modded_person} />
</span>,
<span> as a mod to the community </span>,
<span>
@ -318,7 +318,7 @@ export class Modlog extends Component<any, ModlogState> {
return [
<span>{mb.mod_ban.banned ? "Banned " : "Unbanned "} </span>,
<span>
<UserListing user={mb.banned_user} />
<PersonListing person={mb.banned_person} />
</span>,
<div>{mb.mod_ban.reason && ` reason: ${mb.mod_ban.reason}`}</div>,
<div>
@ -332,7 +332,7 @@ export class Modlog extends Component<any, ModlogState> {
return [
<span>{ma.mod_add.removed ? "Removed " : "Appointed "} </span>,
<span>
<UserListing user={ma.modded_user} />
<PersonListing person={ma.modded_person} />
</span>,
<span> as an admin </span>,
];
@ -353,7 +353,7 @@ export class Modlog extends Component<any, ModlogState> {
<MomentTime data={i} />
</td>
<td>
<UserListing user={i.view.moderator} />
<PersonListing person={i.view.moderator} />
</td>
<td>{this.renderModlogType(i)}</td>
</tr>

View file

@ -6,8 +6,8 @@ import {
UserOperation,
GetReplies,
GetRepliesResponse,
GetUserMentions,
GetUserMentionsResponse,
GetPersonMentions,
GetPersonMentionsResponse,
GetPrivateMessages,
PrivateMessagesResponse,
SortType,
@ -174,7 +174,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
// TODO class active corresponding to current page
navbar() {
let user = UserService.Instance.user || this.props.site_res.my_user;
let localUserView =
UserService.Instance.localUserView || this.props.site_res.my_user;
return (
<nav class="navbar navbar-expand-lg navbar-light shadow-sm p-0 px-3">
<div class="container">
@ -261,7 +262,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
title={i18n.t("support_lemmy")}
href={supportLemmyUrl}
>
<Icon icon="beer" classes="small" />
<Icon icon="heart" classes="small" />
</a>
</li>
</ul>
@ -338,16 +339,16 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
<li className="nav-item">
<Link
className="nav-link"
to={`/u/${user.name}`}
to={`/u/${localUserView.person.name}`}
title={i18n.t("settings")}
>
<span>
{user.avatar && showAvatars() && (
<PictrsImage src={user.avatar} icon />
{localUserView.person.avatar && showAvatars() && (
<PictrsImage src={localUserView.person.avatar} icon />
)}
{user.preferred_username
? user.preferred_username
: user.name}
{localUserView.person.preferred_username
? localUserView.person.preferred_username
: localUserView.person.name}
</span>
</Link>
</li>
@ -400,8 +401,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
this.state.unreadCount = this.calculateUnreadCount();
this.setState(this.state);
this.sendUnreadCount();
} else if (op == UserOperation.GetUserMentions) {
let data = wsJsonToRes<GetUserMentionsResponse>(msg).data;
} else if (op == UserOperation.GetPersonMentions) {
let data = wsJsonToRes<GetPersonMentionsResponse>(msg).data;
let unreadMentions = data.mentions.filter(r => !r.comment.read);
this.state.mentions = unreadMentions;
@ -422,8 +423,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
// This is only called on a successful login
let data = wsJsonToRes<GetSiteResponse>(msg).data;
console.log(data.my_user);
UserService.Instance.user = data.my_user;
setTheme(UserService.Instance.user.theme);
UserService.Instance.localUserView = data.my_user;
setTheme(UserService.Instance.localUserView.local_user.theme);
i18n.changeLanguage(getLanguage());
this.state.isLoggedIn = true;
this.setState(this.state);
@ -431,7 +432,11 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
let data = wsJsonToRes<CommentResponse>(msg).data;
if (this.state.isLoggedIn) {
if (data.recipient_ids.includes(UserService.Instance.user.id)) {
if (
data.recipient_ids.includes(
UserService.Instance.localUserView.local_user.id
)
) {
this.state.replies.push(data.comment_view);
this.state.unreadCount++;
this.setState(this.state);
@ -444,7 +449,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
if (this.state.isLoggedIn) {
if (
data.private_message_view.recipient.id == UserService.Instance.user.id
data.private_message_view.recipient.id ==
UserService.Instance.localUserView.person.id
) {
this.state.messages.push(data.private_message_view);
this.state.unreadCount++;
@ -466,7 +472,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
auth: authField(),
};
let userMentionsForm: GetUserMentions = {
let personMentionsForm: GetPersonMentions = {
sort: SortType.New,
unread_only: true,
page: 1,
@ -484,7 +490,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
if (this.currentLocation !== "/inbox") {
WebSocketService.Instance.send(wsClient.getReplies(repliesForm));
WebSocketService.Instance.send(
wsClient.getUserMentions(userMentionsForm)
wsClient.getPersonMentions(personMentionsForm)
);
WebSocketService.Instance.send(
wsClient.getPrivateMessages(privateMessagesForm)
@ -510,15 +516,15 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
get canAdmin(): boolean {
return (
UserService.Instance.user &&
UserService.Instance.localUserView &&
this.props.site_res.admins
.map(a => a.user.id)
.includes(UserService.Instance.user.id)
.map(a => a.person.id)
.includes(UserService.Instance.localUserView.person.id)
);
}
requestNotificationPermission() {
if (UserService.Instance.user) {
if (UserService.Instance.localUserView) {
document.addEventListener("DOMContentLoaded", function () {
if (!Notification) {
toast(i18n.t("notifications_error"), "danger");

View file

@ -4,23 +4,23 @@ import {
PostView,
CommentView,
SortType,
GetUserDetailsResponse,
UserViewSafe,
GetPersonDetailsResponse,
PersonViewSafe,
} from "lemmy-js-client";
import { UserDetailsView } from "../interfaces";
import { PersonDetailsView } from "../interfaces";
import { commentsToFlatNodes, setupTippy } from "../utils";
import { PostListing } from "./post-listing";
import { CommentNodes } from "./comment-nodes";
interface UserDetailsProps {
userRes: GetUserDetailsResponse;
admins: UserViewSafe[];
interface PersonDetailsProps {
personRes: GetPersonDetailsResponse;
admins: PersonViewSafe[];
page: number;
limit: number;
sort: SortType;
enableDownvotes: boolean;
enableNsfw: boolean;
view: UserDetailsView;
view: PersonDetailsView;
onPageChange(page: number): number | any;
}
@ -36,7 +36,7 @@ type ItemType = {
score: number;
};
export class UserDetails extends Component<UserDetailsProps, any> {
export class PersonDetails extends Component<PersonDetailsProps, any> {
constructor(props: any, context: any) {
super(props, context);
}
@ -65,12 +65,15 @@ export class UserDetails extends Component<UserDetailsProps, any> {
);
}
viewSelector(view: UserDetailsView) {
if (view === UserDetailsView.Overview || view === UserDetailsView.Saved) {
viewSelector(view: PersonDetailsView) {
if (
view === PersonDetailsView.Overview ||
view === PersonDetailsView.Saved
) {
return this.overview();
} else if (view === UserDetailsView.Comments) {
} else if (view === PersonDetailsView.Comments) {
return this.comments();
} else if (view === UserDetailsView.Posts) {
} else if (view === PersonDetailsView.Posts) {
return this.posts();
} else {
return null;
@ -114,14 +117,14 @@ export class UserDetails extends Component<UserDetailsProps, any> {
overview() {
let id = 0;
let comments: ItemType[] = this.props.userRes.comments.map(r => ({
let comments: ItemType[] = this.props.personRes.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 => ({
let posts: ItemType[] = this.props.personRes.posts.map(r => ({
id: id++,
type_: ItemEnum.Post,
view: r,
@ -149,7 +152,7 @@ export class UserDetails extends Component<UserDetailsProps, any> {
return (
<div>
<CommentNodes
nodes={commentsToFlatNodes(this.props.userRes.comments)}
nodes={commentsToFlatNodes(this.props.personRes.comments)}
admins={this.props.admins}
noIndent
showCommunity
@ -163,7 +166,7 @@ export class UserDetails extends Component<UserDetailsProps, any> {
posts() {
return (
<div>
{this.props.userRes.posts.map(post => (
{this.props.personRes.posts.map(post => (
<>
<PostListing
post_view={post}
@ -190,7 +193,8 @@ export class UserDetails extends Component<UserDetailsProps, any> {
{i18n.t("prev")}
</button>
)}
{this.props.userRes.comments.length + this.props.userRes.posts.length >
{this.props.personRes.comments.length +
this.props.personRes.posts.length >
0 && (
<button
class="btn btn-secondary"
@ -203,11 +207,11 @@ export class UserDetails extends Component<UserDetailsProps, any> {
);
}
nextPage(i: UserDetails) {
nextPage(i: PersonDetails) {
i.props.onPageChange(i.props.page + 1);
}
prevPage(i: UserDetails) {
prevPage(i: PersonDetails) {
i.props.onPageChange(i.props.page - 1);
}
}

View file

@ -1,12 +1,12 @@
import { Component } from "inferno";
import { Link } from "inferno-router";
import { UserSafe } from "lemmy-js-client";
import { PersonSafe } from "lemmy-js-client";
import { showAvatars, hostname, isCakeDay } from "../utils";
import { CakeDay } from "./cake-day";
import { PictrsImage } from "./pictrs-image";
interface UserListingProps {
user: UserSafe;
interface PersonListingProps {
person: PersonSafe;
realLink?: boolean;
useApubName?: boolean;
muted?: boolean;
@ -14,31 +14,31 @@ interface UserListingProps {
showApubName?: boolean;
}
export class UserListing extends Component<UserListingProps, any> {
export class PersonListing extends Component<PersonListingProps, any> {
constructor(props: any, context: any) {
super(props, context);
}
render() {
let user = this.props.user;
let local = user.local == null ? true : user.local;
let person = this.props.person;
let local = person.local == null ? true : person.local;
let apubName: string, link: string;
if (local) {
apubName = `@${user.name}`;
link = `/u/${user.name}`;
apubName = `@${person.name}`;
link = `/u/${person.name}`;
} else {
apubName = `@${user.name}@${hostname(user.actor_id)}`;
link = !this.props.realLink ? `/user/${user.id}` : user.actor_id;
apubName = `@${person.name}@${hostname(person.actor_id)}`;
link = !this.props.realLink ? `/user/${person.id}` : person.actor_id;
}
let displayName = this.props.useApubName
? apubName
: user.preferred_username
? user.preferred_username
: person.preferred_username
? person.preferred_username
: apubName;
if (this.props.showApubName && !local && user.preferred_username) {
if (this.props.showApubName && !local && person.preferred_username) {
displayName = `${displayName} (${apubName})`;
}
@ -49,13 +49,13 @@ export class UserListing extends Component<UserListingProps, any> {
className={this.props.muted ? "text-muted" : "text-info"}
to={link}
>
{!this.props.hideAvatar && user.avatar && showAvatars() && (
<PictrsImage src={user.avatar} icon />
{!this.props.hideAvatar && person.avatar && showAvatars() && (
<PictrsImage src={person.avatar} icon />
)}
<span>{displayName}</span>
</Link>
{isCakeDay(user.published) && <CakeDay creatorName={apubName} />}
{isCakeDay(person.published) && <CakeDay creatorName={apubName} />}
</>
);
}

View file

@ -1,6 +1,7 @@
import { Component, linkEvent } from "inferno";
import { Link } from "inferno-router";
import { Subscription } from "rxjs";
import ISO6391 from "iso-639-1";
import {
UserOperation,
SortType,
@ -9,14 +10,14 @@ import {
LoginResponse,
DeleteAccount,
GetSiteResponse,
GetUserDetailsResponse,
GetPersonDetailsResponse,
AddAdminResponse,
GetUserDetails,
GetPersonDetails,
CommentResponse,
PostResponse,
BanUserResponse,
BanPersonResponse,
} from "lemmy-js-client";
import { InitialFetchRequest, UserDetailsView } from "../interfaces";
import { InitialFetchRequest, PersonDetailsView } from "../interfaces";
import { WebSocketService, UserService } from "../services";
import {
wsJsonToRes,
@ -48,25 +49,25 @@ import {
saveScrollPosition,
restoreScrollPosition,
} from "../utils";
import { UserListing } from "./user-listing";
import { PersonListing } from "./person-listing";
import { HtmlTags } from "./html-tags";
import { SortSelect } from "./sort-select";
import { ListingTypeSelect } from "./listing-type-select";
import { MomentTime } from "./moment-time";
import { i18n } from "../i18next";
import moment from "moment";
import { UserDetails } from "./user-details";
import { PersonDetails } from "./person-details";
import { MarkdownTextArea } from "./markdown-textarea";
import { Icon, Spinner } from "./icon";
import { ImageUploadForm } from "./image-upload-form";
import { BannerIconHeader } from "./banner-icon-header";
import { CommunityLink } from "./community-link";
interface UserState {
userRes: GetUserDetailsResponse;
userId: number;
interface PersonState {
personRes: GetPersonDetailsResponse;
personId: number;
userName: string;
view: UserDetailsView;
view: PersonDetailsView;
sort: SortType;
page: number;
loading: boolean;
@ -78,11 +79,11 @@ interface UserState {
siteRes: GetSiteResponse;
}
interface UserProps {
view: UserDetailsView;
interface PersonProps {
view: PersonDetailsView;
sort: SortType;
page: number;
user_id: number | null;
person_id: number | null;
username: string;
}
@ -92,17 +93,17 @@ interface UrlParams {
page?: number;
}
export class User extends Component<any, UserState> {
export class Person extends Component<any, PersonState> {
private isoData = setIsoData(this.context);
private subscription: Subscription;
private emptyState: UserState = {
userRes: undefined,
userId: getIdFromProps(this.props),
private emptyState: PersonState = {
personRes: undefined,
personId: getIdFromProps(this.props),
userName: getUsernameFromProps(this.props),
loading: true,
view: User.getViewFromProps(this.props.match.view),
sort: User.getSortTypeFromProps(this.props.match.sort),
page: User.getPageFromProps(this.props.match.page),
view: Person.getViewFromProps(this.props.match.view),
sort: Person.getSortTypeFromProps(this.props.match.sort),
page: Person.getPageFromProps(this.props.match.page),
userSettingsForm: {
show_nsfw: null,
theme: null,
@ -152,7 +153,7 @@ export class User extends Component<any, UserState> {
// Only fetch the data if coming from another route
if (this.isoData.path == this.context.router.route.match.url) {
this.state.userRes = this.isoData.routeData[0];
this.state.personRes = this.isoData.routeData[0];
this.setUserInfo();
this.state.loading = false;
} else {
@ -163,27 +164,27 @@ export class User extends Component<any, UserState> {
}
fetchUserData() {
let form: GetUserDetails = {
user_id: this.state.userId,
let form: GetPersonDetails = {
person_id: this.state.personId,
username: this.state.userName,
sort: this.state.sort,
saved_only: this.state.view === UserDetailsView.Saved,
saved_only: this.state.view === PersonDetailsView.Saved,
page: this.state.page,
limit: fetchLimit,
auth: authField(false),
};
WebSocketService.Instance.send(wsClient.getUserDetails(form));
WebSocketService.Instance.send(wsClient.getPersonDetails(form));
}
get isCurrentUser() {
return (
UserService.Instance.user &&
UserService.Instance.user.id == this.state.userRes.user_view.user.id
UserService.Instance.localUserView?.person.id ==
this.state.personRes.person_view.person.id
);
}
static getViewFromProps(view: string): UserDetailsView {
return view ? UserDetailsView[view] : UserDetailsView.Overview;
static getViewFromProps(view: string): PersonDetailsView {
return view ? PersonDetailsView[view] : PersonDetailsView.Overview;
}
static getSortTypeFromProps(sort: string): SortType {
@ -200,33 +201,33 @@ export class User extends Component<any, UserState> {
// It can be /u/me, or /username/1
let idOrName = pathSplit[2];
let user_id: number;
let person_id: number;
let username: string;
if (isNaN(Number(idOrName))) {
username = idOrName;
} else {
user_id = Number(idOrName);
person_id = Number(idOrName);
}
let view = this.getViewFromProps(pathSplit[4]);
let sort = this.getSortTypeFromProps(pathSplit[6]);
let page = this.getPageFromProps(Number(pathSplit[8]));
let form: GetUserDetails = {
let form: GetPersonDetails = {
sort,
saved_only: view === UserDetailsView.Saved,
saved_only: view === PersonDetailsView.Saved,
page,
limit: fetchLimit,
};
setOptionalAuth(form, req.auth);
this.setIdOrName(form, user_id, username);
promises.push(req.client.getUserDetails(form));
this.setIdOrName(form, person_id, username);
promises.push(req.client.getPersonDetails(form));
return promises;
}
static setIdOrName(obj: any, id: number, name_: string) {
if (id) {
obj.user_id = id;
obj.person_id = id;
} else {
obj.username = name_;
}
@ -237,12 +238,12 @@ export class User extends Component<any, UserState> {
saveScrollPosition(this.context);
}
static getDerivedStateFromProps(props: any): UserProps {
static getDerivedStateFromProps(props: any): PersonProps {
return {
view: this.getViewFromProps(props.match.params.view),
sort: this.getSortTypeFromProps(props.match.params.sort),
page: this.getPageFromProps(props.match.params.page),
user_id: Number(props.match.params.id) || null,
person_id: Number(props.match.params.id) || null,
username: props.match.params.username,
};
}
@ -259,12 +260,12 @@ export class User extends Component<any, UserState> {
}
get documentTitle(): string {
return `@${this.state.userRes.user_view.user.name} - ${this.state.siteRes.site_view.site.name}`;
return `@${this.state.personRes.person_view.person.name} - ${this.state.siteRes.site_view.site.name}`;
}
get bioTag(): string {
return this.state.userRes.user_view.user.bio
? previewLines(this.state.userRes.user_view.user.bio)
return this.state.personRes.person_view.person.bio
? previewLines(this.state.personRes.person_view.person.bio)
: undefined;
}
@ -283,14 +284,14 @@ 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_view.user.avatar}
image={this.state.personRes.person_view.person.avatar}
/>
{this.userInfo()}
<hr />
</>
{!this.state.loading && this.selects()}
<UserDetails
userRes={this.state.userRes}
<PersonDetails
personRes={this.state.personRes}
admins={this.state.siteRes.admins}
sort={this.state.sort}
page={this.state.page}
@ -322,52 +323,52 @@ export class User extends Component<any, UserState> {
<div class="btn-group btn-group-toggle flex-wrap mb-2">
<label
className={`btn btn-outline-secondary pointer
${this.state.view == UserDetailsView.Overview && "active"}
${this.state.view == PersonDetailsView.Overview && "active"}
`}
>
<input
type="radio"
value={UserDetailsView.Overview}
checked={this.state.view === UserDetailsView.Overview}
value={PersonDetailsView.Overview}
checked={this.state.view === PersonDetailsView.Overview}
onChange={linkEvent(this, this.handleViewChange)}
/>
{i18n.t("overview")}
</label>
<label
className={`btn btn-outline-secondary pointer
${this.state.view == UserDetailsView.Comments && "active"}
${this.state.view == PersonDetailsView.Comments && "active"}
`}
>
<input
type="radio"
value={UserDetailsView.Comments}
checked={this.state.view == UserDetailsView.Comments}
value={PersonDetailsView.Comments}
checked={this.state.view == PersonDetailsView.Comments}
onChange={linkEvent(this, this.handleViewChange)}
/>
{i18n.t("comments")}
</label>
<label
className={`btn btn-outline-secondary pointer
${this.state.view == UserDetailsView.Posts && "active"}
${this.state.view == PersonDetailsView.Posts && "active"}
`}
>
<input
type="radio"
value={UserDetailsView.Posts}
checked={this.state.view == UserDetailsView.Posts}
value={PersonDetailsView.Posts}
checked={this.state.view == PersonDetailsView.Posts}
onChange={linkEvent(this, this.handleViewChange)}
/>
{i18n.t("posts")}
</label>
<label
className={`btn btn-outline-secondary pointer
${this.state.view == UserDetailsView.Saved && "active"}
${this.state.view == PersonDetailsView.Saved && "active"}
`}
>
<input
type="radio"
value={UserDetailsView.Saved}
checked={this.state.view == UserDetailsView.Saved}
value={PersonDetailsView.Saved}
checked={this.state.view == PersonDetailsView.Saved}
onChange={linkEvent(this, this.handleViewChange)}
/>
{i18n.t("saved")}
@ -398,29 +399,29 @@ export class User extends Component<any, UserState> {
}
userInfo() {
let uv = this.state.userRes?.user_view;
let pv = this.state.personRes?.person_view;
return (
<div>
<BannerIconHeader banner={uv.user.banner} icon={uv.user.avatar} />
<BannerIconHeader banner={pv.person.banner} icon={pv.person.avatar} />
<div class="mb-3">
<div class="">
<div class="mb-0 d-flex flex-wrap">
<div>
{uv.user.preferred_username && (
<h5 class="mb-0">{uv.user.preferred_username}</h5>
{pv.person.preferred_username && (
<h5 class="mb-0">{pv.person.preferred_username}</h5>
)}
<ul class="list-inline mb-2">
<li className="list-inline-item">
<UserListing
user={uv.user}
<PersonListing
person={pv.person}
realLink
useApubName
muted
hideAvatar
/>
</li>
{uv.user.banned && (
{pv.person.banned && (
<li className="list-inline-item badge badge-danger">
{i18n.t("banned")}
</li>
@ -437,53 +438,56 @@ export class User extends Component<any, UserState> {
</button>
) : (
<>
{/* TODO matrix ids aren't currently federated, so don't come back with GetPersonDetails
<a
className={`d-flex align-self-start btn btn-secondary mr-2 ${
!uv.user.matrix_user_id && "invisible"
!pv.local_user.matrix_user_id && "invisible"
}`}
rel="noopener"
href={`https://matrix.to/#/${uv.user.matrix_user_id}`}
href={`https://matrix.to/#/${pv.local_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/${uv.user.id}`}
to={`/create_private_message/recipient/${pv.person.id}`}
>
{i18n.t("send_message")}
</Link>
</>
)}
</div>
{uv.user.bio && (
{pv.person.bio && (
<div className="d-flex align-items-center mb-2">
<div
className="md-div"
dangerouslySetInnerHTML={mdToHtml(uv.user.bio)}
dangerouslySetInnerHTML={mdToHtml(pv.person.bio)}
/>
</div>
)}
<div>
<ul class="list-inline mb-2">
<li className="list-inline-item badge badge-light">
{i18n.t("number_of_posts", { count: uv.counts.post_count })}
{i18n.t("number_of_posts", { count: pv.counts.post_count })}
</li>
<li className="list-inline-item badge badge-light">
{i18n.t("number_of_comments", {
count: uv.counts.comment_count,
count: pv.counts.comment_count,
})}
</li>
</ul>
</div>
<div class="text-muted">
{i18n.t("joined")}{" "}
<MomentTime data={uv.user} showAgo ignoreUpdated />
<MomentTime data={pv.person} showAgo ignoreUpdated />
</div>
<div className="d-flex align-items-center text-muted mb-2">
<Icon icon="cake" />
<span className="ml-2">
{i18n.t("cake_day_title")}{" "}
{moment.utc(uv.user.published).local().format("MMM DD, YYYY")}
{moment.utc(pv.person.published).local().format("MMM DD, YYYY")}
</span>
</div>
</div>
@ -533,8 +537,10 @@ export class User extends Component<any, UserState> {
<option disabled aria-hidden="true">
</option>
{languages.map(lang => (
<option value={lang.code}>{lang.name}</option>
{languages.sort().map(lang => (
<option value={lang.code}>
{ISO6391.getNativeName(lang.code) || lang.code}
</option>
))}
</select>
</div>
@ -557,7 +563,7 @@ export class User extends Component<any, UserState> {
</div>
<form className="form-group">
<label>
<div class="mr-2">{i18n.t("sort_type")}</div>
<div class="mr-2">{i18n.t("type")}</div>
</label>
<ListingTypeSelect
type_={
@ -573,7 +579,7 @@ export class User extends Component<any, UserState> {
</form>
<form className="form-group">
<label>
<div class="mr-2">{i18n.t("type")}</div>
<div class="mr-2">{i18n.t("sort_type")}</div>
</label>
<SortSelect
sort={
@ -845,12 +851,12 @@ export class User extends Component<any, UserState> {
moderates() {
return (
<div>
{this.state.userRes.moderates.length > 0 && (
{this.state.personRes.moderates.length > 0 && (
<div class="card border-secondary mb-3">
<div class="card-body">
<h5>{i18n.t("moderates")}</h5>
<ul class="list-unstyled mb-0">
{this.state.userRes.moderates.map(cmv => (
{this.state.personRes.moderates.map(cmv => (
<li>
<CommunityLink community={cmv.community} />
</li>
@ -866,12 +872,12 @@ export class User extends Component<any, UserState> {
follows() {
return (
<div>
{this.state.userRes.follows.length > 0 && (
{this.state.personRes.follows.length > 0 && (
<div class="card border-secondary mb-3">
<div class="card-body">
<h5>{i18n.t("subscribed")}</h5>
<ul class="list-unstyled mb-0">
{this.state.userRes.follows.map(cfv => (
{this.state.personRes.follows.map(cfv => (
<li>
<CommunityLink community={cfv.community} />
</li>
@ -886,12 +892,12 @@ export class User extends Component<any, UserState> {
updateUrl(paramUpdates: UrlParams) {
const page = paramUpdates.page || this.state.page;
const viewStr = paramUpdates.view || UserDetailsView[this.state.view];
const viewStr = paramUpdates.view || PersonDetailsView[this.state.view];
const sortStr = paramUpdates.sort || this.state.sort;
let typeView = this.state.userName
? `/u/${this.state.userName}`
: `/user/${this.state.userId}`;
: `/user/${this.state.personId}`;
this.props.history.push(
`${typeView}/view/${viewStr}/sort/${sortStr}/page/${page}`
@ -909,36 +915,37 @@ export class User extends Component<any, UserState> {
this.updateUrl({ sort: val, page: 1 });
}
handleViewChange(i: User, event: any) {
handleViewChange(i: Person, event: any) {
i.updateUrl({
view: UserDetailsView[Number(event.target.value)],
view: PersonDetailsView[Number(event.target.value)],
page: 1,
});
}
handleUserSettingsShowNsfwChange(i: User, event: any) {
handleUserSettingsShowNsfwChange(i: Person, event: any) {
i.state.userSettingsForm.show_nsfw = event.target.checked;
i.setState(i.state);
}
handleUserSettingsShowAvatarsChange(i: User, event: any) {
handleUserSettingsShowAvatarsChange(i: Person, event: any) {
i.state.userSettingsForm.show_avatars = event.target.checked;
UserService.Instance.user.show_avatars = event.target.checked; // Just for instant updates
UserService.Instance.localUserView.local_user.show_avatars =
event.target.checked; // Just for instant updates
i.setState(i.state);
}
handleUserSettingsSendNotificationsToEmailChange(i: User, event: any) {
handleUserSettingsSendNotificationsToEmailChange(i: Person, event: any) {
i.state.userSettingsForm.send_notifications_to_email = event.target.checked;
i.setState(i.state);
}
handleUserSettingsThemeChange(i: User, event: any) {
handleUserSettingsThemeChange(i: Person, event: any) {
i.state.userSettingsForm.theme = event.target.value;
setTheme(event.target.value, true);
i.setState(i.state);
}
handleUserSettingsLangChange(i: User, event: any) {
handleUserSettingsLangChange(i: Person, event: any) {
i.state.userSettingsForm.lang = event.target.value;
i18n.changeLanguage(getLanguage(i.state.userSettingsForm.lang));
i.setState(i.state);
@ -958,7 +965,7 @@ export class User extends Component<any, UserState> {
this.setState(this.state);
}
handleUserSettingsEmailChange(i: User, event: any) {
handleUserSettingsEmailChange(i: Person, event: any) {
i.state.userSettingsForm.email = event.target.value;
i.setState(i.state);
}
@ -988,23 +995,23 @@ export class User extends Component<any, UserState> {
this.setState(this.state);
}
handleUserSettingsPreferredUsernameChange(i: User, event: any) {
handleUserSettingsPreferredUsernameChange(i: Person, event: any) {
i.state.userSettingsForm.preferred_username = event.target.value;
i.setState(i.state);
}
handleUserSettingsMatrixUserIdChange(i: User, event: any) {
handleUserSettingsMatrixUserIdChange(i: Person, event: any) {
i.state.userSettingsForm.matrix_user_id = event.target.value;
if (
i.state.userSettingsForm.matrix_user_id == "" &&
!i.state.userRes.user_view.user.matrix_user_id
!UserService.Instance.localUserView.person.matrix_user_id
) {
i.state.userSettingsForm.matrix_user_id = undefined;
}
i.setState(i.state);
}
handleUserSettingsNewPasswordChange(i: User, event: any) {
handleUserSettingsNewPasswordChange(i: Person, event: any) {
i.state.userSettingsForm.new_password = event.target.value;
if (i.state.userSettingsForm.new_password == "") {
i.state.userSettingsForm.new_password = undefined;
@ -1012,7 +1019,7 @@ export class User extends Component<any, UserState> {
i.setState(i.state);
}
handleUserSettingsNewPasswordVerifyChange(i: User, event: any) {
handleUserSettingsNewPasswordVerifyChange(i: Person, event: any) {
i.state.userSettingsForm.new_password_verify = event.target.value;
if (i.state.userSettingsForm.new_password_verify == "") {
i.state.userSettingsForm.new_password_verify = undefined;
@ -1020,7 +1027,7 @@ export class User extends Component<any, UserState> {
i.setState(i.state);
}
handleUserSettingsOldPasswordChange(i: User, event: any) {
handleUserSettingsOldPasswordChange(i: Person, event: any) {
i.state.userSettingsForm.old_password = event.target.value;
if (i.state.userSettingsForm.old_password == "") {
i.state.userSettingsForm.old_password = undefined;
@ -1028,7 +1035,7 @@ export class User extends Component<any, UserState> {
i.setState(i.state);
}
handleUserSettingsSubmit(i: User, event: any) {
handleUserSettingsSubmit(i: Person, event: any) {
event.preventDefault();
i.state.userSettingsLoading = true;
i.setState(i.state);
@ -1038,23 +1045,23 @@ export class User extends Component<any, UserState> {
);
}
handleDeleteAccountShowConfirmToggle(i: User, event: any) {
handleDeleteAccountShowConfirmToggle(i: Person, event: any) {
event.preventDefault();
i.state.deleteAccountShowConfirm = !i.state.deleteAccountShowConfirm;
i.setState(i.state);
}
handleDeleteAccountPasswordChange(i: User, event: any) {
handleDeleteAccountPasswordChange(i: Person, event: any) {
i.state.deleteAccountForm.password = event.target.value;
i.setState(i.state);
}
handleLogoutClick(i: User) {
handleLogoutClick(i: Person) {
UserService.Instance.logout();
i.context.router.history.push("/");
}
handleDeleteAccount(i: User, event: any) {
handleDeleteAccount(i: Person, event: any) {
event.preventDefault();
i.state.deleteAccountLoading = true;
i.setState(i.state);
@ -1068,27 +1075,33 @@ export class User extends Component<any, UserState> {
setUserInfo() {
if (this.isCurrentUser) {
this.state.userSettingsForm.show_nsfw =
UserService.Instance.user.show_nsfw;
this.state.userSettingsForm.theme = UserService.Instance.user.theme
? UserService.Instance.user.theme
UserService.Instance.localUserView.local_user.show_nsfw;
this.state.userSettingsForm.theme = UserService.Instance.localUserView
.local_user.theme
? UserService.Instance.localUserView.local_user.theme
: "browser";
this.state.userSettingsForm.default_sort_type =
UserService.Instance.user.default_sort_type;
UserService.Instance.localUserView.local_user.default_sort_type;
this.state.userSettingsForm.default_listing_type =
UserService.Instance.user.default_listing_type;
this.state.userSettingsForm.lang = UserService.Instance.user.lang;
this.state.userSettingsForm.avatar = UserService.Instance.user.avatar;
this.state.userSettingsForm.banner = UserService.Instance.user.banner;
UserService.Instance.localUserView.local_user.default_listing_type;
this.state.userSettingsForm.lang =
UserService.Instance.localUserView.local_user.lang;
this.state.userSettingsForm.avatar =
UserService.Instance.localUserView.person.avatar;
this.state.userSettingsForm.banner =
UserService.Instance.localUserView.person.banner;
this.state.userSettingsForm.preferred_username =
UserService.Instance.user.preferred_username;
UserService.Instance.localUserView.person.preferred_username;
this.state.userSettingsForm.show_avatars =
UserService.Instance.user.show_avatars;
this.state.userSettingsForm.email = UserService.Instance.user.email;
this.state.userSettingsForm.bio = UserService.Instance.user.bio;
UserService.Instance.localUserView.local_user.show_avatars;
this.state.userSettingsForm.email =
UserService.Instance.localUserView.local_user.email;
this.state.userSettingsForm.bio =
UserService.Instance.localUserView.person.bio;
this.state.userSettingsForm.send_notifications_to_email =
UserService.Instance.user.send_notifications_to_email;
UserService.Instance.localUserView.local_user.send_notifications_to_email;
this.state.userSettingsForm.matrix_user_id =
UserService.Instance.user.matrix_user_id;
UserService.Instance.localUserView.person.matrix_user_id;
}
}
@ -1106,12 +1119,12 @@ export class User extends Component<any, UserState> {
return;
} else if (msg.reconnect) {
this.fetchUserData();
} else if (op == UserOperation.GetUserDetails) {
// Since the UserDetails contains posts/comments as well as some general user info we listen here as well
} else if (op == UserOperation.GetPersonDetails) {
// Since the PersonDetails 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
let data = wsJsonToRes<GetUserDetailsResponse>(msg).data;
this.state.userRes = data;
let data = wsJsonToRes<GetPersonDetailsResponse>(msg).data;
this.state.personRes = data;
this.setUserInfo();
this.state.loading = false;
this.setState(this.state);
@ -1119,10 +1132,10 @@ export class User extends Component<any, UserState> {
} else if (op == UserOperation.SaveUserSettings) {
let data = wsJsonToRes<LoginResponse>(msg).data;
UserService.Instance.login(data);
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.personRes.person_view.person.bio = this.state.userSettingsForm.bio;
this.state.personRes.person_view.person.preferred_username = this.state.userSettingsForm.preferred_username;
this.state.personRes.person_view.person.banner = this.state.userSettingsForm.banner;
this.state.personRes.person_view.person.avatar = this.state.userSettingsForm.avatar;
this.state.userSettingsLoading = false;
this.setState(this.state);
@ -1139,7 +1152,7 @@ export class User extends Component<any, UserState> {
this.setState(this.state);
} else if (op == UserOperation.CreateCommentLike) {
let data = wsJsonToRes<CommentResponse>(msg).data;
createCommentLikeRes(data.comment_view, this.state.userRes.comments);
createCommentLikeRes(data.comment_view, this.state.personRes.comments);
this.setState(this.state);
} else if (
op == UserOperation.EditComment ||
@ -1147,19 +1160,20 @@ export class User extends Component<any, UserState> {
op == UserOperation.RemoveComment
) {
let data = wsJsonToRes<CommentResponse>(msg).data;
editCommentRes(data.comment_view, this.state.userRes.comments);
editCommentRes(data.comment_view, this.state.personRes.comments);
this.setState(this.state);
} else if (op == UserOperation.CreateComment) {
let data = wsJsonToRes<CommentResponse>(msg).data;
if (
UserService.Instance.user &&
data.comment_view.creator.id == UserService.Instance.user.id
UserService.Instance.localUserView &&
data.comment_view.creator.id ==
UserService.Instance.localUserView.person.id
) {
toast(i18n.t("reply_sent"));
}
} else if (op == UserOperation.SaveComment) {
let data = wsJsonToRes<CommentResponse>(msg).data;
saveCommentRes(data.comment_view, this.state.userRes.comments);
saveCommentRes(data.comment_view, this.state.personRes.comments);
this.setState(this.state);
} else if (
op == UserOperation.EditPost ||
@ -1170,19 +1184,19 @@ export class User extends Component<any, UserState> {
op == UserOperation.SavePost
) {
let data = wsJsonToRes<PostResponse>(msg).data;
editPostFindRes(data.post_view, this.state.userRes.posts);
editPostFindRes(data.post_view, this.state.personRes.posts);
this.setState(this.state);
} else if (op == UserOperation.CreatePostLike) {
let data = wsJsonToRes<PostResponse>(msg).data;
createPostLikeFindRes(data.post_view, this.state.userRes.posts);
createPostLikeFindRes(data.post_view, this.state.personRes.posts);
this.setState(this.state);
} else if (op == UserOperation.BanUser) {
let data = wsJsonToRes<BanUserResponse>(msg).data;
this.state.userRes.comments
.filter(c => c.creator.id == data.user_view.user.id)
} else if (op == UserOperation.BanPerson) {
let data = wsJsonToRes<BanPersonResponse>(msg).data;
this.state.personRes.comments
.filter(c => c.creator.id == data.person_view.person.id)
.forEach(c => (c.creator.banned = data.banned));
this.state.userRes.posts
.filter(c => c.creator.id == data.user_view.user.id)
this.state.personRes.posts
.filter(c => c.creator.id == data.person_view.person.id)
.forEach(c => (c.creator.banned = data.banned));
this.setState(this.state);
}

View file

@ -191,7 +191,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
<label
htmlFor="file-upload"
className={`${
UserService.Instance.user && "pointer"
UserService.Instance.localUserView && "pointer"
} d-inline-block float-right text-muted font-weight-bold`}
data-tippy-content={i18n.t("upload_image")}
>
@ -203,7 +203,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
accept="image/*,video/*"
name="file"
class="d-none"
disabled={!UserService.Instance.user}
disabled={!UserService.Instance.localUserView}
onChange={linkEvent(this, this.handleImageUpload)}
/>
</form>
@ -613,13 +613,19 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
return;
} else if (op == UserOperation.CreatePost) {
let data = wsJsonToRes<PostResponse>(msg).data;
if (data.post_view.creator.id == UserService.Instance.user.id) {
if (
data.post_view.creator.id ==
UserService.Instance.localUserView.person.id
) {
this.state.loading = false;
this.props.onCreate(data.post_view);
}
} else if (op == UserOperation.EditPost) {
let data = wsJsonToRes<PostResponse>(msg).data;
if (data.post_view.creator.id == UserService.Instance.user.id) {
if (
data.post_view.creator.id ==
UserService.Instance.localUserView.person.id
) {
this.state.loading = false;
this.props.onEdit(data.post_view);
}

View file

@ -9,9 +9,9 @@ import {
LockPost,
StickyPost,
SavePost,
UserViewSafe,
PersonViewSafe,
BanFromCommunity,
BanUser,
BanPerson,
AddModToCommunity,
AddAdmin,
TransferSite,
@ -22,7 +22,7 @@ import { BanType } from "../interfaces";
import { MomentTime } from "./moment-time";
import { PostForm } from "./post-form";
import { IFramelyCard } from "./iframely-card";
import { UserListing } from "./user-listing";
import { PersonListing } from "./person-listing";
import { CommunityLink } from "./community-link";
import { PictrsImage } from "./pictrs-image";
import { Icon } from "./icon";
@ -70,7 +70,7 @@ interface PostListingProps {
showCommunity?: boolean;
showBody?: boolean;
moderators?: CommunityModeratorView[];
admins?: UserViewSafe[];
admins?: PersonViewSafe[];
enableDownvotes: boolean;
enableNsfw: boolean;
}
@ -270,7 +270,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
return (
<ul class="list-inline mb-1 text-muted small">
<li className="list-inline-item">
<UserListing user={post_view.creator} />
<PersonListing person={post_view.creator} />
{this.isMod && (
<span className="mx-1 badge badge-light">{i18n.t("mod")}</span>
@ -599,7 +599,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
postActions(mobile = false) {
let post_view = this.props.post_view;
return (
UserService.Instance.user && (
UserService.Instance.localUserView && (
<>
{this.props.showBody && (
<>
@ -1083,8 +1083,9 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
private get myPost(): boolean {
return (
UserService.Instance.user &&
this.props.post_view.creator.id == UserService.Instance.user.id
UserService.Instance.localUserView &&
this.props.post_view.creator.id ==
UserService.Instance.localUserView.person.id
);
}
@ -1102,7 +1103,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
return (
this.props.admins &&
isMod(
this.props.admins.map(a => a.user.id),
this.props.admins.map(a => a.person.id),
this.props.post_view.creator.id
)
);
@ -1111,11 +1112,11 @@ 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.user.id)
.map(a => a.person.id)
.concat(this.props.moderators.map(m => m.moderator.id));
return canMod(
UserService.Instance.user,
UserService.Instance.localUserView,
adminsThenMods,
this.props.post_view.creator.id
);
@ -1127,11 +1128,11 @@ 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.user.id)
.map(a => a.person.id)
.concat(this.props.moderators.map(m => m.moderator.id));
return canMod(
UserService.Instance.user,
UserService.Instance.localUserView,
adminsThenMods,
this.props.post_view.creator.id,
true
@ -1145,8 +1146,8 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
return (
this.props.admins &&
canMod(
UserService.Instance.user,
this.props.admins.map(a => a.user.id),
UserService.Instance.localUserView,
this.props.admins.map(a => a.person.id),
this.props.post_view.creator.id
)
);
@ -1155,24 +1156,28 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
get amCommunityCreator(): boolean {
return (
this.props.moderators &&
UserService.Instance.user &&
this.props.post_view.creator.id != UserService.Instance.user.id &&
UserService.Instance.user.id == this.props.moderators[0].moderator.id
UserService.Instance.localUserView &&
this.props.post_view.creator.id !=
UserService.Instance.localUserView.person.id &&
UserService.Instance.localUserView.person.id ==
this.props.moderators[0].moderator.id
);
}
get amSiteCreator(): boolean {
return (
this.props.admins &&
UserService.Instance.user &&
this.props.post_view.creator.id != UserService.Instance.user.id &&
UserService.Instance.user.id == this.props.admins[0].user.id
UserService.Instance.localUserView &&
this.props.post_view.creator.id !=
UserService.Instance.localUserView.person.id &&
UserService.Instance.localUserView.person.id ==
this.props.admins[0].person.id
);
}
handlePostLike(i: PostListing, event: any) {
event.preventDefault();
if (!UserService.Instance.user) {
if (!UserService.Instance.localUserView) {
this.context.router.history.push(`/login`);
}
@ -1205,7 +1210,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
handlePostDisLike(i: PostListing, event: any) {
event.preventDefault();
if (!UserService.Instance.user) {
if (!UserService.Instance.localUserView) {
this.context.router.history.push(`/login`);
}
@ -1377,7 +1382,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
i.state.removeData = false;
}
let form: BanFromCommunity = {
user_id: i.props.post_view.creator.id,
person_id: i.props.post_view.creator.id,
community_id: i.props.post_view.community.id,
ban,
remove_data: i.state.removeData,
@ -1392,15 +1397,15 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
if (ban == false) {
i.state.removeData = false;
}
let form: BanUser = {
user_id: i.props.post_view.creator.id,
let form: BanPerson = {
person_id: i.props.post_view.creator.id,
ban,
remove_data: i.state.removeData,
reason: i.state.banReason,
expires: getUnixTime(i.state.banExpires),
auth: authField(),
};
WebSocketService.Instance.send(wsClient.banUser(form));
WebSocketService.Instance.send(wsClient.banPerson(form));
}
i.state.showBanDialog = false;
@ -1409,7 +1414,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
handleAddModToCommunity(i: PostListing) {
let form: AddModToCommunity = {
user_id: i.props.post_view.creator.id,
person_id: i.props.post_view.creator.id,
community_id: i.props.post_view.community.id,
added: !i.isMod,
auth: authField(),
@ -1420,7 +1425,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
handleAddAdmin(i: PostListing) {
let form: AddAdmin = {
user_id: i.props.post_view.creator.id,
person_id: i.props.post_view.creator.id,
added: !i.isAdmin,
auth: authField(),
};
@ -1441,7 +1446,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
handleTransferCommunity(i: PostListing) {
let form: TransferCommunity = {
community_id: i.props.post_view.community.id,
user_id: i.props.post_view.creator.id,
person_id: i.props.post_view.creator.id,
auth: authField(),
};
WebSocketService.Instance.send(wsClient.transferCommunity(form));
@ -1461,7 +1466,7 @@ export class PostListing extends Component<PostListingProps, PostListingState> {
handleTransferSite(i: PostListing) {
let form: TransferSite = {
user_id: i.props.post_view.creator.id,
person_id: i.props.post_view.creator.id,
auth: authField(),
};
WebSocketService.Instance.send(wsClient.transferSite(form));

View file

@ -11,7 +11,7 @@ import {
CommentResponse,
CommunityResponse,
BanFromCommunityResponse,
BanUserResponse,
BanPersonResponse,
AddModToCommunityResponse,
AddAdminResponse,
SearchType,
@ -207,13 +207,13 @@ export class Post extends Component<any, PostState> {
let parent = this.state.postRes.comments.find(
c => found.comment.parent_id == c.comment.id
);
let parent_user_id = parent
let parent_person_id = parent
? parent.creator.id
: this.state.postRes.post_view.creator.id;
if (
UserService.Instance.user &&
UserService.Instance.user.id == parent_user_id
UserService.Instance.localUserView &&
UserService.Instance.localUserView.person.id == parent_person_id
) {
let form: MarkCommentAsRead = {
comment_id: found.comment.id,
@ -522,9 +522,11 @@ export class Post extends Component<any, PostState> {
} else if (op == UserOperation.BanFromCommunity) {
let data = wsJsonToRes<BanFromCommunityResponse>(msg).data;
this.state.postRes.comments
.filter(c => c.creator.id == data.user_view.user.id)
.filter(c => c.creator.id == data.person_view.person.id)
.forEach(c => (c.creator_banned_from_community = data.banned));
if (this.state.postRes.post_view.creator.id == data.user_view.user.id) {
if (
this.state.postRes.post_view.creator.id == data.person_view.person.id
) {
this.state.postRes.post_view.creator_banned_from_community =
data.banned;
}
@ -533,12 +535,14 @@ export class Post extends Component<any, PostState> {
let data = wsJsonToRes<AddModToCommunityResponse>(msg).data;
this.state.postRes.moderators = data.moderators;
this.setState(this.state);
} else if (op == UserOperation.BanUser) {
let data = wsJsonToRes<BanUserResponse>(msg).data;
} else if (op == UserOperation.BanPerson) {
let data = wsJsonToRes<BanPersonResponse>(msg).data;
this.state.postRes.comments
.filter(c => c.creator.id == data.user_view.user.id)
.filter(c => c.creator.id == data.person_view.person.id)
.forEach(c => (c.creator.banned = data.banned));
if (this.state.postRes.post_view.creator.id == data.user_view.user.id) {
if (
this.state.postRes.post_view.creator.id == data.person_view.person.id
) {
this.state.postRes.post_view.creator.banned = data.banned;
}
this.setState(this.state);

View file

@ -6,7 +6,7 @@ import {
EditPrivateMessage,
PrivateMessageView,
PrivateMessageResponse,
UserSafe,
PersonSafe,
UserOperation,
} from "lemmy-js-client";
import { WebSocketService } from "../services";
@ -21,14 +21,14 @@ import {
wsClient,
authField,
} from "../utils";
import { UserListing } from "./user-listing";
import { PersonListing } from "./person-listing";
import { MarkdownTextArea } from "./markdown-textarea";
import { Icon, Spinner } from "./icon";
import { i18n } from "../i18next";
import { T } from "inferno-i18next";
interface PrivateMessageFormProps {
recipient: UserSafe;
recipient: PersonSafe;
privateMessage?: PrivateMessageView; // If a pm is given, that means this is an edit
onCancel?(): any;
onCreate?(message: PrivateMessageView): any;
@ -108,7 +108,7 @@ export class PrivateMessageForm extends Component<
</label>
<div class="col-sm-10 form-control-plaintext">
<UserListing user={this.props.recipient} />
<PersonListing person={this.props.recipient} />
</div>
</div>
)}

View file

@ -3,13 +3,13 @@ import {
PrivateMessageView,
DeletePrivateMessage,
MarkPrivateMessageAsRead,
UserSafe,
PersonSafe,
} from "lemmy-js-client";
import { WebSocketService, UserService } from "../services";
import { authField, mdToHtml, toast, wsClient } from "../utils";
import { MomentTime } from "./moment-time";
import { PrivateMessageForm } from "./private-message-form";
import { UserListing } from "./user-listing";
import { PersonListing } from "./person-listing";
import { Icon } from "./icon";
import { i18n } from "../i18next";
@ -48,15 +48,16 @@ export class PrivateMessage extends Component<
get mine(): boolean {
return (
UserService.Instance.user &&
UserService.Instance.user.id == this.props.private_message_view.creator.id
UserService.Instance.localUserView &&
UserService.Instance.localUserView.person.id ==
this.props.private_message_view.creator.id
);
}
render() {
let message_view = this.props.private_message_view;
// TODO check this again
let userOther: UserSafe = this.mine
let otherPerson: PersonSafe = this.mine
? message_view.recipient
: message_view.creator;
@ -69,7 +70,7 @@ export class PrivateMessage extends Component<
{this.mine ? i18n.t("to") : i18n.t("from")}
</li>
<li className="list-inline-item">
<UserListing user={userOther} />
<PersonListing person={otherPerson} />
</li>
<li className="list-inline-item">
<span>
@ -92,7 +93,7 @@ export class PrivateMessage extends Component<
</ul>
{this.state.showEdit && (
<PrivateMessageForm
recipient={userOther}
recipient={otherPerson}
privateMessage={message_view}
onEdit={this.handlePrivateMessageEdit}
onCreate={this.handlePrivateMessageCreate}
@ -206,7 +207,7 @@ export class PrivateMessage extends Component<
</div>
{this.state.showReply && (
<PrivateMessageForm
recipient={userOther}
recipient={otherPerson}
onCreate={this.handlePrivateMessageCreate}
/>
)}
@ -272,8 +273,8 @@ export class PrivateMessage extends Component<
handlePrivateMessageCreate(message: PrivateMessageView) {
if (
UserService.Instance.user &&
message.creator.id == UserService.Instance.user.id
UserService.Instance.localUserView &&
message.creator.id == UserService.Instance.localUserView.person.id
) {
this.state.showReply = false;
this.setState(this.state);

View file

@ -5,7 +5,7 @@ import {
PostView,
CommentView,
CommunityView,
UserViewSafe,
PersonViewSafe,
SortType,
Search as SearchForm,
SearchResponse,
@ -36,7 +36,7 @@ import {
import { PostListing } from "./post-listing";
import { HtmlTags } from "./html-tags";
import { Spinner } from "./icon";
import { UserListing } from "./user-listing";
import { PersonListing } from "./person-listing";
import { CommunityLink } from "./community-link";
import { SortSelect } from "./sort-select";
import { CommentNodes } from "./comment-nodes";
@ -257,7 +257,7 @@ export class Search extends Component<any, SearchState> {
all() {
let combined: {
type_: string;
data: CommentView | PostView | CommunityView | UserViewSafe;
data: CommentView | PostView | CommunityView | PersonViewSafe;
published: string;
}[] = [];
let comments = this.state.searchResponse.comments.map(e => {
@ -274,7 +274,7 @@ export class Search extends Component<any, SearchState> {
};
});
let users = this.state.searchResponse.users.map(e => {
return { type_: "users", data: e, published: e.user.published };
return { type_: "users", data: e, published: e.person.published };
});
combined.push(...comments);
@ -290,10 +290,10 @@ export class Search extends Component<any, SearchState> {
(a, b) =>
((b.data as CommentView | PostView).counts.score |
(b.data as CommunityView).counts.subscribers |
(b.data as UserViewSafe).counts.comment_score) -
(b.data as PersonViewSafe).counts.comment_score) -
((a.data as CommentView | PostView).counts.score |
(a.data as CommunityView).counts.subscribers |
(a.data as UserViewSafe).counts.comment_score)
(a.data as PersonViewSafe).counts.comment_score)
);
}
@ -324,7 +324,7 @@ export class Search extends Component<any, SearchState> {
<div>{this.communityListing(i.data as CommunityView)}</div>
)}
{i.type_ == "users" && (
<div>{this.userListing(i.data as UserViewSafe)}</div>
<div>{this.userListing(i.data as PersonViewSafe)}</div>
)}
</div>
</div>
@ -390,13 +390,13 @@ export class Search extends Component<any, SearchState> {
);
}
userListing(user_view: UserViewSafe) {
userListing(person_view: PersonViewSafe) {
return [
<span>
<UserListing user={user_view.user} showApubName />
<PersonListing person={person_view.person} showApubName />
</span>,
<span>{` - ${i18n.t("number_of_comments", {
count: user_view.counts.comment_count,
count: person_view.counts.comment_count,
})}`}</span>,
];
}

View file

@ -6,13 +6,13 @@ import {
FollowCommunity,
DeleteCommunity,
RemoveCommunity,
UserViewSafe,
PersonViewSafe,
AddModToCommunity,
} from "lemmy-js-client";
import { WebSocketService, UserService } from "../services";
import { mdToHtml, getUnixTime, wsClient, authField } from "../utils";
import { CommunityForm } from "./community-form";
import { UserListing } from "./user-listing";
import { PersonListing } from "./person-listing";
import { CommunityLink } from "./community-link";
import { BannerIconHeader } from "./banner-icon-header";
import { Icon } from "./icon";
@ -21,7 +21,7 @@ import { i18n } from "../i18next";
interface SidebarProps {
community_view: CommunityView;
moderators: CommunityModeratorView[];
admins: UserViewSafe[];
admins: PersonViewSafe[];
online: number;
enableNsfw: boolean;
showIcon?: boolean;
@ -224,7 +224,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={mod.moderator} />
<PersonListing person={mod.moderator} />
</li>
))}
</ul>
@ -453,7 +453,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
handleLeaveModTeamClick(i: Sidebar) {
let form: AddModToCommunity = {
user_id: UserService.Instance.user.id,
person_id: UserService.Instance.localUserView.person.id,
community_id: i.props.community_view.community.id,
added: false,
auth: authField(),
@ -489,24 +489,27 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
}
private get amCreator(): boolean {
return this.props.community_view.creator.id == UserService.Instance.user.id;
return (
this.props.community_view.creator.id ==
UserService.Instance.localUserView.person.id
);
}
get canMod(): boolean {
return (
UserService.Instance.user &&
UserService.Instance.localUserView &&
this.props.moderators
.map(m => m.moderator.id)
.includes(UserService.Instance.user.id)
.includes(UserService.Instance.localUserView.person.id)
);
}
get canAdmin(): boolean {
return (
UserService.Instance.user &&
UserService.Instance.localUserView &&
this.props.admins
.map(a => a.user.id)
.includes(UserService.Instance.user.id)
.map(a => a.person.id)
.includes(UserService.Instance.localUserView.person.id)
);
}

View file

@ -1,23 +1,23 @@
import { UserSafeSettings } from "lemmy-js-client";
import { LocalUserSettingsView } from "lemmy-js-client";
import { Helmet } from "inferno-helmet";
import { Component } from "inferno";
interface Props {
user: UserSafeSettings | undefined;
localUserView: LocalUserSettingsView | undefined;
}
export class Theme extends Component<Props> {
render() {
const { user } = this.props;
const hasUserTheme = user && user.theme !== "browser";
let user = this.props.localUserView;
let hasTheme = user && user.local_user.theme !== "browser";
return (
<Helmet>
{hasUserTheme ? (
{hasTheme ? (
<link
rel="stylesheet"
type="text/css"
href={`/static/assets/css/themes/${user.theme}.min.css`}
href={`/static/assets/css/themes/${user.local_user.theme}.min.css`}
/>
) : (
[

View file

@ -4,6 +4,6 @@ import { i18n } from "./i18next";
import { getLanguage } from "./utils";
export function initializeSite(site: GetSiteResponse) {
UserService.Instance.user = site.my_user;
UserService.Instance.localUserView = site.my_user;
i18n.changeLanguage(getLanguage());
}

View file

@ -2,7 +2,7 @@ import {
CommentView,
GetSiteResponse,
LemmyHttp,
UserMentionView,
PersonMentionView,
} from "lemmy-js-client";
export interface IsoData {
@ -32,7 +32,7 @@ export interface InitialFetchRequest {
}
export interface CommentNode {
comment_view: CommentView | UserMentionView;
comment_view: CommentView | PersonMentionView;
children?: CommentNode[];
depth?: number;
}
@ -67,7 +67,7 @@ export enum BanType {
Site,
}
export enum UserDetailsView {
export enum PersonDetailsView {
Overview,
Comments,
Posts,

View file

@ -8,7 +8,7 @@ import { PasswordChange } from "./components/password_change";
import { Post } from "./components/post";
import { Community } from "./components/community";
import { Communities } from "./components/communities";
import { User } from "./components/user";
import { Person } from "./components/person";
import { Modlog } from "./components/modlog";
import { Setup } from "./components/setup";
import { AdminSettings } from "./components/admin-settings";
@ -93,23 +93,23 @@ export const routes: IRoutePropsWithFetch[] = [
},
{
path: `/u/:username/view/:view/sort/:sort/page/:page`,
component: User,
fetchInitialData: req => User.fetchInitialData(req),
component: Person,
fetchInitialData: req => Person.fetchInitialData(req),
},
{
path: `/user/:id/view/:view/sort/:sort/page/:page`,
component: User,
fetchInitialData: req => User.fetchInitialData(req),
component: Person,
fetchInitialData: req => Person.fetchInitialData(req),
},
{
path: `/user/:id`,
component: User,
fetchInitialData: req => User.fetchInitialData(req),
component: Person,
fetchInitialData: req => Person.fetchInitialData(req),
},
{
path: `/u/:username`,
component: User,
fetchInitialData: req => User.fetchInitialData(req),
component: Person,
fetchInitialData: req => Person.fetchInitialData(req),
},
{
path: `/inbox`,

View file

@ -1,17 +1,18 @@
// import Cookies from 'js-cookie';
import IsomorphicCookie from "isomorphic-cookie";
import { UserSafeSettings, LoginResponse } from "lemmy-js-client";
import { LocalUserSettingsView, LoginResponse } from "lemmy-js-client";
import jwt_decode from "jwt-decode";
import { Subject, BehaviorSubject } from "rxjs";
interface Claims {
id: number;
sub: number;
iss: string;
iat: number;
}
export class UserService {
private static _instance: UserService;
public user: UserSafeSettings;
public localUserView: LocalUserSettingsView;
public claims: Claims;
public jwtSub: Subject<string> = new Subject<string>();
public unreadCountSub: BehaviorSubject<number> = new BehaviorSubject<number>(
@ -38,7 +39,7 @@ export class UserService {
public logout() {
IsomorphicCookie.remove("jwt", { secure: false });
this.claims = undefined;
this.user = undefined;
this.localUserView = undefined;
// setTheme();
this.jwtSub.next();
console.log("Logged out.");

View file

@ -1,5 +1,5 @@
import { wsUri } from "../env";
import { UserViewSafe, WebSocketJsonResponse } from "lemmy-js-client";
import { PersonViewSafe, WebSocketJsonResponse } from "lemmy-js-client";
import { isBrowser } from "../utils";
import { Observable } from "rxjs";
import { share } from "rxjs/operators";
@ -17,8 +17,8 @@ export class WebSocketService {
};
public subject: Observable<any>;
public admins: UserViewSafe[];
public banned: UserViewSafe[];
public admins: PersonViewSafe[];
public banned: PersonViewSafe[];
private constructor() {
this.ws = new ReconnectingWebSocket(wsUri, [], this.wsOptions);

View file

@ -32,7 +32,7 @@ import "moment/locale/hr";
import {
UserOperation,
CommentView,
UserSafeSettings,
LocalUserSettingsView,
SortType,
ListingType,
SearchType,
@ -43,7 +43,7 @@ import {
PostView,
PrivateMessageView,
LemmyWebsocket,
UserViewSafe,
PersonViewSafe,
CommunityView,
} from "lemmy-js-client";
@ -78,7 +78,7 @@ export const favIconPngUrl = "/static/assets/apple-touch-icon.png";
// export const defaultFavIcon = `${window.location.protocol}//${window.location.host}${favIconPngUrl}`;
export const repoUrl = "https://github.com/LemmyNet";
export const joinLemmyUrl = "https://join.lemmy.ml";
export const supportLemmyUrl = "https://join.lemmy.ml/sponsors";
export const supportLemmyUrl = "https://join.lemmy.ml/support";
export const docsUrl = "https://join.lemmy.ml/docs/en/index.html";
export const helpGuideUrl = "https://join.lemmy.ml/docs/en/about/guide.html"; // TODO find a way to redirect to the non-en folder
export const markdownHelpUrl = `${helpGuideUrl}#markdown-guide`;
@ -91,39 +91,39 @@ export const fetchLimit = 20;
export const mentionDropdownFetchLimit = 10;
export const languages = [
{ code: "ca", name: "Català" },
{ code: "en", name: "English" },
{ code: "el", name: "Ελληνικά" },
{ code: "eu", name: "Euskara" },
{ code: "eo", name: "Esperanto" },
{ code: "es", name: "Español" },
{ code: "da", name: "Dansk" },
{ code: "de", name: "Deutsch" },
{ code: "ga", name: "Gaeilge" },
{ code: "gl", name: "Galego" },
{ code: "hr", name: "hrvatski" },
{ code: "hu", name: "Magyar Nyelv" },
{ code: "ka", name: "ქართული ენა" },
{ code: "ko", name: "한국어" },
{ code: "km", name: "ភាសាខ្មែរ" },
{ code: "hi", name: "मानक हिन्दी" },
{ code: "fa", name: "فارسی" },
{ code: "ja", name: "日本語" },
{ code: "oc", name: "Occitan" },
{ code: "pl", name: "Polski" },
{ code: "pt_BR", name: "Português Brasileiro" },
{ code: "zh", name: "中文" },
{ code: "fi", name: "Suomi" },
{ code: "fr", name: "Français" },
{ code: "sv", name: "Svenska" },
{ code: "sq", name: "Shqip" },
{ code: "sr_Latn", name: "srpski" },
{ code: "th", name: "ภาษาไทย" },
{ code: "tr", name: "Türkçe" },
{ code: "uk", name: "Українська Mова" },
{ code: "ru", name: "Русский" },
{ code: "nl", name: "Nederlands" },
{ code: "it", name: "Italiano" },
{ code: "ca" },
{ code: "en" },
{ code: "el" },
{ code: "eu" },
{ code: "eo" },
{ code: "es" },
{ code: "da" },
{ code: "de" },
{ code: "ga" },
{ code: "gl" },
{ code: "hr" },
{ code: "hu" },
{ code: "ka" },
{ code: "ko" },
{ code: "km" },
{ code: "hi" },
{ code: "fa" },
{ code: "ja" },
{ code: "oc" },
{ code: "pl" },
{ code: "pt_BR" },
{ code: "zh" },
{ code: "fi" },
{ code: "fr" },
{ code: "sv" },
{ code: "sq" },
{ code: "sr_Latn" },
{ code: "th" },
{ code: "tr" },
{ code: "uk" },
{ code: "ru" },
{ code: "nl" },
{ code: "it" },
];
export const themes = [
@ -239,14 +239,14 @@ export function getUnixTime(text: string): number {
}
export function canMod(
user: UserSafeSettings,
localUserView: LocalUserSettingsView,
modIds: number[],
creator_id: number,
onSelf = false
): boolean {
// You can do moderator actions only on the mods added after you.
if (user) {
let yourIndex = modIds.findIndex(id => id == user.id);
if (localUserView) {
let yourIndex = modIds.findIndex(id => id == localUserView.person.id);
if (yourIndex == -1) {
return false;
} else {
@ -367,8 +367,12 @@ export function debounce(func: any, wait = 1000, immediate = false) {
// TODO
export function getLanguage(override?: string): string {
let user = UserService.Instance.user;
let lang = override || (user && user.lang ? user.lang : "browser");
let localUserView = UserService.Instance.localUserView;
let lang =
override ||
(localUserView?.local_user.lang
? localUserView.local_user.lang
: "browser");
if (lang == "browser" && isBrowser()) {
return getBrowserLanguage();
@ -377,7 +381,6 @@ export function getLanguage(override?: string): string {
}
}
// TODO
export function getBrowserLanguage(): string {
return navigator.language;
}
@ -508,21 +511,21 @@ export function objectFlip(obj: any) {
export function showAvatars(): boolean {
return (
(UserService.Instance.user && UserService.Instance.user.show_avatars) ||
!UserService.Instance.user
UserService.Instance.localUserView?.local_user.show_avatars ||
!UserService.Instance.localUserView
);
}
export function isCakeDay(published: string): boolean {
// moment(undefined) or moment.utc(undefined) returns the current date/time
// moment(null) or moment.utc(null) returns null
const userCreationDate = moment.utc(published || null).local();
const createDate = moment.utc(published || null).local();
const currentDate = moment(new Date());
return (
userCreationDate.date() === currentDate.date() &&
userCreationDate.month() === currentDate.month() &&
userCreationDate.year() !== currentDate.year()
createDate.date() === currentDate.date() &&
createDate.month() === currentDate.month() &&
createDate.year() !== currentDate.year()
);
}
@ -666,15 +669,15 @@ export function setupTribute() {
// menuItemLimit: mentionDropdownFetchLimit,
menuShowMinLength: 2,
},
// Users
// Persons
{
trigger: "@",
selectTemplate: (item: any) => {
let it: UserTribute = item.original;
return `[${it.key}](${it.view.user.actor_id})`;
let it: PersonTribute = item.original;
return `[${it.key}](${it.view.person.actor_id})`;
},
values: (text: string, cb: (users: UserTribute[]) => any) => {
userSearch(text, (users: UserTribute[]) => cb(users));
values: (text: string, cb: (persons: PersonTribute[]) => any) => {
personSearch(text, (persons: PersonTribute[]) => cb(persons));
},
allowSpaces: false,
autocompleteMode: true,
@ -721,12 +724,12 @@ export function setupTippy() {
}
}
interface UserTribute {
interface PersonTribute {
key: string;
view: UserViewSafe;
view: PersonViewSafe;
}
function userSearch(text: string, cb: (users: UserTribute[]) => any) {
function personSearch(text: string, cb: (persons: PersonTribute[]) => any) {
if (text) {
let form: Search = {
q: text,
@ -739,20 +742,20 @@ function userSearch(text: string, cb: (users: UserTribute[]) => any) {
WebSocketService.Instance.send(wsClient.search(form));
let userSub = WebSocketService.Instance.subject.subscribe(
let personSub = WebSocketService.Instance.subject.subscribe(
msg => {
let res = wsJsonToRes(msg);
if (res.op == UserOperation.Search) {
let data = res.data as SearchResponse;
let users: UserTribute[] = data.users.map(uv => {
let tribute: UserTribute = {
key: `@${uv.user.name}@${hostname(uv.user.actor_id)}`,
view: uv,
let persons: PersonTribute[] = data.users.map(pv => {
let tribute: PersonTribute = {
key: `@${pv.person.name}@${hostname(pv.person.actor_id)}`,
view: pv,
};
return tribute;
});
cb(users);
userSub.unsubscribe();
cb(persons);
personSub.unsubscribe();
}
},
err => console.error(err),
@ -811,8 +814,10 @@ function communitySearch(
export function getListingTypeFromProps(props: any): ListingType {
return props.match.params.listing_type
? routeListingTypeToEnum(props.match.params.listing_type)
: UserService.Instance.user
? Object.values(ListingType)[UserService.Instance.user.default_listing_type]
: UserService.Instance.localUserView
? Object.values(ListingType)[
UserService.Instance.localUserView.local_user.default_listing_type
]
: ListingType.Local;
}
@ -826,8 +831,10 @@ export function getDataTypeFromProps(props: any): DataType {
export function getSortTypeFromProps(props: any): SortType {
return props.match.params.sort
? routeSortTypeToEnum(props.match.params.sort)
: UserService.Instance.user
? Object.values(SortType)[UserService.Instance.user.default_sort_type]
: UserService.Instance.localUserView
? Object.values(SortType)[
UserService.Instance.localUserView.local_user.default_sort_type
]
: SortType.Active;
}

View file

@ -22,6 +22,6 @@
},
"include": [
"src/**/*",
"node_modules/inferno/dist/index.d.ts",
"node_modules/inferno/dist/index.d.ts"
]
}

View file

@ -4833,6 +4833,11 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
iso-639-1@^2.1.9:
version "2.1.9"
resolved "https://registry.yarnpkg.com/iso-639-1/-/iso-639-1-2.1.9.tgz#e41b11d4f1808e5316d0252c3fa16eeb9b37bb58"
integrity sha512-owRu9up+Cpx/hwSzm83j6G8PtC7U99UCtPVItsafefNfEgMl+pi8KBwhXwJkJfp6IouyYWFxj8n24SvCWpKZEQ==
isobject@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
@ -5022,10 +5027,10 @@ lcid@^1.0.0:
dependencies:
invert-kv "^1.0.0"
lemmy-js-client@0.10.0-rc.1:
version "0.10.0-rc.1"
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.10.0-rc.1.tgz#4a9b9db8fcc8229d634920d7e66f63ab5db8b28e"
integrity sha512-18TQO+EpE+mgCWSwynfFvDCASUjzTkr73/CbneMMHcqexq2R4donE+pNDFFSDHOeYIbdna0f4GZEJhyeh6826g==
lemmy-js-client@0.10.0-rc.10:
version "0.10.0-rc.10"
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.10.0-rc.10.tgz#36802f64191a10e8e70624e04d0cc98de465ae5a"
integrity sha512-WOAjHE0SgNbpq7pA56F3zJUI7pNdtdpdE/KViAjgfEHGW+yscu/nhLzYf/QA1QjI0ONeZc9U254xOnXzSs8XUw==
levn@^0.4.1:
version "0.4.1"