getQueryParams,
getQueryString,
numToSI,
+ randomStr,
} from "@utils/helpers";
import { canMod, isAdmin, isBanned } from "@utils/roles";
import type { QueryParams } from "@utils/types";
import { RouteDataResponse } from "@utils/types";
import classNames from "classnames";
+import format from "date-fns/format";
+import parseISO from "date-fns/parseISO";
import { NoOptionI18nKeys } from "i18next";
import { Component, linkEvent } from "inferno";
import { Link } from "inferno-router";
SortType,
TransferCommunity,
} from "lemmy-js-client";
-import moment from "moment";
import { fetchLimit, relTags } from "../../config";
import { InitialFetchRequest, PersonDetailsView } from "../../interfaces";
import { mdToHtml } from "../../markdown";
import { Icon, Spinner } from "../common/icon";
import { MomentTime } from "../common/moment-time";
import { SortSelect } from "../common/sort-select";
+import { UserBadges } from "../common/user-badges";
import { CommunityLink } from "../community/community-link";
import { PersonDetails } from "./person-details";
import { PersonListing } from "./person-listing";
communityViews.length > 0 && (
<div className="card border-secondary mb-3">
<div className="card-body">
- <h5>{I18NextService.i18n.t(translationKey)}</h5>
+ <h2 className="h5">{I18NextService.i18n.t(translationKey)}</h2>
<ul className="list-unstyled mb-0">
{communityViews.map(({ community }) => (
<li key={community.id}>
this.handleSavePost = this.handleSavePost.bind(this);
this.handlePurgePost = this.handlePurgePost.bind(this);
this.handleFeaturePost = this.handleFeaturePost.bind(this);
+ this.handleModBanSubmit = this.handleModBanSubmit.bind(this);
// Only fetch the data if coming from another route
if (FirstLoadService.isFirstLoad) {
async fetchUserData() {
const { page, sort, view } = getProfileQueryParams();
- this.setState({ personRes: { state: "empty" } });
+ this.setState({ personRes: { state: "loading" } });
this.setState({
personRes: await HttpService.client.getPersonDetails({
username: this.props.match.params.username,
<HtmlTags
title={this.documentTitle}
path={this.context.router.route.match.url}
+ canonicalPath={personRes.person_view.person.actor_id}
description={personRes.person_view.person.bio}
image={personRes.person_view.person.avatar}
/>
get viewRadios() {
return (
- <div className="btn-group btn-group-toggle flex-wrap mb-2">
+ <div className="btn-group btn-group-toggle flex-wrap mb-2" role="group">
{this.getRadio(PersonDetailsView.Overview)}
{this.getRadio(PersonDetailsView.Comments)}
{this.getRadio(PersonDetailsView.Posts)}
getRadio(view: PersonDetailsView) {
const { view: urlView } = getProfileQueryParams();
const active = view === urlView;
+ const radioId = randomStr();
return (
- <label
- className={classNames("btn btn-outline-secondary pointer", {
- active,
- })}
- >
+ <>
<input
+ id={radioId}
type="radio"
className="btn-check"
value={view}
checked={active}
onChange={linkEvent(this, this.handleViewChange)}
/>
- {I18NextService.i18n.t(view.toLowerCase() as NoOptionI18nKeys)}
- </label>
+ <label
+ htmlFor={radioId}
+ className={classNames("btn btn-outline-secondary pointer", {
+ active,
+ })}
+ >
+ {I18NextService.i18n.t(view.toLowerCase() as NoOptionI18nKeys)}
+ </label>
+ </>
);
}
<div className="mb-0 d-flex flex-wrap">
<div>
{pv.person.display_name && (
- <h5 className="mb-0">{pv.person.display_name}</h5>
+ <h1 className="h4 mb-4">{pv.person.display_name}</h1>
)}
<ul className="list-inline mb-2">
<li className="list-inline-item">
hideAvatar
/>
</li>
- {isBanned(pv.person) && (
- <li className="list-inline-item badge text-bg-danger">
- {I18NextService.i18n.t("banned")}
- </li>
- )}
- {pv.person.deleted && (
- <li className="list-inline-item badge text-bg-danger">
- {I18NextService.i18n.t("deleted")}
- </li>
- )}
- {pv.person.admin && (
- <li className="list-inline-item badge text-bg-light">
- {I18NextService.i18n.t("admin")}
- </li>
- )}
- {pv.person.bot_account && (
- <li className="list-inline-item badge text-bg-light">
- {I18NextService.i18n.t("bot_account").toLowerCase()}
- </li>
- )}
+ <li className="list-inline-item">
+ <UserBadges
+ classNames="ms-1"
+ isBanned={isBanned(pv.person)}
+ isDeleted={pv.person.deleted}
+ isAdmin={pv.person.admin}
+ isBot={pv.person.bot_account}
+ />
+ </li>
</ul>
</div>
{this.banDialog(pv)}
<Icon icon="cake" />
<span className="ms-2">
{I18NextService.i18n.t("cake_day_title")}{" "}
- {moment
- .utc(pv.person.published)
- .local()
- .format("MMM DD, YYYY")}
+ {format(parseISO(pv.person.published), "PPP")}
</span>
</div>
{!UserService.Instance.myUserInfo && (
value={this.state.banReason}
onInput={linkEvent(this, this.handleModBanReasonChange)}
/>
- <label className="col-form-label" htmlFor={`mod-ban-expires`}>
+ <label className="col-form-label" htmlFor="mod-ban-expires">
{I18NextService.i18n.t("expires")}
</label>
<input
type="number"
- id={`mod-ban-expires`}
+ id="mod-ban-expires"
className="form-control me-2"
placeholder={I18NextService.i18n.t("number_of_days")}
value={this.state.banExpireDays}
>
{I18NextService.i18n.t("cancel")}
</button>
+ </div>
+ <div className="mb-3 row">
<button
type="submit"
className="btn btn-secondary"
s.personRes.data.comments
.filter(c => c.creator.id == banRes.data.person_view.person.id)
.forEach(c => (c.creator.banned = banRes.data.banned));
+ s.personRes.data.person_view.person.banned = banRes.data.banned;
}
return s;
});