mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-12-23 03:11:25 +00:00
Private instances (#523)
* Updating translations. * Adding registration applications. * Updating translations. * Adding verify email route. * Fix missing signup question bug. * Updating translations. * A few fixes from comments on lemmy PR. * v0.15.0-rc.4 * Some suggestions from PR. * v0.15.0-rc.5 * Adding optional auth to modlog fetches. * v0.15.0-rc.6 * Hide deny / approve buttons
This commit is contained in:
parent
4c6713d3f2
commit
b96e16b4e9
23 changed files with 849 additions and 65 deletions
|
@ -1 +1 @@
|
|||
Subproject commit 0412b6b349e5e8d6ac3ed88801187833e95c72c9
|
||||
Subproject commit 1e0bb9920cda13bb128c87e85125b98ab8f319b6
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "lemmy-ui",
|
||||
"description": "An isomorphic UI for lemmy",
|
||||
"version": "0.14.5",
|
||||
"version": "0.15.0-rc.6",
|
||||
"author": "Dessalines <tyhou13@gmx.com>",
|
||||
"license": "AGPL-3.0",
|
||||
"scripts": {
|
||||
|
@ -72,7 +72,7 @@
|
|||
"husky": "^7.0.4",
|
||||
"import-sort-style-module": "^6.0.0",
|
||||
"iso-639-1": "^2.1.10",
|
||||
"lemmy-js-client": "0.14.0-rc.1",
|
||||
"lemmy-js-client": "0.15.0-rc.6",
|
||||
"lint-staged": "^12.1.2",
|
||||
"mini-css-extract-plugin": "^2.4.5",
|
||||
"node-fetch": "^2.6.1",
|
||||
|
|
|
@ -91,8 +91,12 @@ server.get("/*", async (req, res) => {
|
|||
if (routeData[0] && routeData[0].error) {
|
||||
let errCode = routeData[0].error;
|
||||
console.error(errCode);
|
||||
if (errCode == "instance_is_private") {
|
||||
return res.redirect(`/signup`);
|
||||
} else {
|
||||
return res.redirect(`/404?err=${errCode}`);
|
||||
}
|
||||
}
|
||||
|
||||
let isoData: IsoData = {
|
||||
path: req.path,
|
||||
|
|
|
@ -7,6 +7,8 @@ import {
|
|||
GetSiteResponse,
|
||||
GetUnreadCount,
|
||||
GetUnreadCountResponse,
|
||||
GetUnreadRegistrationApplicationCount,
|
||||
GetUnreadRegistrationApplicationCountResponse,
|
||||
PrivateMessageResponse,
|
||||
UserOperation,
|
||||
} from "lemmy-js-client";
|
||||
|
@ -41,6 +43,7 @@ interface NavbarState {
|
|||
expanded: boolean;
|
||||
unreadInboxCount: number;
|
||||
unreadReportCount: number;
|
||||
unreadApplicationCount: number;
|
||||
searchParam: string;
|
||||
toggleSearch: boolean;
|
||||
showDropdown: boolean;
|
||||
|
@ -52,11 +55,13 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
|||
private userSub: Subscription;
|
||||
private unreadInboxCountSub: Subscription;
|
||||
private unreadReportCountSub: Subscription;
|
||||
private unreadApplicationCountSub: Subscription;
|
||||
private searchTextField: RefObject<HTMLInputElement>;
|
||||
emptyState: NavbarState = {
|
||||
isLoggedIn: !!this.props.site_res.my_user,
|
||||
unreadInboxCount: 0,
|
||||
unreadReportCount: 0,
|
||||
unreadApplicationCount: 0,
|
||||
expanded: false,
|
||||
searchParam: "",
|
||||
toggleSearch: false,
|
||||
|
@ -115,6 +120,11 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
|||
UserService.Instance.unreadReportCountSub.subscribe(res => {
|
||||
this.setState({ unreadReportCount: res });
|
||||
});
|
||||
// Subscribe to unread application count
|
||||
this.unreadApplicationCountSub =
|
||||
UserService.Instance.unreadApplicationCountSub.subscribe(res => {
|
||||
this.setState({ unreadApplicationCount: res });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,6 +133,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
|||
this.userSub.unsubscribe();
|
||||
this.unreadInboxCountSub.unsubscribe();
|
||||
this.unreadReportCountSub.unsubscribe();
|
||||
this.unreadApplicationCountSub.unsubscribe();
|
||||
}
|
||||
|
||||
updateUrl() {
|
||||
|
@ -215,6 +226,31 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
|||
</li>
|
||||
</ul>
|
||||
)}
|
||||
{UserService.Instance.myUserInfo?.local_user_view.person
|
||||
.admin && (
|
||||
<ul class="navbar-nav ml-1">
|
||||
<li className="nav-item">
|
||||
<NavLink
|
||||
to="/registration_applications"
|
||||
className="p-1 navbar-toggler nav-link border-0"
|
||||
onMouseUp={linkEvent(this, this.handleHideExpandNavbar)}
|
||||
title={i18n.t("unread_registration_applications", {
|
||||
count: this.state.unreadApplicationCount,
|
||||
formattedCount: numToSI(
|
||||
this.state.unreadApplicationCount
|
||||
),
|
||||
})}
|
||||
>
|
||||
<Icon icon="clipboard" />
|
||||
{this.state.unreadApplicationCount > 0 && (
|
||||
<span class="mx-1 badge badge-light">
|
||||
{numToSI(this.state.unreadApplicationCount)}
|
||||
</span>
|
||||
)}
|
||||
</NavLink>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<button
|
||||
|
@ -366,6 +402,31 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
|||
</li>
|
||||
</ul>
|
||||
)}
|
||||
{UserService.Instance.myUserInfo?.local_user_view.person
|
||||
.admin && (
|
||||
<ul class="navbar-nav my-2">
|
||||
<li className="nav-item">
|
||||
<NavLink
|
||||
to="/registration_applications"
|
||||
className="nav-link"
|
||||
onMouseUp={linkEvent(this, this.handleHideExpandNavbar)}
|
||||
title={i18n.t("unread_registration_applications", {
|
||||
count: this.state.unreadApplicationCount,
|
||||
formattedCount: numToSI(
|
||||
this.state.unreadApplicationCount
|
||||
),
|
||||
})}
|
||||
>
|
||||
<Icon icon="clipboard" />
|
||||
{this.state.unreadApplicationCount > 0 && (
|
||||
<span class="mx-1 badge badge-light">
|
||||
{numToSI(this.state.unreadApplicationCount)}
|
||||
</span>
|
||||
)}
|
||||
</NavLink>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item dropdown">
|
||||
<button
|
||||
|
@ -537,6 +598,12 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
|||
this.state.unreadReportCount = data.post_reports + data.comment_reports;
|
||||
this.setState(this.state);
|
||||
this.sendReportUnread();
|
||||
} else if (op == UserOperation.GetUnreadRegistrationApplicationCount) {
|
||||
let data =
|
||||
wsJsonToRes<GetUnreadRegistrationApplicationCountResponse>(msg).data;
|
||||
this.state.unreadApplicationCount = data.registration_applications;
|
||||
this.setState(this.state);
|
||||
this.sendApplicationUnread();
|
||||
} else if (op == UserOperation.GetSite) {
|
||||
// This is only called on a successful login
|
||||
let data = wsJsonToRes<GetSiteResponse>(msg).data;
|
||||
|
@ -586,7 +653,6 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
|||
let unreadForm: GetUnreadCount = {
|
||||
auth: authField(),
|
||||
};
|
||||
|
||||
WebSocketService.Instance.send(wsClient.getUnreadCount(unreadForm));
|
||||
|
||||
console.log("Fetching reports...");
|
||||
|
@ -594,8 +660,18 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
|||
let reportCountForm: GetReportCount = {
|
||||
auth: authField(),
|
||||
};
|
||||
|
||||
WebSocketService.Instance.send(wsClient.getReportCount(reportCountForm));
|
||||
|
||||
if (UserService.Instance.myUserInfo?.local_user_view.person.admin) {
|
||||
console.log("Fetching applications...");
|
||||
|
||||
let applicationCountForm: GetUnreadRegistrationApplicationCount = {
|
||||
auth: authField(),
|
||||
};
|
||||
WebSocketService.Instance.send(
|
||||
wsClient.getUnreadRegistrationApplicationCount(applicationCountForm)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
get currentLocation() {
|
||||
|
@ -612,6 +688,12 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
|||
);
|
||||
}
|
||||
|
||||
sendApplicationUnread() {
|
||||
UserService.Instance.unreadApplicationCountSub.next(
|
||||
this.state.unreadApplicationCount
|
||||
);
|
||||
}
|
||||
|
||||
get canAdmin(): boolean {
|
||||
return (
|
||||
UserService.Instance.myUserInfo &&
|
||||
|
|
|
@ -25,6 +25,9 @@ export class CommentReport extends Component<CommentReportProps, any> {
|
|||
render() {
|
||||
let r = this.props.report;
|
||||
let comment = r.comment;
|
||||
let tippyContent = i18n.t(
|
||||
r.comment_report.resolved ? "unresolve_report" : "resolve_report"
|
||||
);
|
||||
|
||||
// Set the original post data ( a troll could change it )
|
||||
comment.content = r.comment_report.original_comment_text;
|
||||
|
@ -78,12 +81,8 @@ export class CommentReport extends Component<CommentReportProps, any> {
|
|||
<button
|
||||
className="btn btn-link btn-animate text-muted py-0"
|
||||
onClick={linkEvent(this, this.handleResolveReport)}
|
||||
data-tippy-content={
|
||||
r.comment_report.resolved ? "unresolve_report" : "resolve_report"
|
||||
}
|
||||
aria-label={
|
||||
r.comment_report.resolved ? "unresolve_report" : "resolve_report"
|
||||
}
|
||||
data-tippy-content={tippyContent}
|
||||
aria-label={tippyContent}
|
||||
>
|
||||
<Icon
|
||||
icon="check"
|
|
@ -17,7 +17,7 @@ import {
|
|||
import { Icon, Spinner } from "./icon";
|
||||
|
||||
interface MarkdownTextAreaProps {
|
||||
initialContent: string;
|
||||
initialContent?: string;
|
||||
finished?: boolean;
|
||||
buttonTitle?: string;
|
||||
replyType?: boolean;
|
||||
|
|
150
src/shared/components/common/registration-application.tsx
Normal file
150
src/shared/components/common/registration-application.tsx
Normal file
|
@ -0,0 +1,150 @@
|
|||
import { Component, linkEvent } from "inferno";
|
||||
import { T } from "inferno-i18next-dess";
|
||||
import {
|
||||
ApproveRegistrationApplication,
|
||||
RegistrationApplicationView,
|
||||
} from "lemmy-js-client";
|
||||
import { i18n } from "../../i18next";
|
||||
import { WebSocketService } from "../../services";
|
||||
import { authField, mdToHtml, wsClient } from "../../utils";
|
||||
import { PersonListing } from "../person/person-listing";
|
||||
import { MarkdownTextArea } from "./markdown-textarea";
|
||||
import { MomentTime } from "./moment-time";
|
||||
|
||||
interface RegistrationApplicationProps {
|
||||
application: RegistrationApplicationView;
|
||||
}
|
||||
|
||||
interface RegistrationApplicationState {
|
||||
denyReason?: string;
|
||||
denyExpanded: boolean;
|
||||
}
|
||||
|
||||
export class RegistrationApplication extends Component<
|
||||
RegistrationApplicationProps,
|
||||
RegistrationApplicationState
|
||||
> {
|
||||
private emptyState: RegistrationApplicationState = {
|
||||
denyReason: this.props.application.registration_application.deny_reason,
|
||||
denyExpanded: false,
|
||||
};
|
||||
|
||||
constructor(props: any, context: any) {
|
||||
super(props, context);
|
||||
|
||||
this.state = this.emptyState;
|
||||
this.handleDenyReasonChange = this.handleDenyReasonChange.bind(this);
|
||||
}
|
||||
|
||||
render() {
|
||||
let a = this.props.application;
|
||||
let ra = this.props.application.registration_application;
|
||||
let accepted = a.creator_local_user.accepted_application;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
{i18n.t("applicant")}: <PersonListing person={a.creator} />
|
||||
</div>
|
||||
<div>
|
||||
{i18n.t("created")}: <MomentTime showAgo data={ra} />
|
||||
</div>
|
||||
<div>{i18n.t("answer")}:</div>
|
||||
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(ra.answer)} />
|
||||
|
||||
{a.admin && (
|
||||
<div>
|
||||
{accepted ? (
|
||||
<T i18nKey="approved_by">
|
||||
#
|
||||
<PersonListing person={a.admin} />
|
||||
</T>
|
||||
) : (
|
||||
<div>
|
||||
<T i18nKey="denied_by">
|
||||
#
|
||||
<PersonListing person={a.admin} />
|
||||
</T>
|
||||
<div>
|
||||
{i18n.t("deny_reason")}:{" "}
|
||||
<div
|
||||
className="md-div d-inline-flex"
|
||||
dangerouslySetInnerHTML={mdToHtml(ra.deny_reason || "")}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{this.state.denyExpanded && (
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label">
|
||||
{i18n.t("deny_reason")}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<MarkdownTextArea
|
||||
initialContent={this.state.denyReason}
|
||||
onContentChange={this.handleDenyReasonChange}
|
||||
hideNavigationWarnings
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{(!ra.admin_id || (ra.admin_id && !accepted)) && (
|
||||
<button
|
||||
className="btn btn-secondary mr-2 my-2"
|
||||
onClick={linkEvent(this, this.handleApprove)}
|
||||
aria-label={i18n.t("approve")}
|
||||
>
|
||||
{i18n.t("approve")}
|
||||
</button>
|
||||
)}
|
||||
{(!ra.admin_id || (ra.admin_id && accepted)) && (
|
||||
<button
|
||||
className="btn btn-secondary mr-2"
|
||||
onClick={linkEvent(this, this.handleDeny)}
|
||||
aria-label={i18n.t("deny")}
|
||||
>
|
||||
{i18n.t("deny")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
handleApprove(i: RegistrationApplication) {
|
||||
i.setState({ denyExpanded: false });
|
||||
let form: ApproveRegistrationApplication = {
|
||||
id: i.props.application.registration_application.id,
|
||||
deny_reason: "",
|
||||
approve: true,
|
||||
auth: authField(),
|
||||
};
|
||||
WebSocketService.Instance.send(
|
||||
wsClient.approveRegistrationApplication(form)
|
||||
);
|
||||
}
|
||||
|
||||
handleDeny(i: RegistrationApplication) {
|
||||
if (i.state.denyExpanded) {
|
||||
i.setState({ denyExpanded: false });
|
||||
let form: ApproveRegistrationApplication = {
|
||||
id: i.props.application.registration_application.id,
|
||||
approve: false,
|
||||
deny_reason: i.state.denyReason,
|
||||
auth: authField(),
|
||||
};
|
||||
WebSocketService.Instance.send(
|
||||
wsClient.approveRegistrationApplication(form)
|
||||
);
|
||||
} else {
|
||||
i.setState({ denyExpanded: true });
|
||||
}
|
||||
}
|
||||
|
||||
handleDenyReasonChange(val: string) {
|
||||
this.state.denyReason = val;
|
||||
this.setState(this.state);
|
||||
}
|
||||
}
|
|
@ -12,6 +12,9 @@ export const SYMBOLS = (
|
|||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
>
|
||||
<defs>
|
||||
<symbol id="icon-clipboard" viewBox="0 0 24 24">
|
||||
<path d="M7 5c0 0.552 0.225 1.053 0.586 1.414s0.862 0.586 1.414 0.586h6c0.552 0 1.053-0.225 1.414-0.586s0.586-0.862 0.586-1.414h1c0.276 0 0.525 0.111 0.707 0.293s0.293 0.431 0.293 0.707v14c0 0.276-0.111 0.525-0.293 0.707s-0.431 0.293-0.707 0.293h-12c-0.276 0-0.525-0.111-0.707-0.293s-0.293-0.431-0.293-0.707v-14c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293zM9 1c-0.552 0-1.053 0.225-1.414 0.586s-0.586 0.862-0.586 1.414h-1c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v14c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h12c0.828 0 1.58-0.337 2.121-0.879s0.879-1.293 0.879-2.121v-14c0-0.828-0.337-1.58-0.879-2.121s-1.293-0.879-2.121-0.879h-1c0-0.552-0.225-1.053-0.586-1.414s-0.862-0.586-1.414-0.586zM9 3h6v2h-6z"></path>
|
||||
</symbol>
|
||||
<symbol id="icon-shield" viewBox="0 0 24 24">
|
||||
<path d="M12 20.862c-1.184-0.672-4.42-2.695-6.050-5.549-0.079-0.138-0.153-0.276-0.223-0.417-0.456-0.911-0.727-1.878-0.727-2.896v-6.307l7-2.625 7 2.625v6.307c0 1.018-0.271 1.985-0.726 2.897-0.070 0.14-0.145 0.279-0.223 0.417-1.631 2.854-4.867 4.876-6.050 5.549zM12.447 22.894c0 0 4.989-2.475 7.34-6.589 0.096-0.168 0.188-0.34 0.276-0.515 0.568-1.135 0.937-2.408 0.937-3.79v-7c0-0.426-0.267-0.79-0.649-0.936l-8-3c-0.236-0.089-0.485-0.082-0.702 0l-8 3c-0.399 0.149-0.646 0.527-0.649 0.936v7c0 1.382 0.369 2.655 0.938 3.791 0.087 0.175 0.179 0.346 0.276 0.515 2.351 4.114 7.34 6.589 7.34 6.589 0.292 0.146 0.62 0.136 0.894 0z"></path>
|
||||
</symbol>
|
||||
|
|
|
@ -239,6 +239,7 @@ export class Home extends Component<any, HomeState> {
|
|||
sort: SortType.Hot,
|
||||
limit: 6,
|
||||
};
|
||||
setOptionalAuth(trendingCommunitiesForm, req.auth);
|
||||
promises.push(req.client.listCommunities(trendingCommunitiesForm));
|
||||
|
||||
return promises;
|
||||
|
|
|
@ -185,8 +185,6 @@ export class Login extends Component<any, State> {
|
|||
if (msg.error) {
|
||||
toast(i18n.t(msg.error), "danger");
|
||||
this.state = this.emptyState;
|
||||
// Refetch another captcha
|
||||
WebSocketService.Instance.send(wsClient.getCaptcha());
|
||||
this.setState(this.state);
|
||||
return;
|
||||
} else {
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
authField,
|
||||
isBrowser,
|
||||
joinLemmyUrl,
|
||||
mdToHtml,
|
||||
setIsoData,
|
||||
toast,
|
||||
validEmail,
|
||||
|
@ -27,6 +28,7 @@ import {
|
|||
} from "../../utils";
|
||||
import { HtmlTags } from "../common/html-tags";
|
||||
import { Icon, Spinner } from "../common/icon";
|
||||
import { MarkdownTextArea } from "../common/markdown-textarea";
|
||||
|
||||
const passwordStrengthOptions: Options<string> = [
|
||||
{
|
||||
|
@ -77,6 +79,7 @@ export class Signup extends Component<any, State> {
|
|||
captcha_uuid: undefined,
|
||||
captcha_answer: undefined,
|
||||
honeypot: undefined,
|
||||
answer: undefined,
|
||||
},
|
||||
registerLoading: false,
|
||||
captcha: undefined,
|
||||
|
@ -88,6 +91,7 @@ export class Signup extends Component<any, State> {
|
|||
super(props, context);
|
||||
|
||||
this.state = this.emptyState;
|
||||
this.handleAnswerChange = this.handleAnswerChange.bind(this);
|
||||
|
||||
this.parseMessage = this.parseMessage.bind(this);
|
||||
this.subscription = wsSubscribe(this.parseMessage);
|
||||
|
@ -104,7 +108,13 @@ export class Signup extends Component<any, State> {
|
|||
}
|
||||
|
||||
get documentTitle(): string {
|
||||
return `${i18n.t("login")} - ${this.state.site_view.site.name}`;
|
||||
return `${this.titleName} - ${this.state.site_view.site.name}`;
|
||||
}
|
||||
|
||||
get titleName(): string {
|
||||
return `${i18n.t(
|
||||
this.state.site_view.site.private_instance ? "apply_to_join" : "sign_up"
|
||||
)}`;
|
||||
}
|
||||
|
||||
get isLemmyMl(): boolean {
|
||||
|
@ -128,7 +138,7 @@ export class Signup extends Component<any, State> {
|
|||
registerForm() {
|
||||
return (
|
||||
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
|
||||
<h5>{i18n.t("sign_up")}</h5>
|
||||
<h5>{this.titleName}</h5>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2 col-form-label" htmlFor="register-username">
|
||||
|
@ -159,13 +169,19 @@ export class Signup extends Component<any, State> {
|
|||
type="email"
|
||||
id="register-email"
|
||||
class="form-control"
|
||||
placeholder={i18n.t("optional")}
|
||||
placeholder={
|
||||
this.state.site_view.site.require_email_verification
|
||||
? i18n.t("required")
|
||||
: i18n.t("optional")
|
||||
}
|
||||
value={this.state.registerForm.email}
|
||||
autoComplete="email"
|
||||
onInput={linkEvent(this, this.handleRegisterEmailChange)}
|
||||
required={this.state.site_view.site.require_email_verification}
|
||||
minLength={3}
|
||||
/>
|
||||
{!validEmail(this.state.registerForm.email) && (
|
||||
{!this.state.site_view.site.require_email_verification &&
|
||||
!validEmail(this.state.registerForm.email) && (
|
||||
<div class="mt-2 mb-0 alert alert-light" role="alert">
|
||||
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
||||
{i18n.t("no_password_reset")}
|
||||
|
@ -219,6 +235,40 @@ export class Signup extends Component<any, State> {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{this.state.site_view.site.require_application && (
|
||||
<>
|
||||
<div class="form-group row">
|
||||
<div class="offset-sm-2 col-sm-10">
|
||||
<div class="mt-2 alert alert-light" role="alert">
|
||||
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
||||
{i18n.t("fill_out_application")}
|
||||
</div>
|
||||
<div
|
||||
className="md-div"
|
||||
dangerouslySetInnerHTML={mdToHtml(
|
||||
this.state.site_view.site.application_question || ""
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label
|
||||
class="col-sm-2 col-form-label"
|
||||
htmlFor="application_answer"
|
||||
>
|
||||
{i18n.t("answer")}
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<MarkdownTextArea
|
||||
onContentChange={this.handleAnswerChange}
|
||||
hideNavigationWarnings
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{this.state.captcha && (
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-2" htmlFor="register-captcha">
|
||||
|
@ -286,7 +336,7 @@ export class Signup extends Component<any, State> {
|
|||
<div class="form-group row">
|
||||
<div class="col-sm-10">
|
||||
<button type="submit" class="btn btn-secondary">
|
||||
{this.state.registerLoading ? <Spinner /> : i18n.t("sign_up")}
|
||||
{this.state.registerLoading ? <Spinner /> : this.titleName}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -382,6 +432,11 @@ export class Signup extends Component<any, State> {
|
|||
i.setState(i.state);
|
||||
}
|
||||
|
||||
handleAnswerChange(val: string) {
|
||||
this.state.registerForm.answer = val;
|
||||
this.setState(this.state);
|
||||
}
|
||||
|
||||
handleHoneyPotChange(i: Signup, event: any) {
|
||||
i.state.registerForm.honeypot = event.target.value;
|
||||
i.setState(i.state);
|
||||
|
@ -434,6 +489,8 @@ export class Signup extends Component<any, State> {
|
|||
let data = wsJsonToRes<LoginResponse>(msg).data;
|
||||
this.state = this.emptyState;
|
||||
this.setState(this.state);
|
||||
// Only log them in if a jwt was set
|
||||
if (data.jwt) {
|
||||
UserService.Instance.login(data);
|
||||
WebSocketService.Instance.send(
|
||||
wsClient.userJoin({
|
||||
|
@ -441,6 +498,15 @@ export class Signup extends Component<any, State> {
|
|||
})
|
||||
);
|
||||
this.props.history.push("/communities");
|
||||
} else {
|
||||
if (data.verify_email_sent) {
|
||||
toast(i18n.t("verify_email_sent"));
|
||||
}
|
||||
if (data.registration_created) {
|
||||
toast(i18n.t("registration_application_sent"));
|
||||
}
|
||||
this.props.history.push("/");
|
||||
}
|
||||
} else if (op == UserOperation.GetCaptcha) {
|
||||
let data = wsJsonToRes<GetCaptchaResponse>(msg).data;
|
||||
if (data.ok) {
|
||||
|
|
|
@ -3,12 +3,7 @@ import { Prompt } from "inferno-router";
|
|||
import { CreateSite, EditSite, Site } from "lemmy-js-client";
|
||||
import { i18n } from "../../i18next";
|
||||
import { WebSocketService } from "../../services";
|
||||
import {
|
||||
authField,
|
||||
capitalizeFirstLetter,
|
||||
randomStr,
|
||||
wsClient,
|
||||
} from "../../utils";
|
||||
import { authField, capitalizeFirstLetter, wsClient } from "../../utils";
|
||||
import { Spinner } from "../common/icon";
|
||||
import { ImageUploadForm } from "../common/image-upload-form";
|
||||
import { MarkdownTextArea } from "../common/markdown-textarea";
|
||||
|
@ -24,7 +19,6 @@ interface SiteFormState {
|
|||
}
|
||||
|
||||
export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||
private id = `site-form-${randomStr()}`;
|
||||
private emptyState: SiteFormState = {
|
||||
siteForm: {
|
||||
enable_downvotes: true,
|
||||
|
@ -33,6 +27,10 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
name: null,
|
||||
icon: null,
|
||||
banner: null,
|
||||
require_email_verification: null,
|
||||
require_application: null,
|
||||
application_question: null,
|
||||
private_instance: null,
|
||||
auth: authField(),
|
||||
},
|
||||
loading: false,
|
||||
|
@ -43,6 +41,8 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
|
||||
this.state = this.emptyState;
|
||||
this.handleSiteSidebarChange = this.handleSiteSidebarChange.bind(this);
|
||||
this.handleSiteApplicationQuestionChange =
|
||||
this.handleSiteApplicationQuestionChange.bind(this);
|
||||
|
||||
this.handleIconUpload = this.handleIconUpload.bind(this);
|
||||
this.handleIconRemove = this.handleIconRemove.bind(this);
|
||||
|
@ -51,17 +51,21 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
this.handleBannerRemove = this.handleBannerRemove.bind(this);
|
||||
|
||||
if (this.props.site) {
|
||||
let site = this.props.site;
|
||||
this.state.siteForm = {
|
||||
name: this.props.site.name,
|
||||
sidebar: this.props.site.sidebar,
|
||||
description: this.props.site.description,
|
||||
enable_downvotes: this.props.site.enable_downvotes,
|
||||
open_registration: this.props.site.open_registration,
|
||||
enable_nsfw: this.props.site.enable_nsfw,
|
||||
community_creation_admin_only:
|
||||
this.props.site.community_creation_admin_only,
|
||||
icon: this.props.site.icon,
|
||||
banner: this.props.site.banner,
|
||||
name: site.name,
|
||||
sidebar: site.sidebar,
|
||||
description: site.description,
|
||||
enable_downvotes: site.enable_downvotes,
|
||||
open_registration: site.open_registration,
|
||||
enable_nsfw: site.enable_nsfw,
|
||||
community_creation_admin_only: site.community_creation_admin_only,
|
||||
icon: site.icon,
|
||||
banner: site.banner,
|
||||
require_email_verification: site.require_email_verification,
|
||||
require_application: site.require_application,
|
||||
application_question: site.application_question,
|
||||
private_instance: site.private_instance,
|
||||
auth: authField(),
|
||||
};
|
||||
}
|
||||
|
@ -79,6 +83,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
!this.props.site &&
|
||||
(this.state.siteForm.name ||
|
||||
this.state.siteForm.sidebar ||
|
||||
this.state.siteForm.application_question ||
|
||||
this.state.siteForm.description)
|
||||
) {
|
||||
window.onbeforeunload = () => true;
|
||||
|
@ -100,6 +105,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
!this.props.site &&
|
||||
(this.state.siteForm.name ||
|
||||
this.state.siteForm.sidebar ||
|
||||
this.state.siteForm.application_question ||
|
||||
this.state.siteForm.description)
|
||||
}
|
||||
message={i18n.t("block_leaving")}
|
||||
|
@ -162,9 +168,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-12 col-form-label" htmlFor={this.id}>
|
||||
{i18n.t("sidebar")}
|
||||
</label>
|
||||
<label class="col-12 col-form-label">{i18n.t("sidebar")}</label>
|
||||
<div class="col-12">
|
||||
<MarkdownTextArea
|
||||
initialContent={this.state.siteForm.sidebar}
|
||||
|
@ -173,6 +177,20 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
{this.state.siteForm.require_application && (
|
||||
<div class="form-group row">
|
||||
<label class="col-12 col-form-label">
|
||||
{i18n.t("application_questionnaire")}
|
||||
</label>
|
||||
<div class="col-12">
|
||||
<MarkdownTextArea
|
||||
initialContent={this.state.siteForm.application_question}
|
||||
onContentChange={this.handleSiteApplicationQuestionChange}
|
||||
hideNavigationWarnings
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div class="form-group row">
|
||||
<div class="col-12">
|
||||
<div class="form-check">
|
||||
|
@ -255,6 +273,66 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-12">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
id="create-site-require-email-verification"
|
||||
type="checkbox"
|
||||
checked={this.state.siteForm.require_email_verification}
|
||||
onChange={linkEvent(
|
||||
this,
|
||||
this.handleSiteRequireEmailVerification
|
||||
)}
|
||||
/>
|
||||
<label
|
||||
class="form-check-label"
|
||||
htmlFor="create-site-require-email-verification"
|
||||
>
|
||||
{i18n.t("require_email_verification")}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-12">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
id="create-site-require-application"
|
||||
type="checkbox"
|
||||
checked={this.state.siteForm.require_application}
|
||||
onChange={linkEvent(this, this.handleSiteRequireApplication)}
|
||||
/>
|
||||
<label
|
||||
class="form-check-label"
|
||||
htmlFor="create-site-require-application"
|
||||
>
|
||||
{i18n.t("require_registration_application")}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-12">
|
||||
<div class="form-check">
|
||||
<input
|
||||
class="form-check-input"
|
||||
id="create-site-private-instance"
|
||||
type="checkbox"
|
||||
checked={this.state.siteForm.private_instance}
|
||||
onChange={linkEvent(this, this.handleSitePrivateInstance)}
|
||||
/>
|
||||
<label
|
||||
class="form-check-label"
|
||||
htmlFor="create-site-private-instance"
|
||||
>
|
||||
{i18n.t("private_instance")}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-12">
|
||||
<button
|
||||
|
@ -311,6 +389,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
this.setState(this.state);
|
||||
}
|
||||
|
||||
handleSiteApplicationQuestionChange(val: string) {
|
||||
this.state.siteForm.application_question = val;
|
||||
this.setState(this.state);
|
||||
}
|
||||
|
||||
handleSiteDescChange(i: SiteForm, event: any) {
|
||||
i.state.siteForm.description = event.target.value;
|
||||
i.setState(i.state);
|
||||
|
@ -336,6 +419,21 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
|||
i.setState(i.state);
|
||||
}
|
||||
|
||||
handleSiteRequireApplication(i: SiteForm, event: any) {
|
||||
i.state.siteForm.require_application = event.target.checked;
|
||||
i.setState(i.state);
|
||||
}
|
||||
|
||||
handleSiteRequireEmailVerification(i: SiteForm, event: any) {
|
||||
i.state.siteForm.require_email_verification = event.target.checked;
|
||||
i.setState(i.state);
|
||||
}
|
||||
|
||||
handleSitePrivateInstance(i: SiteForm, event: any) {
|
||||
i.state.siteForm.private_instance = event.target.checked;
|
||||
i.setState(i.state);
|
||||
}
|
||||
|
||||
handleCancel(i: SiteForm) {
|
||||
i.props.onCancel();
|
||||
}
|
||||
|
|
|
@ -25,9 +25,11 @@ import { i18n } from "../i18next";
|
|||
import { InitialFetchRequest } from "../interfaces";
|
||||
import { UserService, WebSocketService } from "../services";
|
||||
import {
|
||||
authField,
|
||||
fetchLimit,
|
||||
isBrowser,
|
||||
setIsoData,
|
||||
setOptionalAuth,
|
||||
toast,
|
||||
wsClient,
|
||||
wsJsonToRes,
|
||||
|
@ -482,6 +484,7 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
community_id: this.state.communityId,
|
||||
page: this.state.page,
|
||||
limit: fetchLimit,
|
||||
auth: authField(false),
|
||||
};
|
||||
WebSocketService.Instance.send(wsClient.getModlog(modlogForm));
|
||||
|
||||
|
@ -507,6 +510,7 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
if (communityId) {
|
||||
modlogForm.community_id = Number(communityId);
|
||||
}
|
||||
setOptionalAuth(modlogForm, req.auth);
|
||||
|
||||
promises.push(req.client.getModlog(modlogForm));
|
||||
|
||||
|
@ -514,6 +518,7 @@ export class Modlog extends Component<any, ModlogState> {
|
|||
let communityForm: GetCommunity = {
|
||||
id: Number(communityId),
|
||||
};
|
||||
setOptionalAuth(communityForm, req.auth);
|
||||
promises.push(req.client.getCommunity(communityForm));
|
||||
}
|
||||
return promises;
|
||||
|
|
249
src/shared/components/person/registration-applications.tsx
Normal file
249
src/shared/components/person/registration-applications.tsx
Normal file
|
@ -0,0 +1,249 @@
|
|||
import { Component, linkEvent } from "inferno";
|
||||
import {
|
||||
ListRegistrationApplications,
|
||||
ListRegistrationApplicationsResponse,
|
||||
RegistrationApplicationResponse,
|
||||
RegistrationApplicationView,
|
||||
SiteView,
|
||||
UserOperation,
|
||||
} from "lemmy-js-client";
|
||||
import { Subscription } from "rxjs";
|
||||
import { i18n } from "../../i18next";
|
||||
import { InitialFetchRequest } from "../../interfaces";
|
||||
import { UserService, WebSocketService } from "../../services";
|
||||
import {
|
||||
authField,
|
||||
fetchLimit,
|
||||
isBrowser,
|
||||
setIsoData,
|
||||
setupTippy,
|
||||
toast,
|
||||
updateRegistrationApplicationRes,
|
||||
wsClient,
|
||||
wsJsonToRes,
|
||||
wsSubscribe,
|
||||
wsUserOp,
|
||||
} from "../../utils";
|
||||
import { HtmlTags } from "../common/html-tags";
|
||||
import { Spinner } from "../common/icon";
|
||||
import { Paginator } from "../common/paginator";
|
||||
import { RegistrationApplication } from "../common/registration-application";
|
||||
|
||||
enum UnreadOrAll {
|
||||
Unread,
|
||||
All,
|
||||
}
|
||||
|
||||
interface RegistrationApplicationsState {
|
||||
applications: RegistrationApplicationView[];
|
||||
page: number;
|
||||
site_view: SiteView;
|
||||
unreadOrAll: UnreadOrAll;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export class RegistrationApplications extends Component<
|
||||
any,
|
||||
RegistrationApplicationsState
|
||||
> {
|
||||
private isoData = setIsoData(this.context);
|
||||
private subscription: Subscription;
|
||||
private emptyState: RegistrationApplicationsState = {
|
||||
unreadOrAll: UnreadOrAll.Unread,
|
||||
applications: [],
|
||||
page: 1,
|
||||
site_view: this.isoData.site_res.site_view,
|
||||
loading: true,
|
||||
};
|
||||
|
||||
constructor(props: any, context: any) {
|
||||
super(props, context);
|
||||
|
||||
this.state = this.emptyState;
|
||||
this.handlePageChange = this.handlePageChange.bind(this);
|
||||
|
||||
if (!UserService.Instance.myUserInfo && isBrowser()) {
|
||||
toast(i18n.t("not_logged_in"), "danger");
|
||||
this.context.router.history.push(`/login`);
|
||||
}
|
||||
|
||||
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.applications =
|
||||
this.isoData.routeData[0].registration_applications || []; // TODO test
|
||||
this.state.loading = false;
|
||||
} else {
|
||||
this.refetch();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
setupTippy();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (isBrowser()) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
get documentTitle(): string {
|
||||
return `@${
|
||||
UserService.Instance.myUserInfo.local_user_view.person.name
|
||||
} ${i18n.t("registration_applications")} - ${
|
||||
this.state.site_view.site.name
|
||||
}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div class="container">
|
||||
{this.state.loading ? (
|
||||
<h5>
|
||||
<Spinner large />
|
||||
</h5>
|
||||
) : (
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<HtmlTags
|
||||
title={this.documentTitle}
|
||||
path={this.context.router.route.match.url}
|
||||
/>
|
||||
<h5 class="mb-2">{i18n.t("registration_applications")}</h5>
|
||||
{this.selects()}
|
||||
{this.applicationList()}
|
||||
<Paginator
|
||||
page={this.state.page}
|
||||
onChange={this.handlePageChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
unreadOrAllRadios() {
|
||||
return (
|
||||
<div class="btn-group btn-group-toggle flex-wrap mb-2">
|
||||
<label
|
||||
className={`btn btn-outline-secondary pointer
|
||||
${this.state.unreadOrAll == UnreadOrAll.Unread && "active"}
|
||||
`}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
value={UnreadOrAll.Unread}
|
||||
checked={this.state.unreadOrAll == UnreadOrAll.Unread}
|
||||
onChange={linkEvent(this, this.handleUnreadOrAllChange)}
|
||||
/>
|
||||
{i18n.t("unread")}
|
||||
</label>
|
||||
<label
|
||||
className={`btn btn-outline-secondary pointer
|
||||
${this.state.unreadOrAll == UnreadOrAll.All && "active"}
|
||||
`}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
value={UnreadOrAll.All}
|
||||
checked={this.state.unreadOrAll == UnreadOrAll.All}
|
||||
onChange={linkEvent(this, this.handleUnreadOrAllChange)}
|
||||
/>
|
||||
{i18n.t("all")}
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
selects() {
|
||||
return (
|
||||
<div className="mb-2">
|
||||
<span class="mr-3">{this.unreadOrAllRadios()}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
applicationList() {
|
||||
return (
|
||||
<div>
|
||||
{this.state.applications.map(ra => (
|
||||
<>
|
||||
<hr />
|
||||
<RegistrationApplication
|
||||
key={ra.registration_application.id}
|
||||
application={ra}
|
||||
/>
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
handleUnreadOrAllChange(i: RegistrationApplications, event: any) {
|
||||
i.state.unreadOrAll = Number(event.target.value);
|
||||
i.state.page = 1;
|
||||
i.setState(i.state);
|
||||
i.refetch();
|
||||
}
|
||||
|
||||
handlePageChange(page: number) {
|
||||
this.setState({ page });
|
||||
this.refetch();
|
||||
}
|
||||
|
||||
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
||||
let promises: Promise<any>[] = [];
|
||||
|
||||
let form: ListRegistrationApplications = {
|
||||
unread_only: true,
|
||||
page: 1,
|
||||
limit: fetchLimit,
|
||||
auth: req.auth,
|
||||
};
|
||||
promises.push(req.client.listRegistrationApplications(form));
|
||||
|
||||
return promises;
|
||||
}
|
||||
|
||||
refetch() {
|
||||
let unread_only = this.state.unreadOrAll == UnreadOrAll.Unread;
|
||||
let form: ListRegistrationApplications = {
|
||||
unread_only: unread_only,
|
||||
page: this.state.page,
|
||||
limit: fetchLimit,
|
||||
auth: authField(),
|
||||
};
|
||||
WebSocketService.Instance.send(wsClient.listRegistrationApplications(form));
|
||||
}
|
||||
|
||||
parseMessage(msg: any) {
|
||||
let op = wsUserOp(msg);
|
||||
console.log(msg);
|
||||
if (msg.error) {
|
||||
toast(i18n.t(msg.error), "danger");
|
||||
return;
|
||||
} else if (msg.reconnect) {
|
||||
this.refetch();
|
||||
} else if (op == UserOperation.ListRegistrationApplications) {
|
||||
let data = wsJsonToRes<ListRegistrationApplicationsResponse>(msg).data;
|
||||
this.state.applications = data.registration_applications;
|
||||
this.state.loading = false;
|
||||
window.scrollTo(0, 0);
|
||||
this.setState(this.state);
|
||||
} else if (op == UserOperation.ApproveRegistrationApplication) {
|
||||
let data = wsJsonToRes<RegistrationApplicationResponse>(msg).data;
|
||||
updateRegistrationApplicationRes(
|
||||
data.registration_application,
|
||||
this.state.applications
|
||||
);
|
||||
let uacs = UserService.Instance.unreadApplicationCountSub;
|
||||
// Minor bug, where if the application switches from deny to approve, the count will still go down
|
||||
uacs.next(uacs.getValue() - 1);
|
||||
this.setState(this.state);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,11 +29,11 @@ import {
|
|||
wsSubscribe,
|
||||
wsUserOp,
|
||||
} from "../../utils";
|
||||
import { CommentReport } from "../comment/comment_report";
|
||||
import { CommentReport } from "../comment/comment-report";
|
||||
import { HtmlTags } from "../common/html-tags";
|
||||
import { Spinner } from "../common/icon";
|
||||
import { Paginator } from "../common/paginator";
|
||||
import { PostReport } from "../post/post_report";
|
||||
import { PostReport } from "../post/post-report";
|
||||
|
||||
enum UnreadOrAll {
|
||||
Unread,
|
||||
|
|
|
@ -1108,6 +1108,11 @@ export class Settings extends Component<any, SettingsState> {
|
|||
let op = wsUserOp(msg);
|
||||
console.log(msg);
|
||||
if (msg.error) {
|
||||
this.setState({
|
||||
saveUserSettingsLoading: false,
|
||||
changePasswordLoading: false,
|
||||
deleteAccountLoading: false,
|
||||
});
|
||||
toast(i18n.t(msg.error), "danger");
|
||||
return;
|
||||
} else if (op == UserOperation.SaveUserSettings) {
|
||||
|
|
97
src/shared/components/person/verify-email.tsx
Normal file
97
src/shared/components/person/verify-email.tsx
Normal file
|
@ -0,0 +1,97 @@
|
|||
import { Component } from "inferno";
|
||||
import {
|
||||
SiteView,
|
||||
UserOperation,
|
||||
VerifyEmail as VerifyEmailForm,
|
||||
VerifyEmailResponse,
|
||||
} from "lemmy-js-client";
|
||||
import { Subscription } from "rxjs";
|
||||
import { i18n } from "../../i18next";
|
||||
import { WebSocketService } from "../../services";
|
||||
import {
|
||||
isBrowser,
|
||||
setIsoData,
|
||||
toast,
|
||||
wsClient,
|
||||
wsJsonToRes,
|
||||
wsSubscribe,
|
||||
wsUserOp,
|
||||
} from "../../utils";
|
||||
import { HtmlTags } from "../common/html-tags";
|
||||
|
||||
interface State {
|
||||
verifyEmailForm: VerifyEmailForm;
|
||||
site_view: SiteView;
|
||||
}
|
||||
|
||||
export class VerifyEmail extends Component<any, State> {
|
||||
private isoData = setIsoData(this.context);
|
||||
private subscription: Subscription;
|
||||
|
||||
emptyState: State = {
|
||||
verifyEmailForm: {
|
||||
token: this.props.match.params.token,
|
||||
},
|
||||
site_view: this.isoData.site_res.site_view,
|
||||
};
|
||||
|
||||
constructor(props: any, context: any) {
|
||||
super(props, context);
|
||||
|
||||
this.state = this.emptyState;
|
||||
|
||||
this.parseMessage = this.parseMessage.bind(this);
|
||||
this.subscription = wsSubscribe(this.parseMessage);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
WebSocketService.Instance.send(
|
||||
wsClient.verifyEmail(this.state.verifyEmailForm)
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (isBrowser()) {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
get documentTitle(): string {
|
||||
return `${i18n.t("verify_email")} - ${this.state.site_view.site.name}`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div class="container">
|
||||
<HtmlTags
|
||||
title={this.documentTitle}
|
||||
path={this.context.router.route.match.url}
|
||||
/>
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||
<h5>{i18n.t("verify_email")}</h5>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
parseMessage(msg: any) {
|
||||
let op = wsUserOp(msg);
|
||||
console.log(msg);
|
||||
if (msg.error) {
|
||||
toast(i18n.t(msg.error), "danger");
|
||||
this.setState(this.state);
|
||||
this.props.history.push("/");
|
||||
return;
|
||||
} else if (op == UserOperation.VerifyEmail) {
|
||||
let data = wsJsonToRes<VerifyEmailResponse>(msg).data;
|
||||
if (data) {
|
||||
toast(i18n.t("email_verified"));
|
||||
this.state = this.emptyState;
|
||||
this.setState(this.state);
|
||||
this.props.history.push("/login");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,9 @@ export class PostReport extends Component<PostReportProps, any> {
|
|||
render() {
|
||||
let r = this.props.report;
|
||||
let post = r.post;
|
||||
let tippyContent = i18n.t(
|
||||
r.post_report.resolved ? "unresolve_report" : "resolve_report"
|
||||
);
|
||||
|
||||
// Set the original post data ( a troll could change it )
|
||||
post.name = r.post_report.original_post_name;
|
||||
|
@ -70,12 +73,8 @@ export class PostReport extends Component<PostReportProps, any> {
|
|||
<button
|
||||
className="btn btn-link btn-animate text-muted py-0"
|
||||
onClick={linkEvent(this, this.handleResolveReport)}
|
||||
data-tippy-content={
|
||||
r.post_report.resolved ? "unresolve_report" : "resolve_report"
|
||||
}
|
||||
aria-label={
|
||||
r.post_report.resolved ? "unresolve_report" : "resolve_report"
|
||||
}
|
||||
data-tippy-content={tippyContent}
|
||||
aria-label={tippyContent}
|
||||
>
|
||||
<Icon
|
||||
icon="check"
|
|
@ -6,14 +6,16 @@ import { AdminSettings } from "./components/home/admin-settings";
|
|||
import { Home } from "./components/home/home";
|
||||
import { Instances } from "./components/home/instances";
|
||||
import { Login } from "./components/home/login";
|
||||
import { PasswordChange } from "./components/home/password_change";
|
||||
import { Setup } from "./components/home/setup";
|
||||
import { Signup } from "./components/home/signup";
|
||||
import { Modlog } from "./components/modlog";
|
||||
import { Inbox } from "./components/person/inbox";
|
||||
import { PasswordChange } from "./components/person/password-change";
|
||||
import { Profile } from "./components/person/profile";
|
||||
import { RegistrationApplications } from "./components/person/registration-applications";
|
||||
import { Reports } from "./components/person/reports";
|
||||
import { Settings } from "./components/person/settings";
|
||||
import { VerifyEmail } from "./components/person/verify-email";
|
||||
import { CreatePost } from "./components/post/create-post";
|
||||
import { Post } from "./components/post/post";
|
||||
import { CreatePrivateMessage } from "./components/private_message/create-private-message";
|
||||
|
@ -128,6 +130,11 @@ export const routes: IRoutePropsWithFetch[] = [
|
|||
component: Reports,
|
||||
fetchInitialData: req => Reports.fetchInitialData(req),
|
||||
},
|
||||
{
|
||||
path: `/registration_applications`,
|
||||
component: RegistrationApplications,
|
||||
fetchInitialData: req => RegistrationApplications.fetchInitialData(req),
|
||||
},
|
||||
{
|
||||
path: `/search/q/:q/type/:type/sort/:sort/listing_type/:listing_type/community_id/:community_id/creator_id/:creator_id/page/:page`,
|
||||
component: Search,
|
||||
|
@ -142,5 +149,9 @@ export const routes: IRoutePropsWithFetch[] = [
|
|||
path: `/password_change/:token`,
|
||||
component: PasswordChange,
|
||||
},
|
||||
{
|
||||
path: `/verify_email/:token`,
|
||||
component: VerifyEmail,
|
||||
},
|
||||
{ path: `/instances`, component: Instances },
|
||||
];
|
||||
|
|
|
@ -20,6 +20,8 @@ export class UserService {
|
|||
new BehaviorSubject<number>(0);
|
||||
public unreadReportCountSub: BehaviorSubject<number> =
|
||||
new BehaviorSubject<number>(0);
|
||||
public unreadApplicationCountSub: BehaviorSubject<number> =
|
||||
new BehaviorSubject<number>(0);
|
||||
|
||||
private constructor() {
|
||||
if (this.auth) {
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
PostReportView,
|
||||
PostView,
|
||||
PrivateMessageView,
|
||||
RegistrationApplicationView,
|
||||
Search,
|
||||
SearchResponse,
|
||||
SearchType,
|
||||
|
@ -1105,6 +1106,20 @@ export function updateCommentReportRes(
|
|||
}
|
||||
}
|
||||
|
||||
export function updateRegistrationApplicationRes(
|
||||
data: RegistrationApplicationView,
|
||||
applications: RegistrationApplicationView[]
|
||||
) {
|
||||
let found = applications.find(
|
||||
ra => ra.registration_application.id == data.registration_application.id
|
||||
);
|
||||
if (found) {
|
||||
found.registration_application = data.registration_application;
|
||||
found.admin = data.admin;
|
||||
found.creator_local_user = data.creator_local_user;
|
||||
}
|
||||
}
|
||||
|
||||
export function commentsToFlatNodes(comments: CommentView[]): CommentNodeI[] {
|
||||
let nodes: CommentNodeI[] = [];
|
||||
for (let comment of comments) {
|
||||
|
|
|
@ -4997,10 +4997,10 @@ lcid@^1.0.0:
|
|||
dependencies:
|
||||
invert-kv "^1.0.0"
|
||||
|
||||
lemmy-js-client@0.14.0-rc.1:
|
||||
version "0.14.0-rc.1"
|
||||
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.14.0-rc.1.tgz#c714d5f308fa20d5244db3844630f7b197eafa1c"
|
||||
integrity sha512-UF3I+80WTYWwQg2+96HTl0O2Yv0wy6rYFjlLNyzfqMXUZBnsr1O/SdJD1/9yAFPFbGkKgWusdncLoGgzFyn8eg==
|
||||
lemmy-js-client@0.15.0-rc.6:
|
||||
version "0.15.0-rc.6"
|
||||
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.15.0-rc.6.tgz#5f8552488ed82b8c0962c158edccb8ce1d56389e"
|
||||
integrity sha512-eSEZ5+F2ScKVtx+wwjdReHirJBNLQL2YdTV4aMCBWaSsxfsXUcz18/urbNxo+fNMc7Q4u0aRd3737yKBeMP9Kw==
|
||||
|
||||
levn@^0.4.1:
|
||||
version "0.4.1"
|
||||
|
|
Loading…
Reference in a new issue