import { Component, linkEvent } from "inferno"; import { Link } from "inferno-router"; import { AddAdminResponse, BanPersonResponse, BlockPerson, BlockPersonResponse, CommentResponse, GetPersonDetails, GetPersonDetailsResponse, GetSiteResponse, PostResponse, SortType, UserOperation, } from "lemmy-js-client"; import moment from "moment"; import { Subscription } from "rxjs"; import { i18n } from "../../i18next"; import { InitialFetchRequest, PersonDetailsView } from "../../interfaces"; import { UserService, WebSocketService } from "../../services"; import { authField, createCommentLikeRes, createPostLikeFindRes, editCommentRes, editPostFindRes, fetchLimit, getUsernameFromProps, mdToHtml, numToSI, previewLines, relTags, restoreScrollPosition, routeSortTypeToEnum, saveCommentRes, saveScrollPosition, setIsoData, setOptionalAuth, setupTippy, toast, updatePersonBlock, wsClient, wsJsonToRes, wsSubscribe, wsUserOp, } from "../../utils"; import { BannerIconHeader } from "../common/banner-icon-header"; import { HtmlTags } from "../common/html-tags"; import { Icon, Spinner } from "../common/icon"; import { MomentTime } from "../common/moment-time"; import { SortSelect } from "../common/sort-select"; import { CommunityLink } from "../community/community-link"; import { PersonDetails } from "./person-details"; import { PersonListing } from "./person-listing"; interface ProfileState { personRes: GetPersonDetailsResponse; userName: string; view: PersonDetailsView; sort: SortType; page: number; loading: boolean; personBlocked: boolean; siteRes: GetSiteResponse; } interface ProfileProps { view: PersonDetailsView; sort: SortType; page: number; person_id: number | null; username: string; } interface UrlParams { view?: string; sort?: SortType; page?: number; } export class Profile extends Component { private isoData = setIsoData(this.context); private subscription: Subscription; private emptyState: ProfileState = { personRes: undefined, userName: getUsernameFromProps(this.props), loading: true, view: Profile.getViewFromProps(this.props.match.view), sort: Profile.getSortTypeFromProps(this.props.match.sort), page: Profile.getPageFromProps(this.props.match.page), personBlocked: false, siteRes: this.isoData.site_res, }; constructor(props: any, context: any) { super(props, context); this.state = this.emptyState; this.handleSortChange = this.handleSortChange.bind(this); this.handlePageChange = this.handlePageChange.bind(this); this.parseMessage = this.parseMessage.bind(this); this.subscription = wsSubscribe(this.parseMessage); // Only fetch the data if coming from another route if (this.isoData.path == this.context.router.route.match.url) { this.state.personRes = this.isoData.routeData[0]; this.state.loading = false; } else { this.fetchUserData(); } this.setPersonBlock(); } fetchUserData() { let form: GetPersonDetails = { username: this.state.userName, sort: this.state.sort, saved_only: this.state.view === PersonDetailsView.Saved, page: this.state.page, limit: fetchLimit, auth: authField(false), }; WebSocketService.Instance.send(wsClient.getPersonDetails(form)); } get isCurrentUser() { return ( UserService.Instance.myUserInfo?.local_user_view.person.id == this.state.personRes.person_view.person.id ); } setPersonBlock() { this.state.personBlocked = UserService.Instance.myUserInfo?.person_blocks .map(a => a.target.id) .includes(this.state.personRes?.person_view.person.id); } static getViewFromProps(view: string): PersonDetailsView { return view ? PersonDetailsView[view] : PersonDetailsView.Overview; } static getSortTypeFromProps(sort: string): SortType { return sort ? routeSortTypeToEnum(sort) : SortType.New; } static getPageFromProps(page: number): number { return page ? Number(page) : 1; } static fetchInitialData(req: InitialFetchRequest): Promise[] { let pathSplit = req.path.split("/"); let promises: Promise[] = []; // It can be /u/me, or /username/1 let idOrName = pathSplit[2]; let person_id: number; let username: string; if (isNaN(Number(idOrName))) { username = idOrName; } else { 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: GetPersonDetails = { sort, saved_only: view === PersonDetailsView.Saved, page, limit: fetchLimit, }; setOptionalAuth(form, req.auth); 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.person_id = id; } else { obj.username = name_; } } componentDidMount() { setupTippy(); } componentWillUnmount() { this.subscription.unsubscribe(); saveScrollPosition(this.context); } static getDerivedStateFromProps(props: any): ProfileProps { return { view: this.getViewFromProps(props.match.params.view), sort: this.getSortTypeFromProps(props.match.params.sort), page: this.getPageFromProps(props.match.params.page), person_id: Number(props.match.params.id) || null, username: props.match.params.username, }; } componentDidUpdate(lastProps: any) { // Necessary if you are on a post and you click another post (same route) if ( lastProps.location.pathname.split("/")[2] !== lastProps.history.location.pathname.split("/")[2] ) { // Couldnt get a refresh working. This does for now. location.reload(); } } get documentTitle(): string { return `@${this.state.personRes.person_view.person.name} - ${this.state.siteRes.site_view.site.name}`; } get bioTag(): string { return this.state.personRes.person_view.person.bio ? previewLines(this.state.personRes.person_view.person.bio) : undefined; } render() { return (
{this.state.loading ? (
) : (
<> {this.userInfo()}
{!this.state.loading && this.selects()}
{!this.state.loading && (
{this.moderates()} {this.isCurrentUser && this.follows()}
)}
)}
); } viewRadios() { return (
); } selects() { let profileRss = `/feeds/u/${this.state.userName}.xml?sort=${this.state.sort}`; return (
{this.viewRadios()}
); } handleBlockPerson(personId: number) { if (personId != 0) { let blockUserForm: BlockPerson = { person_id: personId, block: true, auth: authField(), }; WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm)); } } handleUnblockPerson(recipientId: number) { let blockUserForm: BlockPerson = { person_id: recipientId, block: false, auth: authField(), }; WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm)); } userInfo() { let pv = this.state.personRes?.person_view; return (
{pv.person.display_name && (
{pv.person.display_name}
)}
  • {pv.person.banned && (
  • {i18n.t("banned")}
  • )} {pv.person.admin && (
  • {i18n.t("admin")}
  • )} {pv.person.bot_account && (
  • {i18n.t("bot_account").toLowerCase()}
  • )}
{!this.isCurrentUser && UserService.Instance.myUserInfo && ( <> {i18n.t("send_secure_message")} {i18n.t("send_message")} {this.state.personBlocked ? ( ) : ( )} )}
{pv.person.bio && (
)}
  • {i18n.t("number_of_posts", { count: pv.counts.post_count, formattedCount: numToSI(pv.counts.post_count), })}
  • {i18n.t("number_of_comments", { count: pv.counts.comment_count, formattedCount: numToSI(pv.counts.comment_count), })}
{i18n.t("joined")}{" "}
{i18n.t("cake_day_title")}{" "} {moment.utc(pv.person.published).local().format("MMM DD, YYYY")}
); } moderates() { return (
{this.state.personRes.moderates.length > 0 && (
{i18n.t("moderates")}
    {this.state.personRes.moderates.map(cmv => (
  • ))}
)}
); } follows() { let follows = UserService.Instance.myUserInfo.follows; return (
{follows.length > 0 && (
{i18n.t("subscribed")}
    {follows.map(cfv => (
  • ))}
)}
); } updateUrl(paramUpdates: UrlParams) { const page = paramUpdates.page || this.state.page; const viewStr = paramUpdates.view || PersonDetailsView[this.state.view]; const sortStr = paramUpdates.sort || this.state.sort; let typeView = `/u/${this.state.userName}`; this.props.history.push( `${typeView}/view/${viewStr}/sort/${sortStr}/page/${page}` ); this.state.loading = true; this.setState(this.state); this.fetchUserData(); } handlePageChange(page: number) { this.updateUrl({ page }); } handleSortChange(val: SortType) { this.updateUrl({ sort: val, page: 1 }); } handleViewChange(i: Profile, event: any) { i.updateUrl({ view: PersonDetailsView[Number(event.target.value)], page: 1, }); } parseMessage(msg: any) { let op = wsUserOp(msg); console.log(msg); if (msg.error) { toast(i18n.t(msg.error), "danger"); if (msg.error == "couldnt_find_that_username_or_email") { this.context.router.history.push("/"); } return; } else if (msg.reconnect) { this.fetchUserData(); } 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(msg).data; this.state.personRes = data; console.log(data); this.state.loading = false; this.setPersonBlock(); this.setState(this.state); restoreScrollPosition(this.context); } else if (op == UserOperation.AddAdmin) { let data = wsJsonToRes(msg).data; this.state.siteRes.admins = data.admins; this.setState(this.state); } else if (op == UserOperation.CreateCommentLike) { let data = wsJsonToRes(msg).data; createCommentLikeRes(data.comment_view, this.state.personRes.comments); this.setState(this.state); } else if ( op == UserOperation.EditComment || op == UserOperation.DeleteComment || op == UserOperation.RemoveComment ) { let data = wsJsonToRes(msg).data; editCommentRes(data.comment_view, this.state.personRes.comments); this.setState(this.state); } else if (op == UserOperation.CreateComment) { let data = wsJsonToRes(msg).data; if ( UserService.Instance.myUserInfo && data.comment_view.creator.id == UserService.Instance.myUserInfo?.local_user_view.person.id ) { toast(i18n.t("reply_sent")); } } else if (op == UserOperation.SaveComment) { let data = wsJsonToRes(msg).data; saveCommentRes(data.comment_view, this.state.personRes.comments); this.setState(this.state); } else if ( op == UserOperation.EditPost || op == UserOperation.DeletePost || op == UserOperation.RemovePost || op == UserOperation.LockPost || op == UserOperation.StickyPost || op == UserOperation.SavePost ) { let data = wsJsonToRes(msg).data; editPostFindRes(data.post_view, this.state.personRes.posts); this.setState(this.state); } else if (op == UserOperation.CreatePostLike) { let data = wsJsonToRes(msg).data; createPostLikeFindRes(data.post_view, this.state.personRes.posts); this.setState(this.state); } else if (op == UserOperation.BanPerson) { let data = wsJsonToRes(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.personRes.posts .filter(c => c.creator.id == data.person_view.person.id) .forEach(c => (c.creator.banned = data.banned)); this.setState(this.state); } else if (op == UserOperation.BlockPerson) { let data = wsJsonToRes(msg).data; updatePersonBlock(data); this.setPersonBlock(); this.setState(this.state); } } }