import { Component, linkEvent } from "inferno"; import { Link } from "inferno-router"; import { AddAdminResponse, BanPerson, 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, canMod, capitalizeFirstLetter, createCommentLikeRes, createPostLikeFindRes, editCommentRes, editPostFindRes, fetchLimit, futureDaysToUnixTime, getUsernameFromProps, isBanned, isMod, mdToHtml, numToSI, 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; showBanDialog: boolean; banReason: string; banExpireDays: number; removeData: boolean; } 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, showBanDialog: false, banReason: null, banExpireDays: null, removeData: false, }; 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[] = []; let username = pathSplit[2]; 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, username: username, }; setOptionalAuth(form, req.auth); promises.push(req.client.getPersonDetails(form)); return promises; } 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 ? 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}
)}
  • {isBanned(pv.person) && (
  • {i18n.t("banned")}
  • )} {pv.person.admin && (
  • {i18n.t("admin")}
  • )} {pv.person.bot_account && (
  • {i18n.t("bot_account").toLowerCase()}
  • )}
{this.banDialog()}
{!this.isCurrentUser && UserService.Instance.myUserInfo && ( <> {i18n.t("send_secure_message")} {i18n.t("send_message")} {this.state.personBlocked ? ( ) : ( )} )} {this.canAdmin && !this.personIsAdmin && !this.state.showBanDialog && (!isBanned(pv.person) ? ( ) : ( ))}
{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")}
); } banDialog() { let pv = this.state.personRes?.person_view; return ( <> {this.state.showBanDialog && (
{/* TODO hold off on expires until later */} {/*
*/} {/* */} {/* */} {/*
*/}
)} ); } 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(); } get canAdmin(): boolean { return ( this.state.siteRes?.admins && canMod( UserService.Instance.myUserInfo, this.state.siteRes.admins.map(a => a.person.id), this.state.personRes?.person_view.person.id ) ); } get personIsAdmin(): boolean { return ( this.state.siteRes?.admins && isMod( this.state.siteRes.admins.map(a => a.person.id), this.state.personRes?.person_view.person.id ) ); } 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, }); } handleModBanShow(i: Profile) { i.state.showBanDialog = true; i.setState(i.state); } handleModBanReasonChange(i: Profile, event: any) { i.state.banReason = event.target.value; i.setState(i.state); } handleModBanExpireDaysChange(i: Profile, event: any) { i.state.banExpireDays = event.target.value; i.setState(i.state); } handleModRemoveDataChange(i: Profile, event: any) { i.state.removeData = event.target.checked; i.setState(i.state); } handleModBanSubmitCancel(i: Profile, event?: any) { event.preventDefault(); i.state.showBanDialog = false; i.setState(i.state); } handleModBanSubmit(i: Profile, event?: any) { if (event) event.preventDefault(); let pv = i.state.personRes.person_view; // If its an unban, restore all their data let ban = !pv.person.banned; if (ban == false) { i.state.removeData = false; } let form: BanPerson = { person_id: pv.person.id, ban, remove_data: i.state.removeData, reason: i.state.banReason, expires: futureDaysToUnixTime(i.state.banExpireDays), auth: authField(), }; WebSocketService.Instance.send(wsClient.banPerson(form)); i.state.showBanDialog = false; i.setState(i.state); } 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)); let pv = this.state.personRes.person_view; if (pv.person.id == data.person_view.person.id) { pv.person.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); } } }