Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Private instances #523

Merged
merged 13 commits into from
Dec 30, 2021
2 changes: 1 addition & 1 deletion lemmy-translations
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>",
"license": "AGPL-3.0",
"scripts": {
Expand Down Expand Up @@ -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",
Expand Down
6 changes: 5 additions & 1 deletion src/server/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,11 @@ server.get("/*", async (req, res) => {
if (routeData[0] && routeData[0].error) {
let errCode = routeData[0].error;
console.error(errCode);
return res.redirect(`/404?err=${errCode}`);
if (errCode == "instance_is_private") {
return res.redirect(`/signup`);
} else {
return res.redirect(`/404?err=${errCode}`);
}
}

let isoData: IsoData = {
Expand Down
86 changes: 84 additions & 2 deletions src/shared/components/app/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
GetSiteResponse,
GetUnreadCount,
GetUnreadCountResponse,
GetUnreadRegistrationApplicationCount,
GetUnreadRegistrationApplicationCountResponse,
PrivateMessageResponse,
UserOperation,
} from "lemmy-js-client";
Expand Down Expand Up @@ -41,6 +43,7 @@ interface NavbarState {
expanded: boolean;
unreadInboxCount: number;
unreadReportCount: number;
unreadApplicationCount: number;
searchParam: string;
toggleSearch: boolean;
showDropdown: boolean;
Expand All @@ -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,
Expand Down Expand Up @@ -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 });
});
}
}

Expand All @@ -123,6 +133,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
this.userSub.unsubscribe();
this.unreadInboxCountSub.unsubscribe();
this.unreadReportCountSub.unsubscribe();
this.unreadApplicationCountSub.unsubscribe();
}

updateUrl() {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -586,16 +653,25 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
let unreadForm: GetUnreadCount = {
auth: authField(),
};

WebSocketService.Instance.send(wsClient.getUnreadCount(unreadForm));

console.log("Fetching reports...");

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() {
Expand All @@ -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 &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion src/shared/components/common/markdown-textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import { Icon, Spinner } from "./icon";

interface MarkdownTextAreaProps {
initialContent: string;
initialContent?: string;
finished?: boolean;
buttonTitle?: string;
replyType?: boolean;
Expand Down
150 changes: 150 additions & 0 deletions src/shared/components/common/registration-application.tsx
Original file line number Diff line number Diff line change
@@ -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);
}
}
3 changes: 3 additions & 0 deletions src/shared/components/common/symbols.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Loading