mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-29 07:41:13 +00:00
* Removing monads. Fixes #884 * Fixing post fetching. * Dont show not_logged_in error for navbar. * Adding the lemmy-js-client RC. * Fixing registration application mode
This commit is contained in:
parent
37c200571b
commit
b64f47cfe9
66 changed files with 5186 additions and 6250 deletions
|
@ -1 +1 @@
|
||||||
Subproject commit c97005696d132acf2cad3506df4f3c2256142349
|
Subproject commit 975c922271f27920aef51a2c3ae9f24714c44004
|
|
@ -23,13 +23,11 @@
|
||||||
"@babel/preset-env": "7.19.3",
|
"@babel/preset-env": "7.19.3",
|
||||||
"@babel/preset-typescript": "^7.18.6",
|
"@babel/preset-typescript": "^7.18.6",
|
||||||
"@babel/runtime": "^7.18.9",
|
"@babel/runtime": "^7.18.9",
|
||||||
"@sniptt/monads": "^0.5.10",
|
|
||||||
"autosize": "^5.0.1",
|
"autosize": "^5.0.1",
|
||||||
"babel-loader": "^8.2.5",
|
"babel-loader": "^8.2.5",
|
||||||
"babel-plugin-inferno": "^6.5.0",
|
"babel-plugin-inferno": "^6.5.0",
|
||||||
"check-password-strength": "^2.0.7",
|
"check-password-strength": "^2.0.7",
|
||||||
"choices.js": "^10.1.0",
|
"choices.js": "^10.1.0",
|
||||||
"class-transformer": "^0.5.1",
|
|
||||||
"classnames": "^2.3.1",
|
"classnames": "^2.3.1",
|
||||||
"clean-webpack-plugin": "^4.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
"copy-webpack-plugin": "^11.0.0",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
|
@ -47,7 +45,7 @@
|
||||||
"inferno-server": "^8.0.5",
|
"inferno-server": "^8.0.5",
|
||||||
"isomorphic-cookie": "^1.2.4",
|
"isomorphic-cookie": "^1.2.4",
|
||||||
"jwt-decode": "^3.1.2",
|
"jwt-decode": "^3.1.2",
|
||||||
"lemmy-js-client": "0.17.0-rc.57",
|
"lemmy-js-client": "0.17.0-rc.61",
|
||||||
"markdown-it": "^13.0.1",
|
"markdown-it": "^13.0.1",
|
||||||
"markdown-it-container": "^3.0.0",
|
"markdown-it-container": "^3.0.0",
|
||||||
"markdown-it-footnote": "^3.0.3",
|
"markdown-it-footnote": "^3.0.3",
|
||||||
|
@ -57,7 +55,6 @@
|
||||||
"mini-css-extract-plugin": "^2.6.1",
|
"mini-css-extract-plugin": "^2.6.1",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
"reflect-metadata": "^0.1.13",
|
|
||||||
"register-service-worker": "^1.7.2",
|
"register-service-worker": "^1.7.2",
|
||||||
"run-node-webpack-plugin": "^1.3.0",
|
"run-node-webpack-plugin": "^1.3.0",
|
||||||
"rxjs": "^7.5.6",
|
"rxjs": "^7.5.6",
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { hydrate } from "inferno-hydrate";
|
import { hydrate } from "inferno-hydrate";
|
||||||
import { BrowserRouter } from "inferno-router";
|
import { BrowserRouter } from "inferno-router";
|
||||||
import { GetSiteResponse } from "lemmy-js-client";
|
|
||||||
import { App } from "../shared/components/app/app";
|
import { App } from "../shared/components/app/app";
|
||||||
import { convertWindowJson, initializeSite } from "../shared/utils";
|
import { initializeSite } from "../shared/utils";
|
||||||
|
|
||||||
const site = convertWindowJson(GetSiteResponse, window.isoData.site_res);
|
const site = window.isoData.site_res;
|
||||||
initializeSite(site);
|
initializeSite(site);
|
||||||
|
|
||||||
const wrapper = (
|
const wrapper = (
|
||||||
|
@ -13,4 +12,7 @@ const wrapper = (
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
);
|
);
|
||||||
|
|
||||||
hydrate(wrapper, document.getElementById("root"));
|
let root = document.getElementById("root");
|
||||||
|
if (root) {
|
||||||
|
hydrate(wrapper, root);
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import { None, Option } from "@sniptt/monads";
|
|
||||||
import { serialize as serializeO } from "class-transformer";
|
|
||||||
import express from "express";
|
import express from "express";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { IncomingHttpHeaders } from "http";
|
import { IncomingHttpHeaders } from "http";
|
||||||
|
@ -7,7 +5,7 @@ import { Helmet } from "inferno-helmet";
|
||||||
import { matchPath, StaticRouter } from "inferno-router";
|
import { matchPath, StaticRouter } from "inferno-router";
|
||||||
import { renderToString } from "inferno-server";
|
import { renderToString } from "inferno-server";
|
||||||
import IsomorphicCookie from "isomorphic-cookie";
|
import IsomorphicCookie from "isomorphic-cookie";
|
||||||
import { GetSite, GetSiteResponse, LemmyHttp, toOption } from "lemmy-js-client";
|
import { GetSite, GetSiteResponse, LemmyHttp } from "lemmy-js-client";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import process from "process";
|
import process from "process";
|
||||||
import serialize from "serialize-javascript";
|
import serialize from "serialize-javascript";
|
||||||
|
@ -114,11 +112,11 @@ server.get("/css/themelist", async (_req, res) => {
|
||||||
// server.use(cookieParser());
|
// server.use(cookieParser());
|
||||||
server.get("/*", async (req, res) => {
|
server.get("/*", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const activeRoute = routes.find(route => matchPath(req.path, route)) || {};
|
const activeRoute = routes.find(route => matchPath(req.path, route));
|
||||||
const context = {} as any;
|
const context = {} as any;
|
||||||
let auth: Option<string> = toOption(IsomorphicCookie.load("jwt", req));
|
let auth: string | undefined = IsomorphicCookie.load("jwt", req);
|
||||||
|
|
||||||
let getSiteForm = new GetSite({ auth });
|
let getSiteForm: GetSite = { auth };
|
||||||
|
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
|
@ -138,14 +136,14 @@ server.get("/*", async (req, res) => {
|
||||||
console.error(
|
console.error(
|
||||||
"Incorrect JWT token, skipping auth so frontend can remove jwt cookie"
|
"Incorrect JWT token, skipping auth so frontend can remove jwt cookie"
|
||||||
);
|
);
|
||||||
getSiteForm.auth = None;
|
getSiteForm.auth = undefined;
|
||||||
initialFetchReq.auth = None;
|
initialFetchReq.auth = undefined;
|
||||||
try_site = await initialFetchReq.client.getSite(getSiteForm);
|
try_site = await initialFetchReq.client.getSite(getSiteForm);
|
||||||
}
|
}
|
||||||
let site: GetSiteResponse = try_site;
|
let site: GetSiteResponse = try_site;
|
||||||
initializeSite(site);
|
initializeSite(site);
|
||||||
|
|
||||||
if (activeRoute.fetchInitialData) {
|
if (activeRoute?.fetchInitialData) {
|
||||||
promises.push(...activeRoute.fetchInitialData(initialFetchReq));
|
promises.push(...activeRoute.fetchInitialData(initialFetchReq));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +191,7 @@ server.get("/*", async (req, res) => {
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html ${helmet.htmlAttributes.toString()} lang="en">
|
<html ${helmet.htmlAttributes.toString()} lang="en">
|
||||||
<head>
|
<head>
|
||||||
<script>window.isoData = ${serializeO(isoData)}</script>
|
<script>window.isoData = ${JSON.stringify(isoData)}</script>
|
||||||
<script>window.lemmyConfig = ${serialize(config)}</script>
|
<script>window.lemmyConfig = ${serialize(config)}</script>
|
||||||
|
|
||||||
<!-- A remote debugging utility for mobile -->
|
<!-- A remote debugging utility for mobile -->
|
||||||
|
@ -246,14 +244,17 @@ server.listen(Number(port), hostname, () => {
|
||||||
function setForwardedHeaders(headers: IncomingHttpHeaders): {
|
function setForwardedHeaders(headers: IncomingHttpHeaders): {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
} {
|
} {
|
||||||
let out = {
|
let out: { [key: string]: string } = {};
|
||||||
host: headers.host,
|
if (headers.host) {
|
||||||
};
|
out.host = headers.host;
|
||||||
if (headers["x-real-ip"]) {
|
|
||||||
out["x-real-ip"] = headers["x-real-ip"];
|
|
||||||
}
|
}
|
||||||
if (headers["x-forwarded-for"]) {
|
let realIp = headers["x-real-ip"];
|
||||||
out["x-forwarded-for"] = headers["x-forwarded-for"];
|
if (realIp) {
|
||||||
|
out["x-real-ip"] = realIp as string;
|
||||||
|
}
|
||||||
|
let forwardedFor = headers["x-forwarded-for"];
|
||||||
|
if (forwardedFor) {
|
||||||
|
out["x-forwarded-for"] = forwardedFor as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
|
|
@ -19,37 +19,37 @@ export class App extends Component<any, any> {
|
||||||
render() {
|
render() {
|
||||||
let siteRes = this.isoData.site_res;
|
let siteRes = this.isoData.site_res;
|
||||||
let siteView = siteRes.site_view;
|
let siteView = siteRes.site_view;
|
||||||
|
let icon = siteView.site.icon;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Provider i18next={i18n}>
|
<Provider i18next={i18n}>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<Theme defaultTheme={siteView.local_site.default_theme} />
|
<Theme defaultTheme={siteView.local_site.default_theme} />
|
||||||
{siteView.site.icon.match({
|
{icon && (
|
||||||
some: icon => (
|
<Helmet>
|
||||||
<Helmet>
|
<link
|
||||||
<link
|
id="favicon"
|
||||||
id="favicon"
|
rel="shortcut icon"
|
||||||
rel="shortcut icon"
|
type="image/x-icon"
|
||||||
type="image/x-icon"
|
href={icon || favIconUrl}
|
||||||
href={icon || favIconUrl}
|
/>
|
||||||
/>
|
<link rel="apple-touch-icon" href={icon || favIconPngUrl} />
|
||||||
<link rel="apple-touch-icon" href={icon || favIconPngUrl} />
|
</Helmet>
|
||||||
</Helmet>
|
)}
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
<Navbar siteRes={siteRes} />
|
<Navbar siteRes={siteRes} />
|
||||||
<div className="mt-4 p-0 fl-1">
|
<div className="mt-4 p-0 fl-1">
|
||||||
<Switch>
|
<Switch>
|
||||||
{routes.map(({ path, exact, component: C, ...rest }) => (
|
{routes.map(
|
||||||
<Route
|
({ path, exact, component: Component, ...rest }) => (
|
||||||
key={path}
|
<Route
|
||||||
path={path}
|
key={path}
|
||||||
exact={exact}
|
path={path}
|
||||||
render={props => <C {...props} {...rest} />}
|
exact={exact}
|
||||||
/>
|
render={props => <Component {...props} {...rest} />}
|
||||||
))}
|
/>
|
||||||
|
)
|
||||||
|
)}
|
||||||
<Route render={props => <NoMatch {...props} />} />
|
<Route render={props => <NoMatch {...props} />} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -32,7 +32,7 @@ export class Footer extends Component<FooterProps, any> {
|
||||||
{i18n.t("modlog")}
|
{i18n.t("modlog")}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
{this.props.site.site_view.local_site.legal_information.isSome() && (
|
{this.props.site.site_view.local_site.legal_information && (
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink className="nav-link" to="/legal">
|
<NavLink className="nav-link" to="/legal">
|
||||||
{i18n.t("legal_information")}
|
{i18n.t("legal_information")}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None } from "@sniptt/monads";
|
|
||||||
import { Component, createRef, linkEvent, RefObject } from "inferno";
|
import { Component, createRef, linkEvent, RefObject } from "inferno";
|
||||||
import { NavLink } from "inferno-router";
|
import { NavLink } from "inferno-router";
|
||||||
import {
|
import {
|
||||||
|
@ -20,10 +19,10 @@ import { i18n } from "../../i18next";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
amAdmin,
|
amAdmin,
|
||||||
auth,
|
|
||||||
canCreateCommunity,
|
canCreateCommunity,
|
||||||
donateLemmyUrl,
|
donateLemmyUrl,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
|
myAuth,
|
||||||
notifyComment,
|
notifyComment,
|
||||||
notifyPrivateMessage,
|
notifyPrivateMessage,
|
||||||
numToSI,
|
numToSI,
|
||||||
|
@ -57,7 +56,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
private unreadReportCountSub: Subscription;
|
private unreadReportCountSub: Subscription;
|
||||||
private unreadApplicationCountSub: Subscription;
|
private unreadApplicationCountSub: Subscription;
|
||||||
private searchTextField: RefObject<HTMLInputElement>;
|
private searchTextField: RefObject<HTMLInputElement>;
|
||||||
emptyState: NavbarState = {
|
state: NavbarState = {
|
||||||
unreadInboxCount: 0,
|
unreadInboxCount: 0,
|
||||||
unreadReportCount: 0,
|
unreadReportCount: 0,
|
||||||
unreadApplicationCount: 0,
|
unreadApplicationCount: 0,
|
||||||
|
@ -70,7 +69,6 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
@ -82,11 +80,12 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
this.searchTextField = createRef();
|
this.searchTextField = createRef();
|
||||||
|
|
||||||
// On the first load, check the unreads
|
// On the first load, check the unreads
|
||||||
if (UserService.Instance.myUserInfo.isSome()) {
|
let auth = myAuth(false);
|
||||||
|
if (auth && UserService.Instance.myUserInfo) {
|
||||||
this.requestNotificationPermission();
|
this.requestNotificationPermission();
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.userJoin({
|
wsClient.userJoin({
|
||||||
auth: auth().unwrap(),
|
auth,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -143,22 +142,22 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
// TODO class active corresponding to current page
|
// TODO class active corresponding to current page
|
||||||
navbar() {
|
navbar() {
|
||||||
let siteView = this.props.siteRes.site_view;
|
let siteView = this.props.siteRes.site_view;
|
||||||
|
let person = UserService.Instance.myUserInfo?.local_user_view.person;
|
||||||
return (
|
return (
|
||||||
<nav className="navbar navbar-expand-md navbar-light shadow-sm p-0 px-3">
|
<nav className="navbar navbar-expand-md navbar-light shadow-sm p-0 px-3">
|
||||||
<div className="container-lg">
|
<div className="container-lg">
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/"
|
to="/"
|
||||||
onMouseUp={linkEvent(this, this.handleHideExpandNavbar)}
|
onMouseUp={linkEvent(this, this.handleHideExpandNavbar)}
|
||||||
title={siteView.site.description.unwrapOr(siteView.site.name)}
|
title={siteView.site.description ?? siteView.site.name}
|
||||||
className="d-flex align-items-center navbar-brand mr-md-3"
|
className="d-flex align-items-center navbar-brand mr-md-3"
|
||||||
>
|
>
|
||||||
{siteView.site.icon.match({
|
{siteView.site.icon && showAvatars() && (
|
||||||
some: icon => showAvatars() && <PictrsImage src={icon} icon />,
|
<PictrsImage src={siteView.site.icon} icon />
|
||||||
none: <></>,
|
)}
|
||||||
})}
|
|
||||||
{siteView.site.name}
|
{siteView.site.name}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
{UserService.Instance.myUserInfo.isSome() && (
|
{UserService.Instance.myUserInfo && (
|
||||||
<>
|
<>
|
||||||
<ul className="navbar-nav ml-auto">
|
<ul className="navbar-nav ml-auto">
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
|
@ -341,7 +340,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
{UserService.Instance.myUserInfo.isSome() ? (
|
{UserService.Instance.myUserInfo ? (
|
||||||
<>
|
<>
|
||||||
<ul className="navbar-nav my-2">
|
<ul className="navbar-nav my-2">
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
|
@ -409,81 +408,69 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
{UserService.Instance.myUserInfo
|
{person && (
|
||||||
.map(m => m.local_user_view.person)
|
<ul className="navbar-nav">
|
||||||
.match({
|
<li className="nav-item dropdown">
|
||||||
some: person => (
|
<button
|
||||||
<ul className="navbar-nav">
|
className="nav-link btn btn-link dropdown-toggle"
|
||||||
<li className="nav-item dropdown">
|
onClick={linkEvent(this, this.handleToggleDropdown)}
|
||||||
<button
|
id="navbarDropdown"
|
||||||
className="nav-link btn btn-link dropdown-toggle"
|
role="button"
|
||||||
onClick={linkEvent(this, this.handleToggleDropdown)}
|
aria-expanded="false"
|
||||||
id="navbarDropdown"
|
>
|
||||||
role="button"
|
<span>
|
||||||
aria-expanded="false"
|
{showAvatars() && person.avatar && (
|
||||||
>
|
<PictrsImage src={person.avatar} icon />
|
||||||
<span>
|
|
||||||
{showAvatars() &&
|
|
||||||
person.avatar.match({
|
|
||||||
some: avatar => (
|
|
||||||
<PictrsImage src={avatar} icon />
|
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
{person.display_name.unwrapOr(person.name)}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
{this.state.showDropdown && (
|
|
||||||
<div
|
|
||||||
className="dropdown-content"
|
|
||||||
onMouseLeave={linkEvent(
|
|
||||||
this,
|
|
||||||
this.handleToggleDropdown
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<li className="nav-item">
|
|
||||||
<NavLink
|
|
||||||
to={`/u/${person.name}`}
|
|
||||||
className="nav-link"
|
|
||||||
title={i18n.t("profile")}
|
|
||||||
>
|
|
||||||
<Icon icon="user" classes="mr-1" />
|
|
||||||
{i18n.t("profile")}
|
|
||||||
</NavLink>
|
|
||||||
</li>
|
|
||||||
<li className="nav-item">
|
|
||||||
<NavLink
|
|
||||||
to="/settings"
|
|
||||||
className="nav-link"
|
|
||||||
title={i18n.t("settings")}
|
|
||||||
>
|
|
||||||
<Icon icon="settings" classes="mr-1" />
|
|
||||||
{i18n.t("settings")}
|
|
||||||
</NavLink>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<hr className="dropdown-divider" />
|
|
||||||
</li>
|
|
||||||
<li className="nav-item">
|
|
||||||
<button
|
|
||||||
className="nav-link btn btn-link"
|
|
||||||
onClick={linkEvent(
|
|
||||||
this,
|
|
||||||
this.handleLogoutClick
|
|
||||||
)}
|
|
||||||
title="test"
|
|
||||||
>
|
|
||||||
<Icon icon="log-out" classes="mr-1" />
|
|
||||||
{i18n.t("logout")}
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</li>
|
{person.display_name ?? person.name}
|
||||||
</ul>
|
</span>
|
||||||
),
|
</button>
|
||||||
none: <></>,
|
{this.state.showDropdown && (
|
||||||
})}
|
<div
|
||||||
|
className="dropdown-content"
|
||||||
|
onMouseLeave={linkEvent(
|
||||||
|
this,
|
||||||
|
this.handleToggleDropdown
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<li className="nav-item">
|
||||||
|
<NavLink
|
||||||
|
to={`/u/${person.name}`}
|
||||||
|
className="nav-link"
|
||||||
|
title={i18n.t("profile")}
|
||||||
|
>
|
||||||
|
<Icon icon="user" classes="mr-1" />
|
||||||
|
{i18n.t("profile")}
|
||||||
|
</NavLink>
|
||||||
|
</li>
|
||||||
|
<li className="nav-item">
|
||||||
|
<NavLink
|
||||||
|
to="/settings"
|
||||||
|
className="nav-link"
|
||||||
|
title={i18n.t("settings")}
|
||||||
|
>
|
||||||
|
<Icon icon="settings" classes="mr-1" />
|
||||||
|
{i18n.t("settings")}
|
||||||
|
</NavLink>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<hr className="dropdown-divider" />
|
||||||
|
</li>
|
||||||
|
<li className="nav-item">
|
||||||
|
<button
|
||||||
|
className="nav-link btn btn-link"
|
||||||
|
onClick={linkEvent(this, this.handleLogoutClick)}
|
||||||
|
title="test"
|
||||||
|
>
|
||||||
|
<Icon icon="log-out" classes="mr-1" />
|
||||||
|
{i18n.t("logout")}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<ul className="navbar-nav my-2">
|
<ul className="navbar-nav my-2">
|
||||||
|
@ -516,11 +503,9 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get moderatesSomething(): boolean {
|
get moderatesSomething(): boolean {
|
||||||
return (
|
let mods = UserService.Instance.myUserInfo?.moderates;
|
||||||
amAdmin() ||
|
let moderatesS = (mods && mods.length > 0) || false;
|
||||||
UserService.Instance.myUserInfo.map(m => m.moderates).unwrapOr([])
|
return amAdmin() || moderatesS;
|
||||||
.length > 0
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleToggleExpandNavbar(i: Navbar) {
|
handleToggleExpandNavbar(i: Navbar) {
|
||||||
|
@ -544,9 +529,9 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.setState({ toggleSearch: true });
|
i.setState({ toggleSearch: true });
|
||||||
|
|
||||||
i.searchTextField.current.focus();
|
i.searchTextField.current?.focus();
|
||||||
const offsetWidth = i.searchTextField.current.offsetWidth;
|
const offsetWidth = i.searchTextField.current?.offsetWidth;
|
||||||
if (i.state.searchParam && offsetWidth > 100) {
|
if (i.state.searchParam && offsetWidth && offsetWidth > 100) {
|
||||||
i.updateUrl();
|
i.updateUrl();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -576,109 +561,91 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
return;
|
return;
|
||||||
} else if (msg.reconnect) {
|
} else if (msg.reconnect) {
|
||||||
console.log(i18n.t("websocket_reconnected"));
|
console.log(i18n.t("websocket_reconnected"));
|
||||||
if (UserService.Instance.myUserInfo.isSome()) {
|
let auth = myAuth(false);
|
||||||
|
if (UserService.Instance.myUserInfo && auth) {
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.userJoin({
|
wsClient.userJoin({
|
||||||
auth: auth().unwrap(),
|
auth,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
this.fetchUnreads();
|
this.fetchUnreads();
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.GetUnreadCount) {
|
} else if (op == UserOperation.GetUnreadCount) {
|
||||||
let data = wsJsonToRes<GetUnreadCountResponse>(
|
let data = wsJsonToRes<GetUnreadCountResponse>(msg);
|
||||||
msg,
|
|
||||||
GetUnreadCountResponse
|
|
||||||
);
|
|
||||||
this.setState({
|
this.setState({
|
||||||
unreadInboxCount: data.replies + data.mentions + data.private_messages,
|
unreadInboxCount: data.replies + data.mentions + data.private_messages,
|
||||||
});
|
});
|
||||||
this.sendUnreadCount();
|
this.sendUnreadCount();
|
||||||
} else if (op == UserOperation.GetReportCount) {
|
} else if (op == UserOperation.GetReportCount) {
|
||||||
let data = wsJsonToRes<GetReportCountResponse>(
|
let data = wsJsonToRes<GetReportCountResponse>(msg);
|
||||||
msg,
|
|
||||||
GetReportCountResponse
|
|
||||||
);
|
|
||||||
this.setState({
|
this.setState({
|
||||||
unreadReportCount:
|
unreadReportCount:
|
||||||
data.post_reports +
|
data.post_reports +
|
||||||
data.comment_reports +
|
data.comment_reports +
|
||||||
data.private_message_reports.unwrapOr(0),
|
(data.private_message_reports ?? 0),
|
||||||
});
|
});
|
||||||
this.sendReportUnread();
|
this.sendReportUnread();
|
||||||
} else if (op == UserOperation.GetUnreadRegistrationApplicationCount) {
|
} else if (op == UserOperation.GetUnreadRegistrationApplicationCount) {
|
||||||
let data = wsJsonToRes<GetUnreadRegistrationApplicationCountResponse>(
|
let data =
|
||||||
msg,
|
wsJsonToRes<GetUnreadRegistrationApplicationCountResponse>(msg);
|
||||||
GetUnreadRegistrationApplicationCountResponse
|
|
||||||
);
|
|
||||||
this.setState({ unreadApplicationCount: data.registration_applications });
|
this.setState({ unreadApplicationCount: data.registration_applications });
|
||||||
this.sendApplicationUnread();
|
this.sendApplicationUnread();
|
||||||
} else if (op == UserOperation.CreateComment) {
|
} else if (op == UserOperation.CreateComment) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
|
let mui = UserService.Instance.myUserInfo;
|
||||||
UserService.Instance.myUserInfo.match({
|
if (
|
||||||
some: mui => {
|
mui &&
|
||||||
if (data.recipient_ids.includes(mui.local_user_view.local_user.id)) {
|
data.recipient_ids.includes(mui.local_user_view.local_user.id)
|
||||||
this.setState({
|
) {
|
||||||
unreadInboxCount: this.state.unreadInboxCount + 1,
|
this.setState({
|
||||||
});
|
unreadInboxCount: this.state.unreadInboxCount + 1,
|
||||||
this.sendUnreadCount();
|
});
|
||||||
notifyComment(data.comment_view, this.context.router);
|
this.sendUnreadCount();
|
||||||
}
|
notifyComment(data.comment_view, this.context.router);
|
||||||
},
|
}
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
} else if (op == UserOperation.CreatePrivateMessage) {
|
} else if (op == UserOperation.CreatePrivateMessage) {
|
||||||
let data = wsJsonToRes<PrivateMessageResponse>(
|
let data = wsJsonToRes<PrivateMessageResponse>(msg);
|
||||||
msg,
|
|
||||||
PrivateMessageResponse
|
|
||||||
);
|
|
||||||
|
|
||||||
UserService.Instance.myUserInfo.match({
|
if (
|
||||||
some: mui => {
|
data.private_message_view.recipient.id ==
|
||||||
if (
|
UserService.Instance.myUserInfo?.local_user_view.person.id
|
||||||
data.private_message_view.recipient.id ==
|
) {
|
||||||
mui.local_user_view.person.id
|
this.setState({
|
||||||
) {
|
unreadInboxCount: this.state.unreadInboxCount + 1,
|
||||||
this.setState({
|
});
|
||||||
unreadInboxCount: this.state.unreadInboxCount + 1,
|
this.sendUnreadCount();
|
||||||
});
|
notifyPrivateMessage(data.private_message_view, this.context.router);
|
||||||
this.sendUnreadCount();
|
}
|
||||||
notifyPrivateMessage(
|
|
||||||
data.private_message_view,
|
|
||||||
this.context.router
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchUnreads() {
|
fetchUnreads() {
|
||||||
console.log("Fetching inbox unreads...");
|
console.log("Fetching inbox unreads...");
|
||||||
|
|
||||||
let unreadForm = new GetUnreadCount({
|
let auth = myAuth();
|
||||||
auth: auth().unwrap(),
|
if (auth) {
|
||||||
});
|
let unreadForm: GetUnreadCount = {
|
||||||
WebSocketService.Instance.send(wsClient.getUnreadCount(unreadForm));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.getUnreadCount(unreadForm));
|
||||||
|
|
||||||
console.log("Fetching reports...");
|
console.log("Fetching reports...");
|
||||||
|
|
||||||
let reportCountForm = new GetReportCount({
|
let reportCountForm: GetReportCount = {
|
||||||
community_id: None,
|
auth,
|
||||||
auth: auth().unwrap(),
|
};
|
||||||
});
|
WebSocketService.Instance.send(wsClient.getReportCount(reportCountForm));
|
||||||
WebSocketService.Instance.send(wsClient.getReportCount(reportCountForm));
|
|
||||||
|
|
||||||
if (amAdmin()) {
|
if (amAdmin()) {
|
||||||
console.log("Fetching applications...");
|
console.log("Fetching applications...");
|
||||||
|
|
||||||
let applicationCountForm = new GetUnreadRegistrationApplicationCount({
|
let applicationCountForm: GetUnreadRegistrationApplicationCount = {
|
||||||
auth: auth().unwrap(),
|
auth,
|
||||||
});
|
};
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.getUnreadRegistrationApplicationCount(applicationCountForm)
|
wsClient.getUnreadRegistrationApplicationCount(applicationCountForm)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,7 +670,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
requestNotificationPermission() {
|
requestNotificationPermission() {
|
||||||
if (UserService.Instance.myUserInfo.isSome()) {
|
if (UserService.Instance.myUserInfo) {
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
if (!Notification) {
|
if (!Notification) {
|
||||||
toast(i18n.t("notifications_error"), "danger");
|
toast(i18n.t("notifications_error"), "danger");
|
||||||
|
|
|
@ -9,19 +9,15 @@ interface Props {
|
||||||
export class Theme extends Component<Props> {
|
export class Theme extends Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
let user = UserService.Instance.myUserInfo;
|
let user = UserService.Instance.myUserInfo;
|
||||||
let hasTheme = user
|
let hasTheme = user?.local_user_view.local_user.theme !== "browser";
|
||||||
.map(m => m.local_user_view.local_user.theme !== "browser")
|
|
||||||
.unwrapOr(false);
|
|
||||||
|
|
||||||
if (hasTheme) {
|
if (user && hasTheme) {
|
||||||
return (
|
return (
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
type="text/css"
|
type="text/css"
|
||||||
href={`/css/themes/${
|
href={`/css/themes/${user.local_user_view.local_user.theme}.css`}
|
||||||
user.unwrap().local_user_view.local_user.theme
|
|
||||||
}.css`}
|
|
||||||
/>
|
/>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Either, None, Option, Some } from "@sniptt/monads";
|
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import { T } from "inferno-i18next-dess";
|
import { T } from "inferno-i18next-dess";
|
||||||
import { Link } from "inferno-router";
|
import { Link } from "inferno-router";
|
||||||
|
@ -16,8 +15,8 @@ import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
|
myAuth,
|
||||||
myFirstDiscussionLanguageId,
|
myFirstDiscussionLanguageId,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
|
@ -29,7 +28,7 @@ interface CommentFormProps {
|
||||||
/**
|
/**
|
||||||
* Can either be the parent, or the editable comment. The right side is a postId.
|
* Can either be the parent, or the editable comment. The right side is a postId.
|
||||||
*/
|
*/
|
||||||
node: Either<CommentNodeI, number>;
|
node: CommentNodeI | number;
|
||||||
edit?: boolean;
|
edit?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
|
@ -41,19 +40,19 @@ interface CommentFormProps {
|
||||||
interface CommentFormState {
|
interface CommentFormState {
|
||||||
buttonTitle: string;
|
buttonTitle: string;
|
||||||
finished: boolean;
|
finished: boolean;
|
||||||
formId: Option<string>;
|
formId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
private emptyState: CommentFormState = {
|
state: CommentFormState = {
|
||||||
buttonTitle: this.props.node.isRight()
|
buttonTitle:
|
||||||
? capitalizeFirstLetter(i18n.t("post"))
|
typeof this.props.node === "number"
|
||||||
: this.props.edit
|
? capitalizeFirstLetter(i18n.t("post"))
|
||||||
? capitalizeFirstLetter(i18n.t("save"))
|
: this.props.edit
|
||||||
: capitalizeFirstLetter(i18n.t("reply")),
|
? capitalizeFirstLetter(i18n.t("save"))
|
||||||
|
: capitalizeFirstLetter(i18n.t("reply")),
|
||||||
finished: false,
|
finished: false,
|
||||||
formId: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -62,50 +61,46 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
this.handleCommentSubmit = this.handleCommentSubmit.bind(this);
|
this.handleCommentSubmit = this.handleCommentSubmit.bind(this);
|
||||||
this.handleReplyCancel = this.handleReplyCancel.bind(this);
|
this.handleReplyCancel = this.handleReplyCancel.bind(this);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let initialContent = this.props.node.match({
|
let initialContent =
|
||||||
left: node =>
|
typeof this.props.node !== "number"
|
||||||
this.props.edit ? Some(node.comment_view.comment.content) : None,
|
? this.props.edit
|
||||||
right: () => None,
|
? this.props.node.comment_view.comment.content
|
||||||
});
|
: undefined
|
||||||
|
: undefined;
|
||||||
|
|
||||||
let selectedLang = this.props.node
|
let selectedLang =
|
||||||
.left()
|
typeof this.props.node !== "number"
|
||||||
.map(n => n.comment_view.comment.language_id)
|
? this.props.node.comment_view.comment.language_id
|
||||||
.or(
|
: myFirstDiscussionLanguageId(
|
||||||
myFirstDiscussionLanguageId(
|
this.props.allLanguages,
|
||||||
this.props.allLanguages,
|
this.props.siteLanguages,
|
||||||
this.props.siteLanguages,
|
UserService.Instance.myUserInfo
|
||||||
UserService.Instance.myUserInfo
|
);
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
{UserService.Instance.myUserInfo.isSome() ? (
|
{UserService.Instance.myUserInfo ? (
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={initialContent}
|
initialContent={initialContent}
|
||||||
initialLanguageId={selectedLang}
|
initialLanguageId={selectedLang}
|
||||||
showLanguage
|
showLanguage
|
||||||
buttonTitle={Some(this.state.buttonTitle)}
|
buttonTitle={this.state.buttonTitle}
|
||||||
maxLength={None}
|
|
||||||
finished={this.state.finished}
|
finished={this.state.finished}
|
||||||
replyType={this.props.node.isLeft()}
|
replyType={typeof this.props.node !== "number"}
|
||||||
focus={this.props.focus}
|
focus={this.props.focus}
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
onSubmit={this.handleCommentSubmit}
|
onSubmit={this.handleCommentSubmit}
|
||||||
onReplyCancel={this.handleReplyCancel}
|
onReplyCancel={this.handleReplyCancel}
|
||||||
placeholder={Some(i18n.t("comment_here"))}
|
placeholder={i18n.t("comment_here")}
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
siteLanguages={this.props.siteLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
/>
|
/>
|
||||||
|
@ -125,55 +120,55 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommentSubmit(msg: {
|
handleCommentSubmit(msg: {
|
||||||
val: Option<string>;
|
val: string;
|
||||||
formId: string;
|
formId: string;
|
||||||
languageId: Option<number>;
|
languageId?: number;
|
||||||
}) {
|
}) {
|
||||||
let content = msg.val;
|
let content = msg.val;
|
||||||
let language_id = msg.languageId;
|
let language_id = msg.languageId;
|
||||||
this.setState({ formId: Some(msg.formId) });
|
let node = this.props.node;
|
||||||
|
|
||||||
this.props.node.match({
|
this.setState({ formId: msg.formId });
|
||||||
left: node => {
|
|
||||||
|
let auth = myAuth();
|
||||||
|
if (auth) {
|
||||||
|
if (typeof node === "number") {
|
||||||
|
let postId = node;
|
||||||
|
let form: CreateComment = {
|
||||||
|
content,
|
||||||
|
form_id: this.state.formId,
|
||||||
|
post_id: postId,
|
||||||
|
language_id,
|
||||||
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.createComment(form));
|
||||||
|
} else {
|
||||||
if (this.props.edit) {
|
if (this.props.edit) {
|
||||||
let form = new EditComment({
|
let form: EditComment = {
|
||||||
content,
|
content,
|
||||||
distinguished: None,
|
|
||||||
form_id: this.state.formId,
|
form_id: this.state.formId,
|
||||||
comment_id: node.comment_view.comment.id,
|
comment_id: node.comment_view.comment.id,
|
||||||
language_id,
|
language_id,
|
||||||
auth: auth().unwrap(),
|
auth,
|
||||||
});
|
};
|
||||||
WebSocketService.Instance.send(wsClient.editComment(form));
|
WebSocketService.Instance.send(wsClient.editComment(form));
|
||||||
} else {
|
} else {
|
||||||
let form = new CreateComment({
|
let form: CreateComment = {
|
||||||
content: content.unwrap(),
|
content,
|
||||||
form_id: this.state.formId,
|
form_id: this.state.formId,
|
||||||
post_id: node.comment_view.post.id,
|
post_id: node.comment_view.post.id,
|
||||||
parent_id: Some(node.comment_view.comment.id),
|
parent_id: node.comment_view.comment.id,
|
||||||
language_id,
|
language_id,
|
||||||
auth: auth().unwrap(),
|
auth,
|
||||||
});
|
};
|
||||||
WebSocketService.Instance.send(wsClient.createComment(form));
|
WebSocketService.Instance.send(wsClient.createComment(form));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
right: postId => {
|
}
|
||||||
let form = new CreateComment({
|
|
||||||
content: content.unwrap(),
|
|
||||||
form_id: this.state.formId,
|
|
||||||
post_id: postId,
|
|
||||||
parent_id: None,
|
|
||||||
language_id,
|
|
||||||
auth: auth().unwrap(),
|
|
||||||
});
|
|
||||||
WebSocketService.Instance.send(wsClient.createComment(form));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReplyCancel() {
|
handleReplyCancel() {
|
||||||
this.props.onReplyCancel();
|
this.props.onReplyCancel?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -181,15 +176,15 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
|
|
||||||
// Only do the showing and hiding if logged in
|
// Only do the showing and hiding if logged in
|
||||||
if (UserService.Instance.myUserInfo.isSome()) {
|
if (UserService.Instance.myUserInfo) {
|
||||||
if (
|
if (
|
||||||
op == UserOperation.CreateComment ||
|
op == UserOperation.CreateComment ||
|
||||||
op == UserOperation.EditComment
|
op == UserOperation.EditComment
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
|
|
||||||
// This only finishes this form, if the randomly generated form_id matches the one received
|
// This only finishes this form, if the randomly generated form_id matches the one received
|
||||||
if (this.state.formId.unwrapOr("") == data.form_id.unwrapOr("")) {
|
if (this.state.formId == data.form_id) {
|
||||||
this.setState({ finished: true });
|
this.setState({ finished: true });
|
||||||
|
|
||||||
// Necessary because it broke tribute for some reason
|
// Necessary because it broke tribute for some reason
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,3 @@
|
||||||
import { Option } from "@sniptt/monads";
|
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommentNode as CommentNodeI,
|
CommentNode as CommentNodeI,
|
||||||
|
@ -11,9 +10,9 @@ import { CommentNode } from "./comment-node";
|
||||||
|
|
||||||
interface CommentNodesProps {
|
interface CommentNodesProps {
|
||||||
nodes: CommentNodeI[];
|
nodes: CommentNodeI[];
|
||||||
moderators: Option<CommunityModeratorView[]>;
|
moderators?: CommunityModeratorView[];
|
||||||
admins: Option<PersonViewSafe[]>;
|
admins?: PersonViewSafe[];
|
||||||
maxCommentsShown: Option<number>;
|
maxCommentsShown?: number;
|
||||||
noBorder?: boolean;
|
noBorder?: boolean;
|
||||||
noIndent?: boolean;
|
noIndent?: boolean;
|
||||||
viewOnly?: boolean;
|
viewOnly?: boolean;
|
||||||
|
@ -34,9 +33,7 @@ export class CommentNodes extends Component<CommentNodesProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let maxComments = this.props.maxCommentsShown.unwrapOr(
|
let maxComments = this.props.maxCommentsShown ?? this.props.nodes.length;
|
||||||
this.props.nodes.length
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="comments">
|
<div className="comments">
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { T } from "inferno-i18next-dess";
|
import { T } from "inferno-i18next-dess";
|
||||||
import {
|
import {
|
||||||
|
@ -11,7 +10,7 @@ import {
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { CommentViewType } from "../../interfaces";
|
import { CommentViewType } from "../../interfaces";
|
||||||
import { WebSocketService } from "../../services";
|
import { WebSocketService } from "../../services";
|
||||||
import { auth, wsClient } from "../../utils";
|
import { myAuth, wsClient } from "../../utils";
|
||||||
import { Icon } from "../common/icon";
|
import { Icon } from "../common/icon";
|
||||||
import { PersonListing } from "../person/person-listing";
|
import { PersonListing } from "../person/person-listing";
|
||||||
import { CommentNode } from "./comment-node";
|
import { CommentNode } from "./comment-node";
|
||||||
|
@ -59,8 +58,6 @@ export class CommentReport extends Component<CommentReportProps, any> {
|
||||||
<CommentNode
|
<CommentNode
|
||||||
node={node}
|
node={node}
|
||||||
viewType={CommentViewType.Flat}
|
viewType={CommentViewType.Flat}
|
||||||
moderators={None}
|
|
||||||
admins={None}
|
|
||||||
enableDownvotes={true}
|
enableDownvotes={true}
|
||||||
viewOnly={true}
|
viewOnly={true}
|
||||||
showCommunity={true}
|
showCommunity={true}
|
||||||
|
@ -74,24 +71,21 @@ export class CommentReport extends Component<CommentReportProps, any> {
|
||||||
<div>
|
<div>
|
||||||
{i18n.t("reason")}: {r.comment_report.reason}
|
{i18n.t("reason")}: {r.comment_report.reason}
|
||||||
</div>
|
</div>
|
||||||
{r.resolver.match({
|
{r.resolver && (
|
||||||
some: resolver => (
|
<div>
|
||||||
<div>
|
{r.comment_report.resolved ? (
|
||||||
{r.comment_report.resolved ? (
|
<T i18nKey="resolved_by">
|
||||||
<T i18nKey="resolved_by">
|
#
|
||||||
#
|
<PersonListing person={r.resolver} />
|
||||||
<PersonListing person={resolver} />
|
</T>
|
||||||
</T>
|
) : (
|
||||||
) : (
|
<T i18nKey="unresolved_by">
|
||||||
<T i18nKey="unresolved_by">
|
#
|
||||||
#
|
<PersonListing person={r.resolver} />
|
||||||
<PersonListing person={resolver} />
|
</T>
|
||||||
</T>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
<button
|
<button
|
||||||
className="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleResolveReport)}
|
onClick={linkEvent(this, this.handleResolveReport)}
|
||||||
|
@ -110,11 +104,14 @@ export class CommentReport extends Component<CommentReportProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleResolveReport(i: CommentReport) {
|
handleResolveReport(i: CommentReport) {
|
||||||
let form = new ResolveCommentReport({
|
let auth = myAuth();
|
||||||
report_id: i.props.report.comment_report.id,
|
if (auth) {
|
||||||
resolved: !i.props.report.comment_report.resolved,
|
let form: ResolveCommentReport = {
|
||||||
auth: auth().unwrap(),
|
report_id: i.props.report.comment_report.id,
|
||||||
});
|
resolved: !i.props.report.comment_report.resolved,
|
||||||
WebSocketService.Instance.send(wsClient.resolveCommentReport(form));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.resolveCommentReport(form));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import { Option } from "@sniptt/monads";
|
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import { PictrsImage } from "./pictrs-image";
|
import { PictrsImage } from "./pictrs-image";
|
||||||
|
|
||||||
interface BannerIconHeaderProps {
|
interface BannerIconHeaderProps {
|
||||||
banner: Option<string>;
|
banner?: string;
|
||||||
icon: Option<string>;
|
icon?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BannerIconHeader extends Component<BannerIconHeaderProps, any> {
|
export class BannerIconHeader extends Component<BannerIconHeaderProps, any> {
|
||||||
|
@ -13,23 +12,19 @@ export class BannerIconHeader extends Component<BannerIconHeaderProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let banner = this.props.banner;
|
||||||
|
let icon = this.props.icon;
|
||||||
return (
|
return (
|
||||||
<div className="position-relative mb-2">
|
<div className="position-relative mb-2">
|
||||||
{this.props.banner.match({
|
{banner && <PictrsImage src={banner} banner alt="" />}
|
||||||
some: banner => <PictrsImage src={banner} banner alt="" />,
|
{icon && (
|
||||||
none: <></>,
|
<PictrsImage
|
||||||
})}
|
src={icon}
|
||||||
{this.props.icon.match({
|
iconOverlay
|
||||||
some: icon => (
|
pushup={!!this.props.banner}
|
||||||
<PictrsImage
|
alt=""
|
||||||
src={icon}
|
/>
|
||||||
iconOverlay
|
)}
|
||||||
pushup={this.props.banner.isSome()}
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,12 @@ export class CommentSortSelect extends Component<
|
||||||
CommentSortSelectState
|
CommentSortSelectState
|
||||||
> {
|
> {
|
||||||
private id = `sort-select-${randomStr()}`;
|
private id = `sort-select-${randomStr()}`;
|
||||||
private emptyState: CommentSortSelectState = {
|
state: CommentSortSelectState = {
|
||||||
sort: this.props.sort,
|
sort: this.props.sort,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromProps(props: any): CommentSortSelectState {
|
static getDerivedStateFromProps(props: any): CommentSortSelectState {
|
||||||
|
@ -65,6 +64,6 @@ export class CommentSortSelect extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSortChange(i: CommentSortSelect, event: any) {
|
handleSortChange(i: CommentSortSelect, event: any) {
|
||||||
i.props.onChange(event.target.value);
|
i.props.onChange?.(event.target.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,13 +15,12 @@ export class DataTypeSelect extends Component<
|
||||||
DataTypeSelectProps,
|
DataTypeSelectProps,
|
||||||
DataTypeSelectState
|
DataTypeSelectState
|
||||||
> {
|
> {
|
||||||
private emptyState: DataTypeSelectState = {
|
state: DataTypeSelectState = {
|
||||||
type_: this.props.type_,
|
type_: this.props.type_,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromProps(props: any): DataTypeSelectProps {
|
static getDerivedStateFromProps(props: any): DataTypeSelectProps {
|
||||||
|
@ -64,6 +63,6 @@ export class DataTypeSelect extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTypeChange(i: DataTypeSelect, event: any) {
|
handleTypeChange(i: DataTypeSelect, event: any) {
|
||||||
i.props.onChange(Number(event.target.value));
|
i.props.onChange?.(Number(event.target.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Option } from "@sniptt/monads";
|
|
||||||
import { htmlToText } from "html-to-text";
|
import { htmlToText } from "html-to-text";
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import { Helmet } from "inferno-helmet";
|
import { Helmet } from "inferno-helmet";
|
||||||
|
@ -8,14 +7,16 @@ import { md } from "../../utils";
|
||||||
interface HtmlTagsProps {
|
interface HtmlTagsProps {
|
||||||
title: string;
|
title: string;
|
||||||
path: string;
|
path: string;
|
||||||
description: Option<string>;
|
description?: string;
|
||||||
image: Option<string>;
|
image?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Taken from https://metatags.io/
|
/// Taken from https://metatags.io/
|
||||||
export class HtmlTags extends Component<HtmlTagsProps, any> {
|
export class HtmlTags extends Component<HtmlTagsProps, any> {
|
||||||
render() {
|
render() {
|
||||||
let url = httpExternalPath(this.props.path);
|
let url = httpExternalPath(this.props.path);
|
||||||
|
let desc = this.props.description;
|
||||||
|
let image = this.props.image;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Helmet title={this.props.title}>
|
<Helmet title={this.props.title}>
|
||||||
|
@ -33,21 +34,19 @@ export class HtmlTags extends Component<HtmlTagsProps, any> {
|
||||||
<meta property="twitter:card" content="summary_large_image" />
|
<meta property="twitter:card" content="summary_large_image" />
|
||||||
|
|
||||||
{/* Optional desc and images */}
|
{/* Optional desc and images */}
|
||||||
{this.props.description.isSome() &&
|
{["description", "og:description", "twitter:description"].map(
|
||||||
["description", "og:description", "twitter:description"].map(n => (
|
n =>
|
||||||
<meta
|
desc && (
|
||||||
key={n}
|
<meta
|
||||||
name={n}
|
key={n}
|
||||||
content={htmlToText(
|
name={n}
|
||||||
md.renderInline(this.props.description.unwrap())
|
content={htmlToText(md.renderInline(desc))}
|
||||||
)}
|
/>
|
||||||
/>
|
)
|
||||||
))}
|
)}
|
||||||
|
{["og:image", "twitter:image"].map(
|
||||||
{this.props.image.isSome() &&
|
p => image && <meta key={p} property={p} content={image} />
|
||||||
["og:image", "twitter:image"].map(p => (
|
)}
|
||||||
<meta key={p} property={p} content={this.props.image.unwrap()} />
|
|
||||||
))}
|
|
||||||
</Helmet>
|
</Helmet>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Option } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { pictrsUri } from "../../env";
|
import { pictrsUri } from "../../env";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
|
@ -8,7 +7,7 @@ import { Icon } from "./icon";
|
||||||
|
|
||||||
interface ImageUploadFormProps {
|
interface ImageUploadFormProps {
|
||||||
uploadTitle: string;
|
uploadTitle: string;
|
||||||
imageSrc: Option<string>;
|
imageSrc?: string;
|
||||||
onUpload(url: string): any;
|
onUpload(url: string): any;
|
||||||
onRemove(): any;
|
onRemove(): any;
|
||||||
rounded?: boolean;
|
rounded?: boolean;
|
||||||
|
@ -39,31 +38,26 @@ export class ImageUploadForm extends Component<
|
||||||
htmlFor={this.id}
|
htmlFor={this.id}
|
||||||
className="pointer text-muted small font-weight-bold"
|
className="pointer text-muted small font-weight-bold"
|
||||||
>
|
>
|
||||||
{this.props.imageSrc.match({
|
{this.props.imageSrc ? (
|
||||||
some: imageSrc => (
|
<span className="d-inline-block position-relative">
|
||||||
<span className="d-inline-block position-relative">
|
<img
|
||||||
<img
|
src={this.props.imageSrc}
|
||||||
src={imageSrc}
|
height={this.props.rounded ? 60 : ""}
|
||||||
height={this.props.rounded ? 60 : ""}
|
width={this.props.rounded ? 60 : ""}
|
||||||
width={this.props.rounded ? 60 : ""}
|
className={`img-fluid ${
|
||||||
className={`img-fluid ${
|
this.props.rounded ? "rounded-circle" : ""
|
||||||
this.props.rounded ? "rounded-circle" : ""
|
}`}
|
||||||
}`}
|
/>
|
||||||
/>
|
<a
|
||||||
<a
|
onClick={linkEvent(this, this.handleRemoveImage)}
|
||||||
onClick={linkEvent(this, this.handleRemoveImage)}
|
aria-label={i18n.t("remove")}
|
||||||
aria-label={i18n.t("remove")}
|
>
|
||||||
>
|
<Icon icon="x" classes="mini-overlay" />
|
||||||
<Icon icon="x" classes="mini-overlay" />
|
</a>
|
||||||
</a>
|
</span>
|
||||||
</span>
|
) : (
|
||||||
),
|
<span className="btn btn-secondary">{this.props.uploadTitle}</span>
|
||||||
none: (
|
)}
|
||||||
<span className="btn btn-secondary">
|
|
||||||
{this.props.uploadTitle}
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
})}
|
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id={this.id}
|
id={this.id}
|
||||||
|
@ -71,7 +65,7 @@ export class ImageUploadForm extends Component<
|
||||||
accept="image/*,video/*"
|
accept="image/*,video/*"
|
||||||
name={this.id}
|
name={this.id}
|
||||||
className="d-none"
|
className="d-none"
|
||||||
disabled={UserService.Instance.myUserInfo.isNone()}
|
disabled={!UserService.Instance.myUserInfo}
|
||||||
onChange={linkEvent(this, this.handleImageUpload)}
|
onChange={linkEvent(this, this.handleImageUpload)}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Option } from "@sniptt/monads";
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { Language } from "lemmy-js-client";
|
import { Language } from "lemmy-js-client";
|
||||||
|
@ -10,7 +9,7 @@ import { Icon } from "./icon";
|
||||||
interface LanguageSelectProps {
|
interface LanguageSelectProps {
|
||||||
allLanguages: Language[];
|
allLanguages: Language[];
|
||||||
siteLanguages: number[];
|
siteLanguages: number[];
|
||||||
selectedLanguageIds: Option<number[]>;
|
selectedLanguageIds?: number[];
|
||||||
multiple: boolean;
|
multiple: boolean;
|
||||||
onChange(val: number[]): any;
|
onChange(val: number[]): any;
|
||||||
showAll?: boolean;
|
showAll?: boolean;
|
||||||
|
@ -31,19 +30,17 @@ export class LanguageSelect extends Component<LanguageSelectProps, any> {
|
||||||
|
|
||||||
// Necessary because there is no HTML way to set selected for multiple in value=
|
// Necessary because there is no HTML way to set selected for multiple in value=
|
||||||
setSelectedValues() {
|
setSelectedValues() {
|
||||||
this.props.selectedLanguageIds.map(toString).match({
|
let ids = this.props.selectedLanguageIds?.map(toString);
|
||||||
some: ids => {
|
if (ids) {
|
||||||
var select = (document.getElementById(this.id) as HTMLSelectElement)
|
let select = (document.getElementById(this.id) as HTMLSelectElement)
|
||||||
.options;
|
.options;
|
||||||
for (let i = 0; i < select.length; i++) {
|
for (let i = 0; i < select.length; i++) {
|
||||||
let o = select[i];
|
let o = select[i];
|
||||||
if (ids.includes(o.value)) {
|
if (ids.includes(o.value)) {
|
||||||
o.selected = true;
|
o.selected = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -107,7 +104,7 @@ export class LanguageSelect extends Component<LanguageSelectProps, any> {
|
||||||
<option
|
<option
|
||||||
key={l.id}
|
key={l.id}
|
||||||
value={l.id}
|
value={l.id}
|
||||||
selected={selectedLangs.unwrapOr([]).includes(l.id)}
|
selected={selectedLangs?.includes(l.id)}
|
||||||
>
|
>
|
||||||
{l.name}
|
{l.name}
|
||||||
</option>
|
</option>
|
||||||
|
|
|
@ -21,13 +21,12 @@ export class ListingTypeSelect extends Component<
|
||||||
> {
|
> {
|
||||||
private id = `listing-type-input-${randomStr()}`;
|
private id = `listing-type-input-${randomStr()}`;
|
||||||
|
|
||||||
private emptyState: ListingTypeSelectState = {
|
state: ListingTypeSelectState = {
|
||||||
type_: this.props.type_,
|
type_: this.props.type_,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromProps(props: any): ListingTypeSelectProps {
|
static getDerivedStateFromProps(props: any): ListingTypeSelectProps {
|
||||||
|
@ -46,7 +45,7 @@ export class ListingTypeSelect extends Component<
|
||||||
title={i18n.t("subscribed_description")}
|
title={i18n.t("subscribed_description")}
|
||||||
className={`btn btn-outline-secondary
|
className={`btn btn-outline-secondary
|
||||||
${this.state.type_ == ListingType.Subscribed && "active"}
|
${this.state.type_ == ListingType.Subscribed && "active"}
|
||||||
${UserService.Instance.myUserInfo.isNone() ? "disabled" : "pointer"}
|
${!UserService.Instance.myUserInfo ? "disabled" : "pointer"}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
@ -55,7 +54,7 @@ export class ListingTypeSelect extends Component<
|
||||||
value={ListingType.Subscribed}
|
value={ListingType.Subscribed}
|
||||||
checked={this.state.type_ == ListingType.Subscribed}
|
checked={this.state.type_ == ListingType.Subscribed}
|
||||||
onChange={linkEvent(this, this.handleTypeChange)}
|
onChange={linkEvent(this, this.handleTypeChange)}
|
||||||
disabled={UserService.Instance.myUserInfo.isNone()}
|
disabled={!UserService.Instance.myUserInfo}
|
||||||
/>
|
/>
|
||||||
{i18n.t("subscribed")}
|
{i18n.t("subscribed")}
|
||||||
</label>
|
</label>
|
||||||
|
@ -100,6 +99,6 @@ export class ListingTypeSelect extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTypeChange(i: ListingTypeSelect, event: any) {
|
handleTypeChange(i: ListingTypeSelect, event: any) {
|
||||||
i.props.onChange(event.target.value);
|
i.props.onChange?.(event.target.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import autosize from "autosize";
|
import autosize from "autosize";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { Prompt } from "inferno-router";
|
import { Prompt } from "inferno-router";
|
||||||
import { Language, toUndefined } from "lemmy-js-client";
|
import { Language } from "lemmy-js-client";
|
||||||
import { pictrsUri } from "../../env";
|
import { pictrsUri } from "../../env";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService } from "../../services";
|
import { UserService } from "../../services";
|
||||||
|
@ -22,11 +21,11 @@ import { Icon, Spinner } from "./icon";
|
||||||
import { LanguageSelect } from "./language-select";
|
import { LanguageSelect } from "./language-select";
|
||||||
|
|
||||||
interface MarkdownTextAreaProps {
|
interface MarkdownTextAreaProps {
|
||||||
initialContent: Option<string>;
|
initialContent?: string;
|
||||||
initialLanguageId: Option<number>;
|
initialLanguageId?: number;
|
||||||
placeholder: Option<string>;
|
placeholder?: string;
|
||||||
buttonTitle: Option<string>;
|
buttonTitle?: string;
|
||||||
maxLength: Option<number>;
|
maxLength?: number;
|
||||||
replyType?: boolean;
|
replyType?: boolean;
|
||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
@ -35,18 +34,14 @@ interface MarkdownTextAreaProps {
|
||||||
hideNavigationWarnings?: boolean;
|
hideNavigationWarnings?: boolean;
|
||||||
onContentChange?(val: string): any;
|
onContentChange?(val: string): any;
|
||||||
onReplyCancel?(): any;
|
onReplyCancel?(): any;
|
||||||
onSubmit?(msg: {
|
onSubmit?(msg: { val?: string; formId: string; languageId?: number }): any;
|
||||||
val: Option<string>;
|
allLanguages: Language[]; // TODO should probably be nullable
|
||||||
formId: string;
|
siteLanguages: number[]; // TODO same
|
||||||
languageId: Option<number>;
|
|
||||||
}): any;
|
|
||||||
allLanguages: Language[];
|
|
||||||
siteLanguages: number[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MarkdownTextAreaState {
|
interface MarkdownTextAreaState {
|
||||||
content: Option<string>;
|
content?: string;
|
||||||
languageId: Option<number>;
|
languageId?: number;
|
||||||
previewMode: boolean;
|
previewMode: boolean;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
imageLoading: boolean;
|
imageLoading: boolean;
|
||||||
|
@ -59,7 +54,7 @@ export class MarkdownTextArea extends Component<
|
||||||
private id = `comment-textarea-${randomStr()}`;
|
private id = `comment-textarea-${randomStr()}`;
|
||||||
private formId = `comment-form-${randomStr()}`;
|
private formId = `comment-form-${randomStr()}`;
|
||||||
private tribute: any;
|
private tribute: any;
|
||||||
private emptyState: MarkdownTextAreaState = {
|
state: MarkdownTextAreaState = {
|
||||||
content: this.props.initialContent,
|
content: this.props.initialContent,
|
||||||
languageId: this.props.initialLanguageId,
|
languageId: this.props.initialLanguageId,
|
||||||
previewMode: false,
|
previewMode: false,
|
||||||
|
@ -75,7 +70,6 @@ export class MarkdownTextArea extends Component<
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.tribute = setupTribute();
|
this.tribute = setupTribute();
|
||||||
}
|
}
|
||||||
this.state = this.emptyState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -84,7 +78,7 @@ export class MarkdownTextArea extends Component<
|
||||||
autosize(textarea);
|
autosize(textarea);
|
||||||
this.tribute.attach(textarea);
|
this.tribute.attach(textarea);
|
||||||
textarea.addEventListener("tribute-replaced", () => {
|
textarea.addEventListener("tribute-replaced", () => {
|
||||||
this.setState({ content: Some(textarea.value) });
|
this.setState({ content: textarea.value });
|
||||||
autosize.update(textarea);
|
autosize.update(textarea);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -100,18 +94,18 @@ export class MarkdownTextArea extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if (!this.props.hideNavigationWarnings && this.state.content.isSome()) {
|
if (!this.props.hideNavigationWarnings && this.state.content) {
|
||||||
window.onbeforeunload = () => true;
|
window.onbeforeunload = () => true;
|
||||||
} else {
|
} else {
|
||||||
window.onbeforeunload = undefined;
|
window.onbeforeunload = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: MarkdownTextAreaProps) {
|
componentWillReceiveProps(nextProps: MarkdownTextAreaProps) {
|
||||||
if (nextProps.finished) {
|
if (nextProps.finished) {
|
||||||
this.setState({ previewMode: false, loading: false, content: None });
|
this.setState({ previewMode: false, loading: false, content: undefined });
|
||||||
if (this.props.replyType) {
|
if (this.props.replyType) {
|
||||||
this.props.onReplyCancel();
|
this.props.onReplyCancel?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
let textarea: any = document.getElementById(this.id);
|
let textarea: any = document.getElementById(this.id);
|
||||||
|
@ -126,12 +120,12 @@ export class MarkdownTextArea extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let languageId = this.state.languageId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form id={this.formId} onSubmit={linkEvent(this, this.handleSubmit)}>
|
<form id={this.formId} onSubmit={linkEvent(this, this.handleSubmit)}>
|
||||||
<Prompt
|
<Prompt
|
||||||
when={
|
when={!this.props.hideNavigationWarnings && this.state.content}
|
||||||
!this.props.hideNavigationWarnings && this.state.content.isSome()
|
|
||||||
}
|
|
||||||
message={i18n.t("block_leaving")}
|
message={i18n.t("block_leaving")}
|
||||||
/>
|
/>
|
||||||
<div className="form-group row">
|
<div className="form-group row">
|
||||||
|
@ -139,27 +133,21 @@ export class MarkdownTextArea extends Component<
|
||||||
<textarea
|
<textarea
|
||||||
id={this.id}
|
id={this.id}
|
||||||
className={`form-control ${this.state.previewMode && "d-none"}`}
|
className={`form-control ${this.state.previewMode && "d-none"}`}
|
||||||
value={toUndefined(this.state.content)}
|
value={this.state.content}
|
||||||
onInput={linkEvent(this, this.handleContentChange)}
|
onInput={linkEvent(this, this.handleContentChange)}
|
||||||
onPaste={linkEvent(this, this.handleImageUploadPaste)}
|
onPaste={linkEvent(this, this.handleImageUploadPaste)}
|
||||||
required
|
required
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
rows={2}
|
rows={2}
|
||||||
maxLength={this.props.maxLength.unwrapOr(
|
maxLength={this.props.maxLength ?? markdownFieldCharacterLimit}
|
||||||
markdownFieldCharacterLimit
|
placeholder={this.props.placeholder}
|
||||||
)}
|
|
||||||
placeholder={toUndefined(this.props.placeholder)}
|
|
||||||
/>
|
/>
|
||||||
{this.state.previewMode &&
|
{this.state.previewMode && this.state.content && (
|
||||||
this.state.content.match({
|
<div
|
||||||
some: content => (
|
className="card border-secondary card-body md-div"
|
||||||
<div
|
dangerouslySetInnerHTML={mdToHtml(this.state.content)}
|
||||||
className="card border-secondary card-body md-div"
|
/>
|
||||||
dangerouslySetInnerHTML={mdToHtml(content)}
|
)}
|
||||||
/>
|
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
<label className="sr-only" htmlFor={this.id}>
|
<label className="sr-only" htmlFor={this.id}>
|
||||||
{i18n.t("body")}
|
{i18n.t("body")}
|
||||||
|
@ -167,22 +155,19 @@ export class MarkdownTextArea extends Component<
|
||||||
</div>
|
</div>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-sm-12 d-flex flex-wrap">
|
<div className="col-sm-12 d-flex flex-wrap">
|
||||||
{this.props.buttonTitle.match({
|
{this.props.buttonTitle && (
|
||||||
some: buttonTitle => (
|
<button
|
||||||
<button
|
type="submit"
|
||||||
type="submit"
|
className="btn btn-sm btn-secondary mr-2"
|
||||||
className="btn btn-sm btn-secondary mr-2"
|
disabled={this.props.disabled || this.state.loading}
|
||||||
disabled={this.props.disabled || this.state.loading}
|
>
|
||||||
>
|
{this.state.loading ? (
|
||||||
{this.state.loading ? (
|
<Spinner />
|
||||||
<Spinner />
|
) : (
|
||||||
) : (
|
<span>{this.props.buttonTitle}</span>
|
||||||
<span>{buttonTitle}</span>
|
)}
|
||||||
)}
|
</button>
|
||||||
</button>
|
)}
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
{this.props.replyType && (
|
{this.props.replyType && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -192,7 +177,7 @@ export class MarkdownTextArea extends Component<
|
||||||
{i18n.t("cancel")}
|
{i18n.t("cancel")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{this.state.content.isSome() && (
|
{this.state.content && (
|
||||||
<button
|
<button
|
||||||
className={`btn btn-sm btn-secondary mr-2 ${
|
className={`btn btn-sm btn-secondary mr-2 ${
|
||||||
this.state.previewMode && "active"
|
this.state.previewMode && "active"
|
||||||
|
@ -209,7 +194,9 @@ export class MarkdownTextArea extends Component<
|
||||||
<LanguageSelect
|
<LanguageSelect
|
||||||
iconVersion
|
iconVersion
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
selectedLanguageIds={this.state.languageId.map(Array.of)}
|
selectedLanguageIds={
|
||||||
|
languageId ? Array.of(languageId) : undefined
|
||||||
|
}
|
||||||
siteLanguages={this.props.siteLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
multiple={false}
|
multiple={false}
|
||||||
onChange={this.handleLanguageChange}
|
onChange={this.handleLanguageChange}
|
||||||
|
@ -243,7 +230,7 @@ export class MarkdownTextArea extends Component<
|
||||||
<label
|
<label
|
||||||
htmlFor={`file-upload-${this.id}`}
|
htmlFor={`file-upload-${this.id}`}
|
||||||
className={`mb-0 ${
|
className={`mb-0 ${
|
||||||
UserService.Instance.myUserInfo.isSome() && "pointer"
|
UserService.Instance.myUserInfo && "pointer"
|
||||||
}`}
|
}`}
|
||||||
data-tippy-content={i18n.t("upload_image")}
|
data-tippy-content={i18n.t("upload_image")}
|
||||||
>
|
>
|
||||||
|
@ -259,7 +246,7 @@ export class MarkdownTextArea extends Component<
|
||||||
accept="image/*,video/*"
|
accept="image/*,video/*"
|
||||||
name="file"
|
name="file"
|
||||||
className="d-none"
|
className="d-none"
|
||||||
disabled={UserService.Instance.myUserInfo.isNone()}
|
disabled={!UserService.Instance.myUserInfo}
|
||||||
onChange={linkEvent(this, this.handleImageUpload)}
|
onChange={linkEvent(this, this.handleImageUpload)}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
@ -376,13 +363,9 @@ export class MarkdownTextArea extends Component<
|
||||||
let deleteToken = res.files[0].delete_token;
|
let deleteToken = res.files[0].delete_token;
|
||||||
let deleteUrl = `${pictrsUri}/delete/${deleteToken}/${hash}`;
|
let deleteUrl = `${pictrsUri}/delete/${deleteToken}/${hash}`;
|
||||||
let imageMarkdown = `![](${url})`;
|
let imageMarkdown = `![](${url})`;
|
||||||
|
let content = i.state.content;
|
||||||
i.setState({
|
i.setState({
|
||||||
content: Some(
|
content: content ? `${content}\n${imageMarkdown}` : imageMarkdown,
|
||||||
i.state.content.match({
|
|
||||||
some: content => `${content}\n${imageMarkdown}`,
|
|
||||||
none: imageMarkdown,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
imageLoading: false,
|
imageLoading: false,
|
||||||
});
|
});
|
||||||
i.contentChange();
|
i.contentChange();
|
||||||
|
@ -407,13 +390,13 @@ export class MarkdownTextArea extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
contentChange() {
|
contentChange() {
|
||||||
if (this.props.onContentChange) {
|
if (this.state.content) {
|
||||||
this.props.onContentChange(toUndefined(this.state.content));
|
this.props.onContentChange?.(this.state.content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleContentChange(i: MarkdownTextArea, event: any) {
|
handleContentChange(i: MarkdownTextArea, event: any) {
|
||||||
i.setState({ content: Some(event.target.value) });
|
i.setState({ content: event.target.value });
|
||||||
i.contentChange();
|
i.contentChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -423,7 +406,7 @@ export class MarkdownTextArea extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLanguageChange(val: number[]) {
|
handleLanguageChange(val: number[]) {
|
||||||
this.setState({ languageId: Some(val[0]) });
|
this.setState({ languageId: val[0] });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit(i: MarkdownTextArea, event: any) {
|
handleSubmit(i: MarkdownTextArea, event: any) {
|
||||||
|
@ -434,11 +417,11 @@ export class MarkdownTextArea extends Component<
|
||||||
formId: i.formId,
|
formId: i.formId,
|
||||||
languageId: i.state.languageId,
|
languageId: i.state.languageId,
|
||||||
};
|
};
|
||||||
i.props.onSubmit(msg);
|
i.props.onSubmit?.(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReplyCancel(i: MarkdownTextArea) {
|
handleReplyCancel(i: MarkdownTextArea) {
|
||||||
i.props.onReplyCancel();
|
i.props.onReplyCancel?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInsertLink(i: MarkdownTextArea, event: any) {
|
handleInsertLink(i: MarkdownTextArea, event: any) {
|
||||||
|
@ -448,25 +431,24 @@ export class MarkdownTextArea extends Component<
|
||||||
let start: number = textarea.selectionStart;
|
let start: number = textarea.selectionStart;
|
||||||
let end: number = textarea.selectionEnd;
|
let end: number = textarea.selectionEnd;
|
||||||
|
|
||||||
if (i.state.content.isNone()) {
|
let content = i.state.content;
|
||||||
i.setState({ content: Some("") });
|
|
||||||
|
if (!i.state.content) {
|
||||||
|
i.setState({ content: "" });
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = i.state.content.unwrap();
|
|
||||||
|
|
||||||
if (start !== end) {
|
if (start !== end) {
|
||||||
let selectedText = content.substring(start, end);
|
let selectedText = content?.substring(start, end);
|
||||||
i.setState({
|
i.setState({
|
||||||
content: Some(
|
content: `${content?.substring(
|
||||||
`${content.substring(0, start)}[${selectedText}]()${content.substring(
|
0,
|
||||||
end
|
start
|
||||||
)}`
|
)}[${selectedText}]()${content?.substring(end)}`,
|
||||||
),
|
|
||||||
});
|
});
|
||||||
textarea.focus();
|
textarea.focus();
|
||||||
setTimeout(() => (textarea.selectionEnd = end + 3), 10);
|
setTimeout(() => (textarea.selectionEnd = end + 3), 10);
|
||||||
} else {
|
} else {
|
||||||
i.setState({ content: Some(`${content} []()`) });
|
i.setState({ content: `${content} []()` });
|
||||||
textarea.focus();
|
textarea.focus();
|
||||||
setTimeout(() => (textarea.selectionEnd -= 1), 10);
|
setTimeout(() => (textarea.selectionEnd -= 1), 10);
|
||||||
}
|
}
|
||||||
|
@ -486,28 +468,25 @@ export class MarkdownTextArea extends Component<
|
||||||
afterChars: string,
|
afterChars: string,
|
||||||
emptyChars = "___"
|
emptyChars = "___"
|
||||||
) {
|
) {
|
||||||
if (this.state.content.isNone()) {
|
let content = this.state.content;
|
||||||
this.setState({ content: Some("") });
|
if (!this.state.content) {
|
||||||
|
this.setState({ content: "" });
|
||||||
}
|
}
|
||||||
let textarea: any = document.getElementById(this.id);
|
let textarea: any = document.getElementById(this.id);
|
||||||
let start: number = textarea.selectionStart;
|
let start: number = textarea.selectionStart;
|
||||||
let end: number = textarea.selectionEnd;
|
let end: number = textarea.selectionEnd;
|
||||||
|
|
||||||
let content = this.state.content.unwrap();
|
|
||||||
|
|
||||||
if (start !== end) {
|
if (start !== end) {
|
||||||
let selectedText = content.substring(start, end);
|
let selectedText = content?.substring(start, end);
|
||||||
this.setState({
|
this.setState({
|
||||||
content: Some(
|
content: `${content?.substring(
|
||||||
`${content.substring(
|
0,
|
||||||
0,
|
start
|
||||||
start
|
)}${beforeChars}${selectedText}${afterChars}${content?.substring(end)}`,
|
||||||
)}${beforeChars}${selectedText}${afterChars}${content.substring(end)}`
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
content: Some(`${content}${beforeChars}${emptyChars}${afterChars}`),
|
content: `${content}${beforeChars}${emptyChars}${afterChars}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.contentChange();
|
this.contentChange();
|
||||||
|
@ -581,11 +560,12 @@ export class MarkdownTextArea extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
simpleInsert(chars: string) {
|
simpleInsert(chars: string) {
|
||||||
if (this.state.content.isNone()) {
|
let content = this.state.content;
|
||||||
this.setState({ content: Some(`${chars} `) });
|
if (!content) {
|
||||||
|
this.setState({ content: `${chars} ` });
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
content: Some(`${this.state.content.unwrap()}\n${chars} `),
|
content: `${content}\n${chars} `,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -606,20 +586,21 @@ export class MarkdownTextArea extends Component<
|
||||||
|
|
||||||
quoteInsert() {
|
quoteInsert() {
|
||||||
let textarea: any = document.getElementById(this.id);
|
let textarea: any = document.getElementById(this.id);
|
||||||
let selectedText = window.getSelection().toString();
|
let selectedText = window.getSelection()?.toString();
|
||||||
|
let content = this.state.content;
|
||||||
if (selectedText) {
|
if (selectedText) {
|
||||||
let quotedText =
|
let quotedText =
|
||||||
selectedText
|
selectedText
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.map(t => `> ${t}`)
|
.map(t => `> ${t}`)
|
||||||
.join("\n") + "\n\n";
|
.join("\n") + "\n\n";
|
||||||
if (this.state.content.isNone()) {
|
if (!content) {
|
||||||
this.setState({ content: Some("") });
|
this.setState({ content: "" });
|
||||||
} else {
|
} else {
|
||||||
this.setState({ content: Some(`${this.state.content.unwrap()}\n`) });
|
this.setState({ content: `${content}\n` });
|
||||||
}
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
content: Some(`${this.state.content.unwrap()}${quotedText}`),
|
content: `${content}${quotedText}`,
|
||||||
});
|
});
|
||||||
this.contentChange();
|
this.contentChange();
|
||||||
// Not sure why this needs a delay
|
// Not sure why this needs a delay
|
||||||
|
@ -631,8 +612,6 @@ export class MarkdownTextArea extends Component<
|
||||||
let textarea: any = document.getElementById(this.id);
|
let textarea: any = document.getElementById(this.id);
|
||||||
let start: number = textarea.selectionStart;
|
let start: number = textarea.selectionStart;
|
||||||
let end: number = textarea.selectionEnd;
|
let end: number = textarea.selectionEnd;
|
||||||
return start !== end
|
return start !== end ? this.state.content?.substring(start, end) ?? "" : "";
|
||||||
? this.state.content.unwrap().substring(start, end)
|
|
||||||
: "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Option } from "@sniptt/monads";
|
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
|
@ -7,7 +6,7 @@ import { Icon } from "./icon";
|
||||||
|
|
||||||
interface MomentTimeProps {
|
interface MomentTimeProps {
|
||||||
published: string;
|
published: string;
|
||||||
updated: Option<string>;
|
updated?: string;
|
||||||
showAgo?: boolean;
|
showAgo?: boolean;
|
||||||
ignoreUpdated?: boolean;
|
ignoreUpdated?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -22,22 +21,27 @@ export class MomentTime extends Component<MomentTimeProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
createdAndModifiedTimes() {
|
createdAndModifiedTimes() {
|
||||||
return `${capitalizeFirstLetter(i18n.t("created"))}: ${this.format(
|
let updated = this.props.updated;
|
||||||
|
let line = `${capitalizeFirstLetter(i18n.t("created"))}: ${this.format(
|
||||||
this.props.published
|
this.props.published
|
||||||
)}\n\n\n${
|
)}`;
|
||||||
this.props.updated.isSome() && capitalizeFirstLetter(i18n.t("modified"))
|
if (updated) {
|
||||||
} ${this.format(this.props.updated.unwrap())}`;
|
line += `\n\n\n${capitalizeFirstLetter(i18n.t("modified"))} ${this.format(
|
||||||
|
updated
|
||||||
|
)}`;
|
||||||
|
}
|
||||||
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.ignoreUpdated && this.props.updated.isSome()) {
|
if (!this.props.ignoreUpdated && this.props.updated) {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
data-tippy-content={this.createdAndModifiedTimes()}
|
data-tippy-content={this.createdAndModifiedTimes()}
|
||||||
className="font-italics pointer unselectable"
|
className="font-italics pointer unselectable"
|
||||||
>
|
>
|
||||||
<Icon icon="edit-2" classes="icon-inline mr-1" />
|
<Icon icon="edit-2" classes="icon-inline mr-1" />
|
||||||
{moment.utc(this.props.updated.unwrap()).fromNow(!this.props.showAgo)}
|
{moment.utc(this.props.updated).fromNow(!this.props.showAgo)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { T } from "inferno-i18next-dess";
|
import { T } from "inferno-i18next-dess";
|
||||||
import {
|
import {
|
||||||
|
@ -7,7 +6,7 @@ import {
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { WebSocketService } from "../../services";
|
import { WebSocketService } from "../../services";
|
||||||
import { auth, mdToHtml, wsClient } from "../../utils";
|
import { mdToHtml, myAuth, wsClient } from "../../utils";
|
||||||
import { PersonListing } from "../person/person-listing";
|
import { PersonListing } from "../person/person-listing";
|
||||||
import { MarkdownTextArea } from "./markdown-textarea";
|
import { MarkdownTextArea } from "./markdown-textarea";
|
||||||
import { MomentTime } from "./moment-time";
|
import { MomentTime } from "./moment-time";
|
||||||
|
@ -17,7 +16,7 @@ interface RegistrationApplicationProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RegistrationApplicationState {
|
interface RegistrationApplicationState {
|
||||||
denyReason: Option<string>;
|
denyReason?: string;
|
||||||
denyExpanded: boolean;
|
denyExpanded: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,15 +24,13 @@ export class RegistrationApplication extends Component<
|
||||||
RegistrationApplicationProps,
|
RegistrationApplicationProps,
|
||||||
RegistrationApplicationState
|
RegistrationApplicationState
|
||||||
> {
|
> {
|
||||||
private emptyState: RegistrationApplicationState = {
|
state: RegistrationApplicationState = {
|
||||||
denyReason: this.props.application.registration_application.deny_reason,
|
denyReason: this.props.application.registration_application.deny_reason,
|
||||||
denyExpanded: false,
|
denyExpanded: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
this.handleDenyReasonChange = this.handleDenyReasonChange.bind(this);
|
this.handleDenyReasonChange = this.handleDenyReasonChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,44 +45,37 @@ export class RegistrationApplication extends Component<
|
||||||
{i18n.t("applicant")}: <PersonListing person={a.creator} />
|
{i18n.t("applicant")}: <PersonListing person={a.creator} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{i18n.t("created")}:{" "}
|
{i18n.t("created")}: <MomentTime showAgo published={ra.published} />
|
||||||
<MomentTime showAgo published={ra.published} updated={None} />
|
|
||||||
</div>
|
</div>
|
||||||
<div>{i18n.t("answer")}:</div>
|
<div>{i18n.t("answer")}:</div>
|
||||||
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(ra.answer)} />
|
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(ra.answer)} />
|
||||||
|
|
||||||
{a.admin.match({
|
{a.admin && (
|
||||||
some: admin => (
|
<div>
|
||||||
<div>
|
{accepted ? (
|
||||||
{accepted ? (
|
<T i18nKey="approved_by">
|
||||||
<T i18nKey="approved_by">
|
#
|
||||||
|
<PersonListing person={a.admin} />
|
||||||
|
</T>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
<T i18nKey="denied_by">
|
||||||
#
|
#
|
||||||
<PersonListing person={admin} />
|
<PersonListing person={a.admin} />
|
||||||
</T>
|
</T>
|
||||||
) : (
|
{ra.deny_reason && (
|
||||||
<div>
|
<div>
|
||||||
<T i18nKey="denied_by">
|
{i18n.t("deny_reason")}:{" "}
|
||||||
#
|
<div
|
||||||
<PersonListing person={admin} />
|
className="md-div d-inline-flex"
|
||||||
</T>
|
dangerouslySetInnerHTML={mdToHtml(ra.deny_reason)}
|
||||||
{ra.deny_reason.match({
|
/>
|
||||||
some: deny_reason => (
|
</div>
|
||||||
<div>
|
)}
|
||||||
{i18n.t("deny_reason")}:{" "}
|
</div>
|
||||||
<div
|
)}
|
||||||
className="md-div d-inline-flex"
|
</div>
|
||||||
dangerouslySetInnerHTML={mdToHtml(deny_reason)}
|
)}
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
|
|
||||||
{this.state.denyExpanded && (
|
{this.state.denyExpanded && (
|
||||||
<div className="form-group row">
|
<div className="form-group row">
|
||||||
|
@ -95,11 +85,7 @@ export class RegistrationApplication extends Component<
|
||||||
<div className="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.denyReason}
|
initialContent={this.state.denyReason}
|
||||||
initialLanguageId={None}
|
|
||||||
onContentChange={this.handleDenyReasonChange}
|
onContentChange={this.handleDenyReasonChange}
|
||||||
placeholder={None}
|
|
||||||
buttonTitle={None}
|
|
||||||
maxLength={None}
|
|
||||||
hideNavigationWarnings
|
hideNavigationWarnings
|
||||||
allLanguages={[]}
|
allLanguages={[]}
|
||||||
siteLanguages={[]}
|
siteLanguages={[]}
|
||||||
|
@ -107,7 +93,7 @@ export class RegistrationApplication extends Component<
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{(ra.admin_id.isNone() || (ra.admin_id.isSome() && !accepted)) && (
|
{(!ra.admin_id || (ra.admin_id && !accepted)) && (
|
||||||
<button
|
<button
|
||||||
className="btn btn-secondary mr-2 my-2"
|
className="btn btn-secondary mr-2 my-2"
|
||||||
onClick={linkEvent(this, this.handleApprove)}
|
onClick={linkEvent(this, this.handleApprove)}
|
||||||
|
@ -116,7 +102,7 @@ export class RegistrationApplication extends Component<
|
||||||
{i18n.t("approve")}
|
{i18n.t("approve")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{(ra.admin_id.isNone() || (ra.admin_id.isSome() && accepted)) && (
|
{(!ra.admin_id || (ra.admin_id && accepted)) && (
|
||||||
<button
|
<button
|
||||||
className="btn btn-secondary mr-2"
|
className="btn btn-secondary mr-2"
|
||||||
onClick={linkEvent(this, this.handleDeny)}
|
onClick={linkEvent(this, this.handleDeny)}
|
||||||
|
@ -130,36 +116,41 @@ export class RegistrationApplication extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleApprove(i: RegistrationApplication) {
|
handleApprove(i: RegistrationApplication) {
|
||||||
i.setState({ denyExpanded: false });
|
let auth = myAuth();
|
||||||
let form = new ApproveRegistrationApplication({
|
if (auth) {
|
||||||
id: i.props.application.registration_application.id,
|
i.setState({ denyExpanded: false });
|
||||||
deny_reason: None,
|
let form: ApproveRegistrationApplication = {
|
||||||
approve: true,
|
id: i.props.application.registration_application.id,
|
||||||
auth: auth().unwrap(),
|
approve: true,
|
||||||
});
|
auth,
|
||||||
WebSocketService.Instance.send(
|
};
|
||||||
wsClient.approveRegistrationApplication(form)
|
WebSocketService.Instance.send(
|
||||||
);
|
wsClient.approveRegistrationApplication(form)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeny(i: RegistrationApplication) {
|
handleDeny(i: RegistrationApplication) {
|
||||||
if (i.state.denyExpanded) {
|
if (i.state.denyExpanded) {
|
||||||
i.setState({ denyExpanded: false });
|
i.setState({ denyExpanded: false });
|
||||||
let form = new ApproveRegistrationApplication({
|
let auth = myAuth();
|
||||||
id: i.props.application.registration_application.id,
|
if (auth) {
|
||||||
approve: false,
|
let form: ApproveRegistrationApplication = {
|
||||||
deny_reason: i.state.denyReason,
|
id: i.props.application.registration_application.id,
|
||||||
auth: auth().unwrap(),
|
approve: false,
|
||||||
});
|
deny_reason: i.state.denyReason,
|
||||||
WebSocketService.Instance.send(
|
auth,
|
||||||
wsClient.approveRegistrationApplication(form)
|
};
|
||||||
);
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.approveRegistrationApplication(form)
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
i.setState({ denyExpanded: true });
|
i.setState({ denyExpanded: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDenyReasonChange(val: string) {
|
handleDenyReasonChange(val: string) {
|
||||||
this.setState({ denyReason: Some(val) });
|
this.setState({ denyReason: val });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,13 +17,12 @@ interface SortSelectState {
|
||||||
|
|
||||||
export class SortSelect extends Component<SortSelectProps, SortSelectState> {
|
export class SortSelect extends Component<SortSelectProps, SortSelectState> {
|
||||||
private id = `sort-select-${randomStr()}`;
|
private id = `sort-select-${randomStr()}`;
|
||||||
private emptyState: SortSelectState = {
|
state: SortSelectState = {
|
||||||
sort: this.props.sort,
|
sort: this.props.sort,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromProps(props: any): SortSelectState {
|
static getDerivedStateFromProps(props: any): SortSelectState {
|
||||||
|
@ -86,6 +85,6 @@ export class SortSelect extends Component<SortSelectProps, SortSelectState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSortChange(i: SortSelect, event: any) {
|
handleSortChange(i: SortSelect, event: any) {
|
||||||
i.props.onChange(event.target.value);
|
i.props.onChange?.(event.target.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommunityResponse,
|
CommunityResponse,
|
||||||
|
@ -18,10 +17,10 @@ import { InitialFetchRequest } from "shared/interfaces";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { WebSocketService } from "../../services";
|
import { WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
|
||||||
getListingTypeFromPropsNoDefault,
|
getListingTypeFromPropsNoDefault,
|
||||||
getPageFromProps,
|
getPageFromProps,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
|
myAuth,
|
||||||
numToSI,
|
numToSI,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
showLocal,
|
showLocal,
|
||||||
|
@ -38,7 +37,7 @@ import { CommunityLink } from "./community-link";
|
||||||
const communityLimit = 50;
|
const communityLimit = 50;
|
||||||
|
|
||||||
interface CommunitiesState {
|
interface CommunitiesState {
|
||||||
listCommunitiesResponse: Option<ListCommunitiesResponse>;
|
listCommunitiesResponse?: ListCommunitiesResponse;
|
||||||
page: number;
|
page: number;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
|
@ -52,10 +51,9 @@ interface CommunitiesProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Communities extends Component<any, CommunitiesState> {
|
export class Communities extends Component<any, CommunitiesState> {
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
private isoData = setIsoData(this.context, ListCommunitiesResponse);
|
private isoData = setIsoData(this.context);
|
||||||
private emptyState: CommunitiesState = {
|
state: CommunitiesState = {
|
||||||
listCommunitiesResponse: None,
|
|
||||||
loading: true,
|
loading: true,
|
||||||
page: getPageFromProps(this.props),
|
page: getPageFromProps(this.props),
|
||||||
listingType: getListingTypeFromPropsNoDefault(this.props),
|
listingType: getListingTypeFromPropsNoDefault(this.props),
|
||||||
|
@ -65,7 +63,6 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
|
||||||
this.handlePageChange = this.handlePageChange.bind(this);
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
|
this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
|
||||||
|
|
||||||
|
@ -74,7 +71,7 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
|
|
||||||
// Only fetch the data if coming from another route
|
// Only fetch the data if coming from another route
|
||||||
if (this.isoData.path == this.context.router.route.match.url) {
|
if (this.isoData.path == this.context.router.route.match.url) {
|
||||||
let listRes = Some(this.isoData.routeData[0] as ListCommunitiesResponse);
|
let listRes = this.isoData.routeData[0] as ListCommunitiesResponse;
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
listCommunitiesResponse: listRes,
|
listCommunitiesResponse: listRes,
|
||||||
|
@ -87,7 +84,7 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,8 +117,6 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<h5>
|
<h5>
|
||||||
|
@ -168,57 +163,54 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{this.state.listCommunitiesResponse
|
{this.state.listCommunitiesResponse?.communities.map(cv => (
|
||||||
.map(l => l.communities)
|
<tr key={cv.community.id}>
|
||||||
.unwrapOr([])
|
<td>
|
||||||
.map(cv => (
|
<CommunityLink community={cv.community} />
|
||||||
<tr key={cv.community.id}>
|
</td>
|
||||||
<td>
|
<td className="text-right">
|
||||||
<CommunityLink community={cv.community} />
|
{numToSI(cv.counts.subscribers)}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-right">
|
<td className="text-right">
|
||||||
{numToSI(cv.counts.subscribers)}
|
{numToSI(cv.counts.users_active_month)}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-right">
|
<td className="text-right d-none d-lg-table-cell">
|
||||||
{numToSI(cv.counts.users_active_month)}
|
{numToSI(cv.counts.posts)}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-right d-none d-lg-table-cell">
|
<td className="text-right d-none d-lg-table-cell">
|
||||||
{numToSI(cv.counts.posts)}
|
{numToSI(cv.counts.comments)}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-right d-none d-lg-table-cell">
|
<td className="text-right">
|
||||||
{numToSI(cv.counts.comments)}
|
{cv.subscribed == SubscribedType.Subscribed && (
|
||||||
</td>
|
<button
|
||||||
<td className="text-right">
|
className="btn btn-link d-inline-block"
|
||||||
{cv.subscribed == SubscribedType.Subscribed && (
|
onClick={linkEvent(
|
||||||
<button
|
cv.community.id,
|
||||||
className="btn btn-link d-inline-block"
|
this.handleUnsubscribe
|
||||||
onClick={linkEvent(
|
)}
|
||||||
cv.community.id,
|
>
|
||||||
this.handleUnsubscribe
|
{i18n.t("unsubscribe")}
|
||||||
)}
|
</button>
|
||||||
>
|
)}
|
||||||
{i18n.t("unsubscribe")}
|
{cv.subscribed == SubscribedType.NotSubscribed && (
|
||||||
</button>
|
<button
|
||||||
)}
|
className="btn btn-link d-inline-block"
|
||||||
{cv.subscribed == SubscribedType.NotSubscribed && (
|
onClick={linkEvent(
|
||||||
<button
|
cv.community.id,
|
||||||
className="btn btn-link d-inline-block"
|
this.handleSubscribe
|
||||||
onClick={linkEvent(
|
)}
|
||||||
cv.community.id,
|
>
|
||||||
this.handleSubscribe
|
{i18n.t("subscribe")}
|
||||||
)}
|
</button>
|
||||||
>
|
)}
|
||||||
{i18n.t("subscribe")}
|
{cv.subscribed == SubscribedType.Pending && (
|
||||||
</button>
|
<div className="text-warning d-inline-block">
|
||||||
)}
|
{i18n.t("subscribe_pending")}
|
||||||
{cv.subscribed == SubscribedType.Pending && (
|
</div>
|
||||||
<div className="text-warning d-inline-block">
|
)}
|
||||||
{i18n.t("subscribe_pending")}
|
</td>
|
||||||
</div>
|
</tr>
|
||||||
)}
|
))}
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -278,21 +270,27 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnsubscribe(communityId: number) {
|
handleUnsubscribe(communityId: number) {
|
||||||
let form = new FollowCommunity({
|
let auth = myAuth();
|
||||||
community_id: communityId,
|
if (auth) {
|
||||||
follow: false,
|
let form: FollowCommunity = {
|
||||||
auth: auth().unwrap(),
|
community_id: communityId,
|
||||||
});
|
follow: false,
|
||||||
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubscribe(communityId: number) {
|
handleSubscribe(communityId: number) {
|
||||||
let form = new FollowCommunity({
|
let auth = myAuth();
|
||||||
community_id: communityId,
|
if (auth) {
|
||||||
follow: true,
|
let form: FollowCommunity = {
|
||||||
auth: auth().unwrap(),
|
community_id: communityId,
|
||||||
});
|
follow: true,
|
||||||
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSearchChange(i: Communities, event: any) {
|
handleSearchChange(i: Communities, event: any) {
|
||||||
|
@ -307,13 +305,13 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
refetch() {
|
refetch() {
|
||||||
let listCommunitiesForm = new ListCommunities({
|
let listCommunitiesForm: ListCommunities = {
|
||||||
type_: Some(this.state.listingType),
|
type_: this.state.listingType,
|
||||||
sort: Some(SortType.TopMonth),
|
sort: SortType.TopMonth,
|
||||||
limit: Some(communityLimit),
|
limit: communityLimit,
|
||||||
page: Some(this.state.page),
|
page: this.state.page,
|
||||||
auth: auth(false).ok(),
|
auth: myAuth(false),
|
||||||
});
|
};
|
||||||
|
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.listCommunities(listCommunitiesForm)
|
wsClient.listCommunities(listCommunitiesForm)
|
||||||
|
@ -322,17 +320,17 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
|
|
||||||
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
||||||
let pathSplit = req.path.split("/");
|
let pathSplit = req.path.split("/");
|
||||||
let type_: Option<ListingType> = Some(
|
let type_: ListingType = pathSplit[3]
|
||||||
pathSplit[3] ? ListingType[pathSplit[3]] : ListingType.Local
|
? ListingType[pathSplit[3]]
|
||||||
);
|
: ListingType.Local;
|
||||||
let page = Some(pathSplit[5] ? Number(pathSplit[5]) : 1);
|
let page = pathSplit[5] ? Number(pathSplit[5]) : 1;
|
||||||
let listCommunitiesForm = new ListCommunities({
|
let listCommunitiesForm: ListCommunities = {
|
||||||
type_,
|
type_,
|
||||||
sort: Some(SortType.TopMonth),
|
sort: SortType.TopMonth,
|
||||||
limit: Some(communityLimit),
|
limit: communityLimit,
|
||||||
page,
|
page,
|
||||||
auth: req.auth,
|
auth: req.auth,
|
||||||
});
|
};
|
||||||
|
|
||||||
return [req.client.listCommunities(listCommunitiesForm)];
|
return [req.client.listCommunities(listCommunitiesForm)];
|
||||||
}
|
}
|
||||||
|
@ -344,25 +342,20 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.ListCommunities) {
|
} else if (op == UserOperation.ListCommunities) {
|
||||||
let data = wsJsonToRes<ListCommunitiesResponse>(
|
let data = wsJsonToRes<ListCommunitiesResponse>(msg);
|
||||||
msg,
|
this.setState({ listCommunitiesResponse: data, loading: false });
|
||||||
ListCommunitiesResponse
|
|
||||||
);
|
|
||||||
this.setState({ listCommunitiesResponse: Some(data), loading: false });
|
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
} else if (op == UserOperation.FollowCommunity) {
|
} else if (op == UserOperation.FollowCommunity) {
|
||||||
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
let data = wsJsonToRes<CommunityResponse>(msg);
|
||||||
this.state.listCommunitiesResponse.match({
|
let res = this.state.listCommunitiesResponse;
|
||||||
some: res => {
|
let found = res?.communities.find(
|
||||||
let found = res.communities.find(
|
c => c.community.id == data.community_view.community.id
|
||||||
c => c.community.id == data.community_view.community.id
|
);
|
||||||
);
|
if (found) {
|
||||||
found.subscribed = data.community_view.subscribed;
|
found.subscribed = data.community_view.subscribed;
|
||||||
found.counts.subscribers = data.community_view.counts.subscribers;
|
found.counts.subscribers = data.community_view.counts.subscribers;
|
||||||
},
|
this.setState(this.state);
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
this.setState(this.state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { Prompt } from "inferno-router";
|
import { Prompt } from "inferno-router";
|
||||||
import {
|
import {
|
||||||
|
@ -7,7 +6,6 @@ import {
|
||||||
CreateCommunity,
|
CreateCommunity,
|
||||||
EditCommunity,
|
EditCommunity,
|
||||||
Language,
|
Language,
|
||||||
toUndefined,
|
|
||||||
UserOperation,
|
UserOperation,
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
wsUserOp,
|
wsUserOp,
|
||||||
|
@ -16,8 +14,8 @@ import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
|
myAuth,
|
||||||
randomStr,
|
randomStr,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
|
@ -28,10 +26,10 @@ import { LanguageSelect } from "../common/language-select";
|
||||||
import { MarkdownTextArea } from "../common/markdown-textarea";
|
import { MarkdownTextArea } from "../common/markdown-textarea";
|
||||||
|
|
||||||
interface CommunityFormProps {
|
interface CommunityFormProps {
|
||||||
community_view: Option<CommunityView>; // If a community is given, that means this is an edit
|
community_view?: CommunityView; // If a community is given, that means this is an edit
|
||||||
allLanguages: Language[];
|
allLanguages: Language[];
|
||||||
siteLanguages: number[];
|
siteLanguages: number[];
|
||||||
communityLanguages: Option<number[]>;
|
communityLanguages?: number[];
|
||||||
onCancel?(): any;
|
onCancel?(): any;
|
||||||
onCreate?(community: CommunityView): any;
|
onCreate?(community: CommunityView): any;
|
||||||
onEdit?(community: CommunityView): any;
|
onEdit?(community: CommunityView): any;
|
||||||
|
@ -39,7 +37,16 @@ interface CommunityFormProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CommunityFormState {
|
interface CommunityFormState {
|
||||||
communityForm: CreateCommunity;
|
form: {
|
||||||
|
name?: string;
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
icon?: string;
|
||||||
|
banner?: string;
|
||||||
|
nsfw?: boolean;
|
||||||
|
posting_restricted_to_mods?: boolean;
|
||||||
|
discussion_languages?: number[];
|
||||||
|
};
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,28 +55,16 @@ export class CommunityForm extends Component<
|
||||||
CommunityFormState
|
CommunityFormState
|
||||||
> {
|
> {
|
||||||
private id = `community-form-${randomStr()}`;
|
private id = `community-form-${randomStr()}`;
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
|
|
||||||
private emptyState: CommunityFormState = {
|
state: CommunityFormState = {
|
||||||
communityForm: new CreateCommunity({
|
form: {},
|
||||||
name: undefined,
|
|
||||||
title: undefined,
|
|
||||||
description: None,
|
|
||||||
discussion_languages: this.props.communityLanguages,
|
|
||||||
nsfw: None,
|
|
||||||
icon: None,
|
|
||||||
banner: None,
|
|
||||||
posting_restricted_to_mods: None,
|
|
||||||
auth: undefined,
|
|
||||||
}),
|
|
||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
|
|
||||||
this.handleCommunityDescriptionChange =
|
this.handleCommunityDescriptionChange =
|
||||||
this.handleCommunityDescriptionChange.bind(this);
|
this.handleCommunityDescriptionChange.bind(this);
|
||||||
|
|
||||||
|
@ -84,24 +79,21 @@ export class CommunityForm extends Component<
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
let cv = this.props.community_view;
|
||||||
|
|
||||||
if (this.props.community_view.isSome()) {
|
if (cv) {
|
||||||
let cv = this.props.community_view.unwrap();
|
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
form: {
|
||||||
communityForm: new CreateCommunity({
|
|
||||||
name: cv.community.name,
|
name: cv.community.name,
|
||||||
title: cv.community.title,
|
title: cv.community.title,
|
||||||
description: cv.community.description,
|
description: cv.community.description,
|
||||||
nsfw: Some(cv.community.nsfw),
|
nsfw: cv.community.nsfw,
|
||||||
icon: cv.community.icon,
|
icon: cv.community.icon,
|
||||||
banner: cv.community.banner,
|
banner: cv.community.banner,
|
||||||
posting_restricted_to_mods: Some(
|
posting_restricted_to_mods: cv.community.posting_restricted_to_mods,
|
||||||
cv.community.posting_restricted_to_mods
|
|
||||||
),
|
|
||||||
discussion_languages: this.props.communityLanguages,
|
discussion_languages: this.props.communityLanguages,
|
||||||
auth: undefined,
|
},
|
||||||
}),
|
loading: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,18 +101,18 @@ export class CommunityForm extends Component<
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if (
|
if (
|
||||||
!this.state.loading &&
|
!this.state.loading &&
|
||||||
(this.state.communityForm.name ||
|
(this.state.form.name ||
|
||||||
this.state.communityForm.title ||
|
this.state.form.title ||
|
||||||
this.state.communityForm.description.isSome())
|
this.state.form.description)
|
||||||
) {
|
) {
|
||||||
window.onbeforeunload = () => true;
|
window.onbeforeunload = () => true;
|
||||||
} else {
|
} else {
|
||||||
window.onbeforeunload = undefined;
|
window.onbeforeunload = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
window.onbeforeunload = null;
|
window.onbeforeunload = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,14 +122,14 @@ export class CommunityForm extends Component<
|
||||||
<Prompt
|
<Prompt
|
||||||
when={
|
when={
|
||||||
!this.state.loading &&
|
!this.state.loading &&
|
||||||
(this.state.communityForm.name ||
|
(this.state.form.name ||
|
||||||
this.state.communityForm.title ||
|
this.state.form.title ||
|
||||||
this.state.communityForm.description.isSome())
|
this.state.form.description)
|
||||||
}
|
}
|
||||||
message={i18n.t("block_leaving")}
|
message={i18n.t("block_leaving")}
|
||||||
/>
|
/>
|
||||||
<form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
|
<form onSubmit={linkEvent(this, this.handleCreateCommunitySubmit)}>
|
||||||
{this.props.community_view.isNone() && (
|
{!this.props.community_view && (
|
||||||
<div className="form-group row">
|
<div className="form-group row">
|
||||||
<label
|
<label
|
||||||
className="col-12 col-sm-2 col-form-label"
|
className="col-12 col-sm-2 col-form-label"
|
||||||
|
@ -156,7 +148,7 @@ export class CommunityForm extends Component<
|
||||||
type="text"
|
type="text"
|
||||||
id="community-name"
|
id="community-name"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
value={this.state.communityForm.name}
|
value={this.state.form.name}
|
||||||
onInput={linkEvent(this, this.handleCommunityNameChange)}
|
onInput={linkEvent(this, this.handleCommunityNameChange)}
|
||||||
required
|
required
|
||||||
minLength={3}
|
minLength={3}
|
||||||
|
@ -183,7 +175,7 @@ export class CommunityForm extends Component<
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="community-title"
|
id="community-title"
|
||||||
value={this.state.communityForm.title}
|
value={this.state.form.title}
|
||||||
onInput={linkEvent(this, this.handleCommunityTitleChange)}
|
onInput={linkEvent(this, this.handleCommunityTitleChange)}
|
||||||
className="form-control"
|
className="form-control"
|
||||||
required
|
required
|
||||||
|
@ -197,7 +189,7 @@ export class CommunityForm extends Component<
|
||||||
<div className="col-12 col-sm-10">
|
<div className="col-12 col-sm-10">
|
||||||
<ImageUploadForm
|
<ImageUploadForm
|
||||||
uploadTitle={i18n.t("upload_icon")}
|
uploadTitle={i18n.t("upload_icon")}
|
||||||
imageSrc={this.state.communityForm.icon}
|
imageSrc={this.state.form.icon}
|
||||||
onUpload={this.handleIconUpload}
|
onUpload={this.handleIconUpload}
|
||||||
onRemove={this.handleIconRemove}
|
onRemove={this.handleIconRemove}
|
||||||
rounded
|
rounded
|
||||||
|
@ -209,7 +201,7 @@ export class CommunityForm extends Component<
|
||||||
<div className="col-12 col-sm-10">
|
<div className="col-12 col-sm-10">
|
||||||
<ImageUploadForm
|
<ImageUploadForm
|
||||||
uploadTitle={i18n.t("upload_banner")}
|
uploadTitle={i18n.t("upload_banner")}
|
||||||
imageSrc={this.state.communityForm.banner}
|
imageSrc={this.state.form.banner}
|
||||||
onUpload={this.handleBannerUpload}
|
onUpload={this.handleBannerUpload}
|
||||||
onRemove={this.handleBannerRemove}
|
onRemove={this.handleBannerRemove}
|
||||||
/>
|
/>
|
||||||
|
@ -221,11 +213,8 @@ export class CommunityForm extends Component<
|
||||||
</label>
|
</label>
|
||||||
<div className="col-12 col-sm-10">
|
<div className="col-12 col-sm-10">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.communityForm.description}
|
initialContent={this.state.form.description}
|
||||||
initialLanguageId={None}
|
placeholder={i18n.t("description")}
|
||||||
placeholder={Some("description")}
|
|
||||||
buttonTitle={None}
|
|
||||||
maxLength={None}
|
|
||||||
onContentChange={this.handleCommunityDescriptionChange}
|
onContentChange={this.handleCommunityDescriptionChange}
|
||||||
allLanguages={[]}
|
allLanguages={[]}
|
||||||
siteLanguages={[]}
|
siteLanguages={[]}
|
||||||
|
@ -244,7 +233,7 @@ export class CommunityForm extends Component<
|
||||||
className="form-check-input position-static"
|
className="form-check-input position-static"
|
||||||
id="community-nsfw"
|
id="community-nsfw"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(this.state.communityForm.nsfw)}
|
checked={this.state.form.nsfw}
|
||||||
onChange={linkEvent(this, this.handleCommunityNsfwChange)}
|
onChange={linkEvent(this, this.handleCommunityNsfwChange)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -261,9 +250,7 @@ export class CommunityForm extends Component<
|
||||||
className="form-check-input position-static"
|
className="form-check-input position-static"
|
||||||
id="community-only-mods-can-post"
|
id="community-only-mods-can-post"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={this.state.form.posting_restricted_to_mods}
|
||||||
this.state.communityForm.posting_restricted_to_mods
|
|
||||||
)}
|
|
||||||
onChange={linkEvent(
|
onChange={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleCommunityPostingRestrictedToMods
|
this.handleCommunityPostingRestrictedToMods
|
||||||
|
@ -276,7 +263,7 @@ export class CommunityForm extends Component<
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
siteLanguages={this.props.siteLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
showSite
|
showSite
|
||||||
selectedLanguageIds={this.state.communityForm.discussion_languages}
|
selectedLanguageIds={this.state.form.discussion_languages}
|
||||||
multiple={true}
|
multiple={true}
|
||||||
onChange={this.handleDiscussionLanguageChange}
|
onChange={this.handleDiscussionLanguageChange}
|
||||||
/>
|
/>
|
||||||
|
@ -289,13 +276,13 @@ export class CommunityForm extends Component<
|
||||||
>
|
>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : this.props.community_view.isSome() ? (
|
) : this.props.community_view ? (
|
||||||
capitalizeFirstLetter(i18n.t("save"))
|
capitalizeFirstLetter(i18n.t("save"))
|
||||||
) : (
|
) : (
|
||||||
capitalizeFirstLetter(i18n.t("create"))
|
capitalizeFirstLetter(i18n.t("create"))
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
{this.props.community_view.isSome() && (
|
{this.props.community_view && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
|
@ -314,82 +301,92 @@ export class CommunityForm extends Component<
|
||||||
handleCreateCommunitySubmit(i: CommunityForm, event: any) {
|
handleCreateCommunitySubmit(i: CommunityForm, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.setState({ loading: true });
|
i.setState({ loading: true });
|
||||||
let cForm = i.state.communityForm;
|
let cForm = i.state.form;
|
||||||
cForm.auth = auth().unwrap();
|
let auth = myAuth();
|
||||||
|
|
||||||
i.props.community_view.match({
|
let cv = i.props.community_view;
|
||||||
some: cv => {
|
|
||||||
let form = new EditCommunity({
|
if (auth) {
|
||||||
|
if (cv) {
|
||||||
|
let form: EditCommunity = {
|
||||||
community_id: cv.community.id,
|
community_id: cv.community.id,
|
||||||
title: Some(cForm.title),
|
title: cForm.title,
|
||||||
description: cForm.description,
|
description: cForm.description,
|
||||||
icon: cForm.icon,
|
icon: cForm.icon,
|
||||||
banner: cForm.banner,
|
banner: cForm.banner,
|
||||||
nsfw: cForm.nsfw,
|
nsfw: cForm.nsfw,
|
||||||
posting_restricted_to_mods: cForm.posting_restricted_to_mods,
|
posting_restricted_to_mods: cForm.posting_restricted_to_mods,
|
||||||
discussion_languages: cForm.discussion_languages,
|
discussion_languages: cForm.discussion_languages,
|
||||||
auth: cForm.auth,
|
auth,
|
||||||
});
|
};
|
||||||
|
|
||||||
WebSocketService.Instance.send(wsClient.editCommunity(form));
|
WebSocketService.Instance.send(wsClient.editCommunity(form));
|
||||||
},
|
} else {
|
||||||
none: () => {
|
if (cForm.title && cForm.name) {
|
||||||
WebSocketService.Instance.send(
|
let form: CreateCommunity = {
|
||||||
wsClient.createCommunity(i.state.communityForm)
|
name: cForm.name,
|
||||||
);
|
title: cForm.title,
|
||||||
},
|
description: cForm.description,
|
||||||
});
|
icon: cForm.icon,
|
||||||
|
banner: cForm.banner,
|
||||||
|
nsfw: cForm.nsfw,
|
||||||
|
posting_restricted_to_mods: cForm.posting_restricted_to_mods,
|
||||||
|
discussion_languages: cForm.discussion_languages,
|
||||||
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.createCommunity(form));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommunityNameChange(i: CommunityForm, event: any) {
|
handleCommunityNameChange(i: CommunityForm, event: any) {
|
||||||
i.state.communityForm.name = event.target.value;
|
i.state.form.name = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommunityTitleChange(i: CommunityForm, event: any) {
|
handleCommunityTitleChange(i: CommunityForm, event: any) {
|
||||||
i.state.communityForm.title = event.target.value;
|
i.state.form.title = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommunityDescriptionChange(val: string) {
|
handleCommunityDescriptionChange(val: string) {
|
||||||
this.setState(s => ((s.communityForm.description = Some(val)), s));
|
this.setState(s => ((s.form.description = val), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommunityNsfwChange(i: CommunityForm, event: any) {
|
handleCommunityNsfwChange(i: CommunityForm, event: any) {
|
||||||
i.state.communityForm.nsfw = Some(event.target.checked);
|
i.state.form.nsfw = event.target.checked;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommunityPostingRestrictedToMods(i: CommunityForm, event: any) {
|
handleCommunityPostingRestrictedToMods(i: CommunityForm, event: any) {
|
||||||
i.state.communityForm.posting_restricted_to_mods = Some(
|
i.state.form.posting_restricted_to_mods = event.target.checked;
|
||||||
event.target.checked
|
|
||||||
);
|
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancel(i: CommunityForm) {
|
handleCancel(i: CommunityForm) {
|
||||||
i.props.onCancel();
|
i.props.onCancel?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleIconUpload(url: string) {
|
handleIconUpload(url: string) {
|
||||||
this.setState(s => ((s.communityForm.icon = Some(url)), s));
|
this.setState(s => ((s.form.icon = url), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleIconRemove() {
|
handleIconRemove() {
|
||||||
this.setState(s => ((s.communityForm.icon = Some("")), s));
|
this.setState(s => ((s.form.icon = ""), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerUpload(url: string) {
|
handleBannerUpload(url: string) {
|
||||||
this.setState(s => ((s.communityForm.banner = Some(url)), s));
|
this.setState(s => ((s.form.banner = url), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerRemove() {
|
handleBannerRemove() {
|
||||||
this.setState(s => ((s.communityForm.banner = Some("")), s));
|
this.setState(s => ((s.form.banner = ""), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDiscussionLanguageChange(val: number[]) {
|
handleDiscussionLanguageChange(val: number[]) {
|
||||||
this.setState(s => ((s.communityForm.discussion_languages = Some(val)), s));
|
this.setState(s => ((s.form.discussion_languages = val), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -401,50 +398,46 @@ export class CommunityForm extends Component<
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.CreateCommunity) {
|
} else if (op == UserOperation.CreateCommunity) {
|
||||||
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
let data = wsJsonToRes<CommunityResponse>(msg);
|
||||||
this.props.onCreate(data.community_view);
|
this.props.onCreate?.(data.community_view);
|
||||||
|
|
||||||
// Update myUserInfo
|
// Update myUserInfo
|
||||||
let community = data.community_view.community;
|
let community = data.community_view.community;
|
||||||
|
|
||||||
UserService.Instance.myUserInfo.match({
|
let mui = UserService.Instance.myUserInfo;
|
||||||
some: mui => {
|
if (mui) {
|
||||||
let person = mui.local_user_view.person;
|
let person = mui.local_user_view.person;
|
||||||
mui.follows.push({
|
mui.follows.push({
|
||||||
community,
|
community,
|
||||||
follower: person,
|
follower: person,
|
||||||
});
|
});
|
||||||
mui.moderates.push({
|
mui.moderates.push({
|
||||||
community,
|
community,
|
||||||
moderator: person,
|
moderator: person,
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
} else if (op == UserOperation.EditCommunity) {
|
} else if (op == UserOperation.EditCommunity) {
|
||||||
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
let data = wsJsonToRes<CommunityResponse>(msg);
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
this.props.onEdit(data.community_view);
|
this.props.onEdit?.(data.community_view);
|
||||||
let community = data.community_view.community;
|
let community = data.community_view.community;
|
||||||
|
|
||||||
UserService.Instance.myUserInfo.match({
|
let mui = UserService.Instance.myUserInfo;
|
||||||
some: mui => {
|
if (mui) {
|
||||||
let followFound = mui.follows.findIndex(
|
let followFound = mui.follows.findIndex(
|
||||||
f => f.community.id == community.id
|
f => f.community.id == community.id
|
||||||
);
|
);
|
||||||
if (followFound) {
|
if (followFound) {
|
||||||
mui.follows[followFound].community = community;
|
mui.follows[followFound].community = community;
|
||||||
}
|
}
|
||||||
|
|
||||||
let moderatesFound = mui.moderates.findIndex(
|
let moderatesFound = mui.moderates.findIndex(
|
||||||
f => f.community.id == community.id
|
f => f.community.id == community.id
|
||||||
);
|
);
|
||||||
if (moderatesFound) {
|
if (moderatesFound) {
|
||||||
mui.moderates[moderatesFound].community = community;
|
mui.moderates[moderatesFound].community = community;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { hostname, relTags, showAvatars } from "../../utils";
|
||||||
import { PictrsImage } from "../common/pictrs-image";
|
import { PictrsImage } from "../common/pictrs-image";
|
||||||
|
|
||||||
interface CommunityLinkProps {
|
interface CommunityLinkProps {
|
||||||
// TODO figure this out better
|
|
||||||
community: CommunitySafe;
|
community: CommunitySafe;
|
||||||
realLink?: boolean;
|
realLink?: boolean;
|
||||||
useApubName?: boolean;
|
useApubName?: boolean;
|
||||||
|
@ -56,14 +55,12 @@ export class CommunityLink extends Component<CommunityLinkProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
avatarAndName(displayName: string) {
|
avatarAndName(displayName: string) {
|
||||||
|
let icon = this.props.community.icon;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!this.props.hideAvatar &&
|
{!this.props.hideAvatar && showAvatars() && icon && (
|
||||||
showAvatars() &&
|
<PictrsImage src={icon} icon />
|
||||||
this.props.community.icon.match({
|
)}
|
||||||
some: icon => <PictrsImage src={icon} icon />,
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
<span className="overflow-wrap-anywhere">{displayName}</span>
|
<span className="overflow-wrap-anywhere">{displayName}</span>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
AddModToCommunityResponse,
|
AddModToCommunityResponse,
|
||||||
|
@ -22,7 +21,6 @@ import {
|
||||||
PostView,
|
PostView,
|
||||||
PurgeItemResponse,
|
PurgeItemResponse,
|
||||||
SortType,
|
SortType,
|
||||||
toOption,
|
|
||||||
UserOperation,
|
UserOperation,
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
wsUserOp,
|
wsUserOp,
|
||||||
|
@ -36,7 +34,6 @@ import {
|
||||||
} from "../../interfaces";
|
} from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
communityRSSUrl,
|
communityRSSUrl,
|
||||||
createCommentLikeRes,
|
createCommentLikeRes,
|
||||||
|
@ -50,6 +47,7 @@ import {
|
||||||
getPageFromProps,
|
getPageFromProps,
|
||||||
getSortTypeFromProps,
|
getSortTypeFromProps,
|
||||||
isPostBlocked,
|
isPostBlocked,
|
||||||
|
myAuth,
|
||||||
notifyPost,
|
notifyPost,
|
||||||
nsfwCheck,
|
nsfwCheck,
|
||||||
postToCommentSortType,
|
postToCommentSortType,
|
||||||
|
@ -79,7 +77,7 @@ import { PostListings } from "../post/post-listings";
|
||||||
import { CommunityLink } from "./community-link";
|
import { CommunityLink } from "./community-link";
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
communityRes: Option<GetCommunityResponse>;
|
communityRes?: GetCommunityResponse;
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
communityName: string;
|
communityName: string;
|
||||||
communityLoading: boolean;
|
communityLoading: boolean;
|
||||||
|
@ -106,15 +104,9 @@ interface UrlParams {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Community extends Component<any, State> {
|
export class Community extends Component<any, State> {
|
||||||
private isoData = setIsoData(
|
private isoData = setIsoData(this.context);
|
||||||
this.context,
|
private subscription?: Subscription;
|
||||||
GetCommunityResponse,
|
state: State = {
|
||||||
GetPostsResponse,
|
|
||||||
GetCommentsResponse
|
|
||||||
);
|
|
||||||
private subscription: Subscription;
|
|
||||||
private emptyState: State = {
|
|
||||||
communityRes: None,
|
|
||||||
communityName: this.props.match.params.name,
|
communityName: this.props.match.params.name,
|
||||||
communityLoading: true,
|
communityLoading: true,
|
||||||
postsLoading: true,
|
postsLoading: true,
|
||||||
|
@ -131,7 +123,6 @@ export class Community extends Component<any, State> {
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
this.handleSortChange = this.handleSortChange.bind(this);
|
this.handleSortChange = this.handleSortChange.bind(this);
|
||||||
this.handleDataTypeChange = this.handleDataTypeChange.bind(this);
|
this.handleDataTypeChange = this.handleDataTypeChange.bind(this);
|
||||||
this.handlePageChange = this.handlePageChange.bind(this);
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
|
@ -143,17 +134,19 @@ export class Community extends Component<any, State> {
|
||||||
if (this.isoData.path == this.context.router.route.match.url) {
|
if (this.isoData.path == this.context.router.route.match.url) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
communityRes: Some(this.isoData.routeData[0] as GetCommunityResponse),
|
communityRes: this.isoData.routeData[0] as GetCommunityResponse,
|
||||||
};
|
};
|
||||||
let postsRes = Some(this.isoData.routeData[1] as GetPostsResponse);
|
let postsRes = this.isoData.routeData[1] as GetPostsResponse | undefined;
|
||||||
let commentsRes = Some(this.isoData.routeData[2] as GetCommentsResponse);
|
let commentsRes = this.isoData.routeData[2] as
|
||||||
|
| GetCommentsResponse
|
||||||
|
| undefined;
|
||||||
|
|
||||||
if (postsRes.isSome()) {
|
if (postsRes) {
|
||||||
this.state = { ...this.state, posts: postsRes.unwrap().posts };
|
this.state = { ...this.state, posts: postsRes.posts };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commentsRes.isSome()) {
|
if (commentsRes) {
|
||||||
this.state = { ...this.state, comments: commentsRes.unwrap().comments };
|
this.state = { ...this.state, comments: commentsRes.comments };
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -169,11 +162,10 @@ export class Community extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchCommunity() {
|
fetchCommunity() {
|
||||||
let form = new GetCommunity({
|
let form: GetCommunity = {
|
||||||
name: Some(this.state.communityName),
|
name: this.state.communityName,
|
||||||
id: None,
|
auth: myAuth(false),
|
||||||
auth: auth(false).ok(),
|
};
|
||||||
});
|
|
||||||
WebSocketService.Instance.send(wsClient.getCommunity(form));
|
WebSocketService.Instance.send(wsClient.getCommunity(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +175,7 @@ export class Community extends Component<any, State> {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
saveScrollPosition(this.context);
|
saveScrollPosition(this.context);
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromProps(props: any): CommunityProps {
|
static getDerivedStateFromProps(props: any): CommunityProps {
|
||||||
|
@ -199,58 +191,50 @@ export class Community extends Component<any, State> {
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
let communityName = pathSplit[2];
|
let communityName = pathSplit[2];
|
||||||
let communityForm = new GetCommunity({
|
let communityForm: GetCommunity = {
|
||||||
name: Some(communityName),
|
name: communityName,
|
||||||
id: None,
|
|
||||||
auth: req.auth,
|
auth: req.auth,
|
||||||
});
|
};
|
||||||
promises.push(req.client.getCommunity(communityForm));
|
promises.push(req.client.getCommunity(communityForm));
|
||||||
|
|
||||||
let dataType: DataType = pathSplit[4]
|
let dataType: DataType = pathSplit[4]
|
||||||
? DataType[pathSplit[4]]
|
? DataType[pathSplit[4]]
|
||||||
: DataType.Post;
|
: DataType.Post;
|
||||||
|
|
||||||
let sort: Option<SortType> = toOption(
|
let mui = UserService.Instance.myUserInfo;
|
||||||
pathSplit[6]
|
|
||||||
? SortType[pathSplit[6]]
|
|
||||||
: UserService.Instance.myUserInfo.match({
|
|
||||||
some: mui =>
|
|
||||||
Object.values(SortType)[
|
|
||||||
mui.local_user_view.local_user.default_sort_type
|
|
||||||
],
|
|
||||||
none: SortType.Active,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
let page = toOption(pathSplit[8] ? Number(pathSplit[8]) : 1);
|
let sort: SortType = pathSplit[6]
|
||||||
|
? SortType[pathSplit[6]]
|
||||||
|
: mui
|
||||||
|
? Object.values(SortType)[
|
||||||
|
mui.local_user_view.local_user.default_sort_type
|
||||||
|
]
|
||||||
|
: SortType.Active;
|
||||||
|
|
||||||
|
let page = pathSplit[8] ? Number(pathSplit[8]) : 1;
|
||||||
|
|
||||||
if (dataType == DataType.Post) {
|
if (dataType == DataType.Post) {
|
||||||
let getPostsForm = new GetPosts({
|
let getPostsForm: GetPosts = {
|
||||||
community_name: Some(communityName),
|
community_name: communityName,
|
||||||
community_id: None,
|
|
||||||
page,
|
page,
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
sort,
|
sort,
|
||||||
type_: Some(ListingType.All),
|
type_: ListingType.All,
|
||||||
saved_only: Some(false),
|
saved_only: false,
|
||||||
auth: req.auth,
|
auth: req.auth,
|
||||||
});
|
};
|
||||||
promises.push(req.client.getPosts(getPostsForm));
|
promises.push(req.client.getPosts(getPostsForm));
|
||||||
promises.push(Promise.resolve());
|
promises.push(Promise.resolve());
|
||||||
} else {
|
} else {
|
||||||
let getCommentsForm = new GetComments({
|
let getCommentsForm: GetComments = {
|
||||||
community_name: Some(communityName),
|
community_name: communityName,
|
||||||
community_id: None,
|
|
||||||
page,
|
page,
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
max_depth: None,
|
sort: postToCommentSortType(sort),
|
||||||
sort: sort.map(postToCommentSortType),
|
type_: ListingType.All,
|
||||||
type_: Some(ListingType.All),
|
saved_only: false,
|
||||||
saved_only: Some(false),
|
|
||||||
post_id: None,
|
|
||||||
parent_id: None,
|
|
||||||
auth: req.auth,
|
auth: req.auth,
|
||||||
});
|
};
|
||||||
promises.push(Promise.resolve());
|
promises.push(Promise.resolve());
|
||||||
promises.push(req.client.getComments(getCommentsForm));
|
promises.push(req.client.getComments(getCommentsForm));
|
||||||
}
|
}
|
||||||
|
@ -270,23 +254,19 @@ export class Community extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return this.state.communityRes.match({
|
let cRes = this.state.communityRes;
|
||||||
some: res =>
|
return cRes
|
||||||
`${res.community_view.community.title} - ${this.state.siteRes.site_view.site.name}`,
|
? `${cRes.community_view.community.title} - ${this.state.siteRes.site_view.site.name}`
|
||||||
none: "",
|
: "";
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
// For some reason, this returns an empty vec if it matches the site langs
|
// For some reason, this returns an empty vec if it matches the site langs
|
||||||
let communityLangs = this.state.communityRes.map(r => {
|
let res = this.state.communityRes;
|
||||||
let langs = r.discussion_languages;
|
let communityLangs =
|
||||||
if (langs.length == 0) {
|
res?.discussion_languages.length == 0
|
||||||
return this.state.siteRes.all_languages.map(l => l.id);
|
? this.state.siteRes.all_languages.map(l => l.id)
|
||||||
} else {
|
: res?.discussion_languages;
|
||||||
return langs;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container-lg">
|
<div className="container-lg">
|
||||||
|
@ -295,103 +275,86 @@ export class Community extends Component<any, State> {
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
</h5>
|
</h5>
|
||||||
) : (
|
) : (
|
||||||
this.state.communityRes.match({
|
res && (
|
||||||
some: res => (
|
<>
|
||||||
<>
|
<HtmlTags
|
||||||
<HtmlTags
|
title={this.documentTitle}
|
||||||
title={this.documentTitle}
|
path={this.context.router.route.match.url}
|
||||||
path={this.context.router.route.match.url}
|
description={res.community_view.community.description}
|
||||||
description={res.community_view.community.description}
|
image={res.community_view.community.icon}
|
||||||
image={res.community_view.community.icon}
|
/>
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-12 col-md-8">
|
<div className="col-12 col-md-8">
|
||||||
{this.communityInfo()}
|
{this.communityInfo()}
|
||||||
<div className="d-block d-md-none">
|
<div className="d-block d-md-none">
|
||||||
<button
|
<button
|
||||||
className="btn btn-secondary d-inline-block mb-2 mr-3"
|
className="btn btn-secondary d-inline-block mb-2 mr-3"
|
||||||
onClick={linkEvent(this, this.handleShowSidebarMobile)}
|
onClick={linkEvent(this, this.handleShowSidebarMobile)}
|
||||||
>
|
>
|
||||||
{i18n.t("sidebar")}{" "}
|
{i18n.t("sidebar")}{" "}
|
||||||
<Icon
|
<Icon
|
||||||
icon={
|
icon={
|
||||||
this.state.showSidebarMobile
|
this.state.showSidebarMobile
|
||||||
? `minus-square`
|
? `minus-square`
|
||||||
: `plus-square`
|
: `plus-square`
|
||||||
|
}
|
||||||
|
classes="icon-inline"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
{this.state.showSidebarMobile && (
|
||||||
|
<>
|
||||||
|
<Sidebar
|
||||||
|
community_view={res.community_view}
|
||||||
|
moderators={res.moderators}
|
||||||
|
admins={this.state.siteRes.admins}
|
||||||
|
online={res.online}
|
||||||
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
|
editable
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
|
siteLanguages={
|
||||||
|
this.state.siteRes.discussion_languages
|
||||||
}
|
}
|
||||||
classes="icon-inline"
|
communityLanguages={communityLangs}
|
||||||
/>
|
/>
|
||||||
</button>
|
{!res.community_view.community.local && res.site && (
|
||||||
{this.state.showSidebarMobile && (
|
|
||||||
<>
|
|
||||||
<Sidebar
|
|
||||||
community_view={res.community_view}
|
|
||||||
moderators={res.moderators}
|
|
||||||
admins={this.state.siteRes.admins}
|
|
||||||
online={res.online}
|
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
|
||||||
editable
|
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
|
||||||
siteLanguages={
|
|
||||||
this.state.siteRes.discussion_languages
|
|
||||||
}
|
|
||||||
communityLanguages={communityLangs}
|
|
||||||
/>
|
|
||||||
{!res.community_view.community.local &&
|
|
||||||
res.site.match({
|
|
||||||
some: site => (
|
|
||||||
<SiteSidebar
|
|
||||||
site={site}
|
|
||||||
showLocal={showLocal(this.isoData)}
|
|
||||||
admins={None}
|
|
||||||
counts={None}
|
|
||||||
online={None}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{this.selects()}
|
|
||||||
{this.listings()}
|
|
||||||
<Paginator
|
|
||||||
page={this.state.page}
|
|
||||||
onChange={this.handlePageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="d-none d-md-block col-md-4">
|
|
||||||
<Sidebar
|
|
||||||
community_view={res.community_view}
|
|
||||||
moderators={res.moderators}
|
|
||||||
admins={this.state.siteRes.admins}
|
|
||||||
online={res.online}
|
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
|
||||||
editable
|
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
|
||||||
communityLanguages={communityLangs}
|
|
||||||
/>
|
|
||||||
{!res.community_view.community.local &&
|
|
||||||
res.site.match({
|
|
||||||
some: site => (
|
|
||||||
<SiteSidebar
|
<SiteSidebar
|
||||||
site={site}
|
site={res.site}
|
||||||
showLocal={showLocal(this.isoData)}
|
showLocal={showLocal(this.isoData)}
|
||||||
admins={None}
|
|
||||||
counts={None}
|
|
||||||
online={None}
|
|
||||||
/>
|
/>
|
||||||
),
|
)}
|
||||||
none: <></>,
|
</>
|
||||||
})}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{this.selects()}
|
||||||
|
{this.listings()}
|
||||||
|
<Paginator
|
||||||
|
page={this.state.page}
|
||||||
|
onChange={this.handlePageChange}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
<div className="d-none d-md-block col-md-4">
|
||||||
),
|
<Sidebar
|
||||||
none: <></>,
|
community_view={res.community_view}
|
||||||
})
|
moderators={res.moderators}
|
||||||
|
admins={this.state.siteRes.admins}
|
||||||
|
online={res.online}
|
||||||
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
|
editable
|
||||||
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
|
siteLanguages={this.state.siteRes.discussion_languages}
|
||||||
|
communityLanguages={communityLangs}
|
||||||
|
/>
|
||||||
|
{!res.community_view.community.local && res.site && (
|
||||||
|
<SiteSidebar
|
||||||
|
site={res.site}
|
||||||
|
showLocal={showLocal(this.isoData)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -424,9 +387,8 @@ export class Community extends Component<any, State> {
|
||||||
noIndent
|
noIndent
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
moderators={this.state.communityRes.map(r => r.moderators)}
|
moderators={this.state.communityRes?.moderators}
|
||||||
admins={Some(this.state.siteRes.admins)}
|
admins={this.state.siteRes.admins}
|
||||||
maxCommentsShown={None}
|
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
siteLanguages={this.state.siteRes.discussion_languages}
|
||||||
/>
|
/>
|
||||||
|
@ -434,30 +396,33 @@ export class Community extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
communityInfo() {
|
communityInfo() {
|
||||||
return this.state.communityRes
|
let community = this.state.communityRes?.community_view.community;
|
||||||
.map(r => r.community_view.community)
|
return (
|
||||||
.match({
|
community && (
|
||||||
some: community => (
|
<div className="mb-2">
|
||||||
<div className="mb-2">
|
<BannerIconHeader banner={community.banner} icon={community.icon} />
|
||||||
<BannerIconHeader banner={community.banner} icon={community.icon} />
|
<h5 className="mb-0 overflow-wrap-anywhere">{community.title}</h5>
|
||||||
<h5 className="mb-0 overflow-wrap-anywhere">{community.title}</h5>
|
<CommunityLink
|
||||||
<CommunityLink
|
community={community}
|
||||||
community={community}
|
realLink
|
||||||
realLink
|
useApubName
|
||||||
useApubName
|
muted
|
||||||
muted
|
hideAvatar
|
||||||
hideAvatar
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
)
|
||||||
),
|
);
|
||||||
none: <></>,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
selects() {
|
selects() {
|
||||||
let communityRss = this.state.communityRes.map(r =>
|
// let communityRss = this.state.communityRes.map(r =>
|
||||||
communityRSSUrl(r.community_view.community.actor_id, this.state.sort)
|
// communityRSSUrl(r.community_view.community.actor_id, this.state.sort)
|
||||||
);
|
// );
|
||||||
|
let res = this.state.communityRes;
|
||||||
|
let communityRss = res
|
||||||
|
? communityRSSUrl(res.community_view.community.actor_id, this.state.sort)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<span className="mr-3">
|
<span className="mr-3">
|
||||||
|
@ -469,17 +434,18 @@ export class Community extends Component<any, State> {
|
||||||
<span className="mr-2">
|
<span className="mr-2">
|
||||||
<SortSelect sort={this.state.sort} onChange={this.handleSortChange} />
|
<SortSelect sort={this.state.sort} onChange={this.handleSortChange} />
|
||||||
</span>
|
</span>
|
||||||
{communityRss.match({
|
{communityRss && (
|
||||||
some: rss => (
|
<>
|
||||||
<>
|
<a href={communityRss} title="RSS" rel={relTags}>
|
||||||
<a href={rss} title="RSS" rel={relTags}>
|
<Icon icon="rss" classes="text-muted small" />
|
||||||
<Icon icon="rss" classes="text-muted small" />
|
</a>
|
||||||
</a>
|
<link
|
||||||
<link rel="alternate" type="application/atom+xml" href={rss} />
|
rel="alternate"
|
||||||
</>
|
type="application/atom+xml"
|
||||||
),
|
href={communityRss}
|
||||||
none: <></>,
|
/>
|
||||||
})}
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -517,31 +483,26 @@ export class Community extends Component<any, State> {
|
||||||
|
|
||||||
fetchData() {
|
fetchData() {
|
||||||
if (this.state.dataType == DataType.Post) {
|
if (this.state.dataType == DataType.Post) {
|
||||||
let form = new GetPosts({
|
let form: GetPosts = {
|
||||||
page: Some(this.state.page),
|
page: this.state.page,
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
sort: Some(this.state.sort),
|
sort: this.state.sort,
|
||||||
type_: Some(ListingType.All),
|
type_: ListingType.All,
|
||||||
community_name: Some(this.state.communityName),
|
community_name: this.state.communityName,
|
||||||
community_id: None,
|
saved_only: false,
|
||||||
saved_only: Some(false),
|
auth: myAuth(false),
|
||||||
auth: auth(false).ok(),
|
};
|
||||||
});
|
|
||||||
WebSocketService.Instance.send(wsClient.getPosts(form));
|
WebSocketService.Instance.send(wsClient.getPosts(form));
|
||||||
} else {
|
} else {
|
||||||
let form = new GetComments({
|
let form: GetComments = {
|
||||||
page: Some(this.state.page),
|
page: this.state.page,
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
max_depth: None,
|
sort: postToCommentSortType(this.state.sort),
|
||||||
sort: Some(postToCommentSortType(this.state.sort)),
|
type_: ListingType.All,
|
||||||
type_: Some(ListingType.All),
|
community_name: this.state.communityName,
|
||||||
community_name: Some(this.state.communityName),
|
saved_only: false,
|
||||||
community_id: None,
|
auth: myAuth(false),
|
||||||
saved_only: Some(false),
|
};
|
||||||
post_id: None,
|
|
||||||
parent_id: None,
|
|
||||||
auth: auth(false).ok(),
|
|
||||||
});
|
|
||||||
WebSocketService.Instance.send(wsClient.getComments(form));
|
WebSocketService.Instance.send(wsClient.getComments(form));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -549,25 +510,23 @@ export class Community extends Component<any, State> {
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
let op = wsUserOp(msg);
|
let op = wsUserOp(msg);
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
|
let res = this.state.communityRes;
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
this.context.router.history.push("/");
|
this.context.router.history.push("/");
|
||||||
return;
|
return;
|
||||||
} else if (msg.reconnect) {
|
} else if (msg.reconnect) {
|
||||||
this.state.communityRes.match({
|
if (res) {
|
||||||
some: res => {
|
WebSocketService.Instance.send(
|
||||||
WebSocketService.Instance.send(
|
wsClient.communityJoin({
|
||||||
wsClient.communityJoin({
|
community_id: res.community_view.community.id,
|
||||||
community_id: res.community_view.community.id,
|
})
|
||||||
})
|
);
|
||||||
);
|
}
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
this.fetchData();
|
this.fetchData();
|
||||||
} else if (op == UserOperation.GetCommunity) {
|
} else if (op == UserOperation.GetCommunity) {
|
||||||
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
let data = wsJsonToRes<GetCommunityResponse>(msg);
|
||||||
this.setState({ communityRes: Some(data), communityLoading: false });
|
this.setState({ communityRes: data, communityLoading: false });
|
||||||
// TODO why is there no auth in this form?
|
// TODO why is there no auth in this form?
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.communityJoin({
|
wsClient.communityJoin({
|
||||||
|
@ -579,28 +538,22 @@ export class Community extends Component<any, State> {
|
||||||
op == UserOperation.DeleteCommunity ||
|
op == UserOperation.DeleteCommunity ||
|
||||||
op == UserOperation.RemoveCommunity
|
op == UserOperation.RemoveCommunity
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
let data = wsJsonToRes<CommunityResponse>(msg);
|
||||||
this.state.communityRes.match({
|
if (res) {
|
||||||
some: res => {
|
res.community_view = data.community_view;
|
||||||
res.community_view = data.community_view;
|
res.discussion_languages = data.discussion_languages;
|
||||||
res.discussion_languages = data.discussion_languages;
|
}
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.FollowCommunity) {
|
} else if (op == UserOperation.FollowCommunity) {
|
||||||
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
let data = wsJsonToRes<CommunityResponse>(msg);
|
||||||
this.state.communityRes.match({
|
if (res) {
|
||||||
some: res => {
|
res.community_view.subscribed = data.community_view.subscribed;
|
||||||
res.community_view.subscribed = data.community_view.subscribed;
|
res.community_view.counts.subscribers =
|
||||||
res.community_view.counts.subscribers =
|
data.community_view.counts.subscribers;
|
||||||
data.community_view.counts.subscribers;
|
}
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.GetPosts) {
|
} else if (op == UserOperation.GetPosts) {
|
||||||
let data = wsJsonToRes<GetPostsResponse>(msg, GetPostsResponse);
|
let data = wsJsonToRes<GetPostsResponse>(msg);
|
||||||
this.setState({ posts: data.posts, postsLoading: false });
|
this.setState({ posts: data.posts, postsLoading: false });
|
||||||
restoreScrollPosition(this.context);
|
restoreScrollPosition(this.context);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
|
@ -612,15 +565,15 @@ export class Community extends Component<any, State> {
|
||||||
op == UserOperation.FeaturePost ||
|
op == UserOperation.FeaturePost ||
|
||||||
op == UserOperation.SavePost
|
op == UserOperation.SavePost
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
let data = wsJsonToRes<PostResponse>(msg);
|
||||||
editPostFindRes(data.post_view, this.state.posts);
|
editPostFindRes(data.post_view, this.state.posts);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.CreatePost) {
|
} else if (op == UserOperation.CreatePost) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
let data = wsJsonToRes<PostResponse>(msg);
|
||||||
|
|
||||||
let showPostNotifs = UserService.Instance.myUserInfo
|
let showPostNotifs =
|
||||||
.map(m => m.local_user_view.local_user.show_new_post_notifs)
|
UserService.Instance.myUserInfo?.local_user_view.local_user
|
||||||
.unwrapOr(false);
|
.show_new_post_notifs;
|
||||||
|
|
||||||
// Only push these if you're on the first page, you pass the nsfw check, and it isn't blocked
|
// Only push these if you're on the first page, you pass the nsfw check, and it isn't blocked
|
||||||
//
|
//
|
||||||
|
@ -636,24 +589,17 @@ export class Community extends Component<any, State> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.CreatePostLike) {
|
} else if (op == UserOperation.CreatePostLike) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
let data = wsJsonToRes<PostResponse>(msg);
|
||||||
createPostLikeFindRes(data.post_view, this.state.posts);
|
createPostLikeFindRes(data.post_view, this.state.posts);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.AddModToCommunity) {
|
} else if (op == UserOperation.AddModToCommunity) {
|
||||||
let data = wsJsonToRes<AddModToCommunityResponse>(
|
let data = wsJsonToRes<AddModToCommunityResponse>(msg);
|
||||||
msg,
|
if (res) {
|
||||||
AddModToCommunityResponse
|
res.moderators = data.moderators;
|
||||||
);
|
}
|
||||||
this.state.communityRes.match({
|
|
||||||
some: res => (res.moderators = data.moderators),
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.BanFromCommunity) {
|
} else if (op == UserOperation.BanFromCommunity) {
|
||||||
let data = wsJsonToRes<BanFromCommunityResponse>(
|
let data = wsJsonToRes<BanFromCommunityResponse>(msg);
|
||||||
msg,
|
|
||||||
BanFromCommunityResponse
|
|
||||||
);
|
|
||||||
|
|
||||||
// TODO this might be incorrect
|
// TODO this might be incorrect
|
||||||
this.state.posts
|
this.state.posts
|
||||||
|
@ -662,18 +608,18 @@ export class Community extends Component<any, State> {
|
||||||
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.GetComments) {
|
} else if (op == UserOperation.GetComments) {
|
||||||
let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
|
let data = wsJsonToRes<GetCommentsResponse>(msg);
|
||||||
this.setState({ comments: data.comments, commentsLoading: false });
|
this.setState({ comments: data.comments, commentsLoading: false });
|
||||||
} else if (
|
} else if (
|
||||||
op == UserOperation.EditComment ||
|
op == UserOperation.EditComment ||
|
||||||
op == UserOperation.DeleteComment ||
|
op == UserOperation.DeleteComment ||
|
||||||
op == UserOperation.RemoveComment
|
op == UserOperation.RemoveComment
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
editCommentRes(data.comment_view, this.state.comments);
|
editCommentRes(data.comment_view, this.state.comments);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.CreateComment) {
|
} else if (op == UserOperation.CreateComment) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
|
|
||||||
// Necessary since it might be a user reply
|
// Necessary since it might be a user reply
|
||||||
if (data.form_id) {
|
if (data.form_id) {
|
||||||
|
@ -681,41 +627,37 @@ export class Community extends Component<any, State> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.SaveComment) {
|
} else if (op == UserOperation.SaveComment) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
saveCommentRes(data.comment_view, this.state.comments);
|
saveCommentRes(data.comment_view, this.state.comments);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.CreateCommentLike) {
|
} else if (op == UserOperation.CreateCommentLike) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
createCommentLikeRes(data.comment_view, this.state.comments);
|
createCommentLikeRes(data.comment_view, this.state.comments);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.BlockPerson) {
|
} else if (op == UserOperation.BlockPerson) {
|
||||||
let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
|
let data = wsJsonToRes<BlockPersonResponse>(msg);
|
||||||
updatePersonBlock(data);
|
updatePersonBlock(data);
|
||||||
} else if (op == UserOperation.CreatePostReport) {
|
} else if (op == UserOperation.CreatePostReport) {
|
||||||
let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
|
let data = wsJsonToRes<PostReportResponse>(msg);
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
toast(i18n.t("report_created"));
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.CreateCommentReport) {
|
} else if (op == UserOperation.CreateCommentReport) {
|
||||||
let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
|
let data = wsJsonToRes<CommentReportResponse>(msg);
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
toast(i18n.t("report_created"));
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.PurgeCommunity) {
|
} else if (op == UserOperation.PurgeCommunity) {
|
||||||
let data = wsJsonToRes<PurgeItemResponse>(msg, PurgeItemResponse);
|
let data = wsJsonToRes<PurgeItemResponse>(msg);
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
toast(i18n.t("purge_success"));
|
toast(i18n.t("purge_success"));
|
||||||
this.context.router.history.push(`/`);
|
this.context.router.history.push(`/`);
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.BlockCommunity) {
|
} else if (op == UserOperation.BlockCommunity) {
|
||||||
let data = wsJsonToRes<BlockCommunityResponse>(
|
let data = wsJsonToRes<BlockCommunityResponse>(msg);
|
||||||
msg,
|
if (res) {
|
||||||
BlockCommunityResponse
|
res.community_view.blocked = data.blocked;
|
||||||
);
|
}
|
||||||
this.state.communityRes.match({
|
|
||||||
some: res => (res.community_view.blocked = data.blocked),
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
updateCommunityBlock(data);
|
updateCommunityBlock(data);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Some } from "@sniptt/monads";
|
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import { CommunityView, GetSiteResponse } from "lemmy-js-client";
|
import { CommunityView, GetSiteResponse } from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
|
@ -22,20 +21,19 @@ interface CreateCommunityState {
|
||||||
|
|
||||||
export class CreateCommunity extends Component<any, CreateCommunityState> {
|
export class CreateCommunity extends Component<any, CreateCommunityState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(this.context);
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
private emptyState: CreateCommunityState = {
|
state: CreateCommunityState = {
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.handleCommunityCreate = this.handleCommunityCreate.bind(this);
|
this.handleCommunityCreate = this.handleCommunityCreate.bind(this);
|
||||||
this.state = this.emptyState;
|
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
if (UserService.Instance.myUserInfo.isNone() && isBrowser()) {
|
if (!UserService.Instance.myUserInfo && isBrowser()) {
|
||||||
toast(i18n.t("not_logged_in"), "danger");
|
toast(i18n.t("not_logged_in"), "danger");
|
||||||
this.context.router.history.push(`/login`);
|
this.context.router.history.push(`/login`);
|
||||||
}
|
}
|
||||||
|
@ -43,7 +41,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,8 +57,6 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<h5>
|
<h5>
|
||||||
|
@ -71,14 +67,11 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
|
||||||
<div className="col-12 col-lg-6 offset-lg-3 mb-4">
|
<div className="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||||
<h5>{i18n.t("create_community")}</h5>
|
<h5>{i18n.t("create_community")}</h5>
|
||||||
<CommunityForm
|
<CommunityForm
|
||||||
community_view={None}
|
|
||||||
onCreate={this.handleCommunityCreate}
|
onCreate={this.handleCommunityCreate}
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
siteLanguages={this.state.siteRes.discussion_languages}
|
||||||
communityLanguages={Some(
|
communityLanguages={this.state.siteRes.discussion_languages}
|
||||||
this.state.siteRes.discussion_languages
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { Link } from "inferno-router";
|
import { Link } from "inferno-router";
|
||||||
import {
|
import {
|
||||||
|
@ -13,7 +12,6 @@ import {
|
||||||
PurgeCommunity,
|
PurgeCommunity,
|
||||||
RemoveCommunity,
|
RemoveCommunity,
|
||||||
SubscribedType,
|
SubscribedType,
|
||||||
toUndefined,
|
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
|
@ -21,9 +19,9 @@ import {
|
||||||
amAdmin,
|
amAdmin,
|
||||||
amMod,
|
amMod,
|
||||||
amTopMod,
|
amTopMod,
|
||||||
auth,
|
|
||||||
getUnixTime,
|
getUnixTime,
|
||||||
mdToHtml,
|
mdToHtml,
|
||||||
|
myAuth,
|
||||||
numToSI,
|
numToSI,
|
||||||
wsClient,
|
wsClient,
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
|
@ -39,7 +37,7 @@ interface SidebarProps {
|
||||||
admins: PersonViewSafe[];
|
admins: PersonViewSafe[];
|
||||||
allLanguages: Language[];
|
allLanguages: Language[];
|
||||||
siteLanguages: number[];
|
siteLanguages: number[];
|
||||||
communityLanguages: Option<number[]>;
|
communityLanguages?: number[];
|
||||||
online: number;
|
online: number;
|
||||||
enableNsfw?: boolean;
|
enableNsfw?: boolean;
|
||||||
showIcon?: boolean;
|
showIcon?: boolean;
|
||||||
|
@ -47,31 +45,27 @@ interface SidebarProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SidebarState {
|
interface SidebarState {
|
||||||
removeReason: Option<string>;
|
removeReason?: string;
|
||||||
removeExpires: Option<string>;
|
removeExpires?: string;
|
||||||
showEdit: boolean;
|
showEdit: boolean;
|
||||||
showRemoveDialog: boolean;
|
showRemoveDialog: boolean;
|
||||||
showPurgeDialog: boolean;
|
showPurgeDialog: boolean;
|
||||||
purgeReason: Option<string>;
|
purgeReason?: string;
|
||||||
purgeLoading: boolean;
|
purgeLoading: boolean;
|
||||||
showConfirmLeaveModTeam: boolean;
|
showConfirmLeaveModTeam: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Sidebar extends Component<SidebarProps, SidebarState> {
|
export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
private emptyState: SidebarState = {
|
state: SidebarState = {
|
||||||
showEdit: false,
|
showEdit: false,
|
||||||
showRemoveDialog: false,
|
showRemoveDialog: false,
|
||||||
removeReason: None,
|
|
||||||
removeExpires: None,
|
|
||||||
showPurgeDialog: false,
|
showPurgeDialog: false,
|
||||||
purgeReason: None,
|
|
||||||
purgeLoading: false,
|
purgeLoading: false,
|
||||||
showConfirmLeaveModTeam: false,
|
showConfirmLeaveModTeam: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
|
||||||
this.handleEditCommunity = this.handleEditCommunity.bind(this);
|
this.handleEditCommunity = this.handleEditCommunity.bind(this);
|
||||||
this.handleEditCancel = this.handleEditCancel.bind(this);
|
this.handleEditCancel = this.handleEditCancel.bind(this);
|
||||||
}
|
}
|
||||||
|
@ -83,7 +77,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
this.sidebar()
|
this.sidebar()
|
||||||
) : (
|
) : (
|
||||||
<CommunityForm
|
<CommunityForm
|
||||||
community_view={Some(this.props.community_view)}
|
community_view={this.props.community_view}
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
siteLanguages={this.props.siteLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
communityLanguages={this.props.communityLanguages}
|
communityLanguages={this.props.communityLanguages}
|
||||||
|
@ -336,13 +330,12 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
description() {
|
description() {
|
||||||
let description = this.props.community_view.community.description;
|
let desc = this.props.community_view.community.description;
|
||||||
return description.match({
|
return (
|
||||||
some: desc => (
|
desc && (
|
||||||
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(desc)} />
|
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(desc)} />
|
||||||
),
|
)
|
||||||
none: <></>,
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
adminButtons() {
|
adminButtons() {
|
||||||
|
@ -350,7 +343,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ul className="list-inline mb-1 text-muted font-weight-bold">
|
<ul className="list-inline mb-1 text-muted font-weight-bold">
|
||||||
{amMod(Some(this.props.moderators)) && (
|
{amMod(this.props.moderators) && (
|
||||||
<>
|
<>
|
||||||
<li className="list-inline-item-action">
|
<li className="list-inline-item-action">
|
||||||
<button
|
<button
|
||||||
|
@ -362,7 +355,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
<Icon icon="edit" classes="icon-inline" />
|
<Icon icon="edit" classes="icon-inline" />
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{!amTopMod(Some(this.props.moderators)) &&
|
{!amTopMod(this.props.moderators) &&
|
||||||
(!this.state.showConfirmLeaveModTeam ? (
|
(!this.state.showConfirmLeaveModTeam ? (
|
||||||
<li className="list-inline-item-action">
|
<li className="list-inline-item-action">
|
||||||
<button
|
<button
|
||||||
|
@ -401,7 +394,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
</li>
|
</li>
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
{amTopMod(Some(this.props.moderators)) && (
|
{amTopMod(this.props.moderators) && (
|
||||||
<li className="list-inline-item-action">
|
<li className="list-inline-item-action">
|
||||||
<button
|
<button
|
||||||
className="btn btn-link text-muted d-inline-block"
|
className="btn btn-link text-muted d-inline-block"
|
||||||
|
@ -466,7 +459,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
id="remove-reason"
|
id="remove-reason"
|
||||||
className="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("optional")}
|
placeholder={i18n.t("optional")}
|
||||||
value={toUndefined(this.state.removeReason)}
|
value={this.state.removeReason}
|
||||||
onInput={linkEvent(this, this.handleModRemoveReasonChange)}
|
onInput={linkEvent(this, this.handleModRemoveReasonChange)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -496,7 +489,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
id="purge-reason"
|
id="purge-reason"
|
||||||
className="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("reason")}
|
placeholder={i18n.t("reason")}
|
||||||
value={toUndefined(this.state.purgeReason)}
|
value={this.state.purgeReason}
|
||||||
onInput={linkEvent(this, this.handlePurgeReasonChange)}
|
onInput={linkEvent(this, this.handlePurgeReasonChange)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -533,12 +526,15 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
|
|
||||||
handleDeleteClick(i: Sidebar, event: any) {
|
handleDeleteClick(i: Sidebar, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let deleteForm = new DeleteCommunity({
|
let auth = myAuth();
|
||||||
community_id: i.props.community_view.community.id,
|
if (auth) {
|
||||||
deleted: !i.props.community_view.community.deleted,
|
let deleteForm: DeleteCommunity = {
|
||||||
auth: auth().unwrap(),
|
community_id: i.props.community_view.community.id,
|
||||||
});
|
deleted: !i.props.community_view.community.deleted,
|
||||||
WebSocketService.Instance.send(wsClient.deleteCommunity(deleteForm));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.deleteCommunity(deleteForm));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowConfirmLeaveModTeamClick(i: Sidebar) {
|
handleShowConfirmLeaveModTeamClick(i: Sidebar) {
|
||||||
|
@ -546,19 +542,18 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLeaveModTeamClick(i: Sidebar) {
|
handleLeaveModTeamClick(i: Sidebar) {
|
||||||
UserService.Instance.myUserInfo.match({
|
let mui = UserService.Instance.myUserInfo;
|
||||||
some: mui => {
|
let auth = myAuth();
|
||||||
let form = new AddModToCommunity({
|
if (auth && mui) {
|
||||||
person_id: mui.local_user_view.person.id,
|
let form: AddModToCommunity = {
|
||||||
community_id: i.props.community_view.community.id,
|
person_id: mui.local_user_view.person.id,
|
||||||
added: false,
|
community_id: i.props.community_view.community.id,
|
||||||
auth: auth().unwrap(),
|
added: false,
|
||||||
});
|
auth,
|
||||||
WebSocketService.Instance.send(wsClient.addModToCommunity(form));
|
};
|
||||||
i.setState({ showConfirmLeaveModTeam: false });
|
WebSocketService.Instance.send(wsClient.addModToCommunity(form));
|
||||||
},
|
i.setState({ showConfirmLeaveModTeam: false });
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancelLeaveModTeamClick(i: Sidebar) {
|
handleCancelLeaveModTeamClick(i: Sidebar) {
|
||||||
|
@ -568,46 +563,50 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
handleUnsubscribe(i: Sidebar, event: any) {
|
handleUnsubscribe(i: Sidebar, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let community_id = i.props.community_view.community.id;
|
let community_id = i.props.community_view.community.id;
|
||||||
let form = new FollowCommunity({
|
let auth = myAuth();
|
||||||
community_id,
|
if (auth) {
|
||||||
follow: false,
|
let form: FollowCommunity = {
|
||||||
auth: auth().unwrap(),
|
community_id,
|
||||||
});
|
follow: false,
|
||||||
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
||||||
|
}
|
||||||
|
|
||||||
// Update myUserInfo
|
// Update myUserInfo
|
||||||
UserService.Instance.myUserInfo.match({
|
let mui = UserService.Instance.myUserInfo;
|
||||||
some: mui =>
|
if (mui) {
|
||||||
(mui.follows = mui.follows.filter(i => i.community.id != community_id)),
|
mui.follows = mui.follows.filter(i => i.community.id != community_id);
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubscribe(i: Sidebar, event: any) {
|
handleSubscribe(i: Sidebar, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let community_id = i.props.community_view.community.id;
|
let community_id = i.props.community_view.community.id;
|
||||||
let form = new FollowCommunity({
|
let auth = myAuth();
|
||||||
community_id,
|
if (auth) {
|
||||||
follow: true,
|
let form: FollowCommunity = {
|
||||||
auth: auth().unwrap(),
|
community_id,
|
||||||
});
|
follow: true,
|
||||||
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
||||||
|
}
|
||||||
|
|
||||||
// Update myUserInfo
|
// Update myUserInfo
|
||||||
UserService.Instance.myUserInfo.match({
|
let mui = UserService.Instance.myUserInfo;
|
||||||
some: mui =>
|
if (mui) {
|
||||||
mui.follows.push({
|
mui.follows.push({
|
||||||
community: i.props.community_view.community,
|
community: i.props.community_view.community,
|
||||||
follower: mui.local_user_view.person,
|
follower: mui.local_user_view.person,
|
||||||
}),
|
});
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get canPost(): boolean {
|
get canPost(): boolean {
|
||||||
return (
|
return (
|
||||||
!this.props.community_view.community.posting_restricted_to_mods ||
|
!this.props.community_view.community.posting_restricted_to_mods ||
|
||||||
amMod(Some(this.props.moderators)) ||
|
amMod(this.props.moderators) ||
|
||||||
amAdmin()
|
amAdmin()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -617,25 +616,28 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveReasonChange(i: Sidebar, event: any) {
|
handleModRemoveReasonChange(i: Sidebar, event: any) {
|
||||||
i.setState({ removeReason: Some(event.target.value) });
|
i.setState({ removeReason: event.target.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveExpiresChange(i: Sidebar, event: any) {
|
handleModRemoveExpiresChange(i: Sidebar, event: any) {
|
||||||
i.setState({ removeExpires: Some(event.target.value) });
|
i.setState({ removeExpires: event.target.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleModRemoveSubmit(i: Sidebar, event: any) {
|
handleModRemoveSubmit(i: Sidebar, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let removeForm = new RemoveCommunity({
|
let auth = myAuth();
|
||||||
community_id: i.props.community_view.community.id,
|
if (auth) {
|
||||||
removed: !i.props.community_view.community.removed,
|
let removeForm: RemoveCommunity = {
|
||||||
reason: i.state.removeReason,
|
community_id: i.props.community_view.community.id,
|
||||||
expires: i.state.removeExpires.map(getUnixTime),
|
removed: !i.props.community_view.community.removed,
|
||||||
auth: auth().unwrap(),
|
reason: i.state.removeReason,
|
||||||
});
|
expires: getUnixTime(i.state.removeExpires),
|
||||||
WebSocketService.Instance.send(wsClient.removeCommunity(removeForm));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.removeCommunity(removeForm));
|
||||||
|
|
||||||
i.setState({ showRemoveDialog: false });
|
i.setState({ showRemoveDialog: false });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePurgeCommunityShow(i: Sidebar) {
|
handlePurgeCommunityShow(i: Sidebar) {
|
||||||
|
@ -643,39 +645,51 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePurgeReasonChange(i: Sidebar, event: any) {
|
handlePurgeReasonChange(i: Sidebar, event: any) {
|
||||||
i.setState({ purgeReason: Some(event.target.value) });
|
i.setState({ purgeReason: event.target.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePurgeSubmit(i: Sidebar, event: any) {
|
handlePurgeSubmit(i: Sidebar, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
let form = new PurgeCommunity({
|
let auth = myAuth();
|
||||||
community_id: i.props.community_view.community.id,
|
if (auth) {
|
||||||
reason: i.state.purgeReason,
|
let form: PurgeCommunity = {
|
||||||
auth: auth().unwrap(),
|
community_id: i.props.community_view.community.id,
|
||||||
});
|
reason: i.state.purgeReason,
|
||||||
WebSocketService.Instance.send(wsClient.purgeCommunity(form));
|
auth,
|
||||||
|
};
|
||||||
i.setState({ purgeLoading: true });
|
WebSocketService.Instance.send(wsClient.purgeCommunity(form));
|
||||||
|
i.setState({ purgeLoading: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBlock(i: Sidebar, event: any) {
|
handleBlock(i: Sidebar, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let blockCommunityForm = new BlockCommunity({
|
let auth = myAuth();
|
||||||
community_id: i.props.community_view.community.id,
|
if (auth) {
|
||||||
block: true,
|
let blockCommunityForm: BlockCommunity = {
|
||||||
auth: auth().unwrap(),
|
community_id: i.props.community_view.community.id,
|
||||||
});
|
block: true,
|
||||||
WebSocketService.Instance.send(wsClient.blockCommunity(blockCommunityForm));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.blockCommunity(blockCommunityForm)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnblock(i: Sidebar, event: any) {
|
handleUnblock(i: Sidebar, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let blockCommunityForm = new BlockCommunity({
|
let auth = myAuth();
|
||||||
community_id: i.props.community_view.community.id,
|
if (auth) {
|
||||||
block: false,
|
let blockCommunityForm: BlockCommunity = {
|
||||||
auth: auth().unwrap(),
|
community_id: i.props.community_view.community.id,
|
||||||
});
|
block: false,
|
||||||
WebSocketService.Instance.send(wsClient.blockCommunity(blockCommunityForm));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.blockCommunity(blockCommunityForm)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None } from "@sniptt/monads";
|
|
||||||
import autosize from "autosize";
|
import autosize from "autosize";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
|
@ -16,9 +15,9 @@ import { i18n } from "../../i18next";
|
||||||
import { InitialFetchRequest } from "../../interfaces";
|
import { InitialFetchRequest } from "../../interfaces";
|
||||||
import { WebSocketService } from "../../services";
|
import { WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
|
myAuth,
|
||||||
randomStr,
|
randomStr,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
showLocal,
|
showLocal,
|
||||||
|
@ -40,20 +39,18 @@ interface AdminSettingsState {
|
||||||
|
|
||||||
export class AdminSettings extends Component<any, AdminSettingsState> {
|
export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
private siteConfigTextAreaId = `site-config-${randomStr()}`;
|
private siteConfigTextAreaId = `site-config-${randomStr()}`;
|
||||||
private isoData = setIsoData(this.context, BannedPersonsResponse);
|
private isoData = setIsoData(this.context);
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
private emptyState: AdminSettingsState = {
|
state: AdminSettingsState = {
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
banned: [],
|
banned: [],
|
||||||
loading: true,
|
loading: true,
|
||||||
leaveAdminTeamLoading: null,
|
leaveAdminTeamLoading: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
|
@ -65,19 +62,25 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
WebSocketService.Instance.send(
|
let cAuth = myAuth();
|
||||||
wsClient.getBannedPersons({
|
if (cAuth) {
|
||||||
auth: auth().unwrap(),
|
WebSocketService.Instance.send(
|
||||||
})
|
wsClient.getBannedPersons({
|
||||||
);
|
auth: cAuth,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
let bannedPersonsForm = new GetBannedPersons({ auth: req.auth.unwrap() });
|
let auth = req.auth;
|
||||||
promises.push(req.client.getBannedPersons(bannedPersonsForm));
|
if (auth) {
|
||||||
|
let bannedPersonsForm: GetBannedPersons = { auth };
|
||||||
|
promises.push(req.client.getBannedPersons(bannedPersonsForm));
|
||||||
|
}
|
||||||
|
|
||||||
return promises;
|
return promises;
|
||||||
}
|
}
|
||||||
|
@ -91,7 +94,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,8 +117,6 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
<SiteForm
|
<SiteForm
|
||||||
siteRes={this.state.siteRes}
|
siteRes={this.state.siteRes}
|
||||||
|
@ -179,10 +180,11 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLeaveAdminTeam(i: AdminSettings) {
|
handleLeaveAdminTeam(i: AdminSettings) {
|
||||||
i.setState({ leaveAdminTeamLoading: true });
|
let auth = myAuth();
|
||||||
WebSocketService.Instance.send(
|
if (auth) {
|
||||||
wsClient.leaveAdmin({ auth: auth().unwrap() })
|
i.setState({ leaveAdminTeamLoading: true });
|
||||||
);
|
WebSocketService.Instance.send(wsClient.leaveAdmin({ auth }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -194,14 +196,14 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.EditSite) {
|
} else if (op == UserOperation.EditSite) {
|
||||||
let data = wsJsonToRes<SiteResponse>(msg, SiteResponse);
|
let data = wsJsonToRes<SiteResponse>(msg);
|
||||||
this.setState(s => ((s.siteRes.site_view = data.site_view), s));
|
this.setState(s => ((s.siteRes.site_view = data.site_view), s));
|
||||||
toast(i18n.t("site_saved"));
|
toast(i18n.t("site_saved"));
|
||||||
} else if (op == UserOperation.GetBannedPersons) {
|
} else if (op == UserOperation.GetBannedPersons) {
|
||||||
let data = wsJsonToRes<BannedPersonsResponse>(msg, BannedPersonsResponse);
|
let data = wsJsonToRes<BannedPersonsResponse>(msg);
|
||||||
this.setState({ banned: data.banned, loading: false });
|
this.setState({ banned: data.banned, loading: false });
|
||||||
} else if (op == UserOperation.LeaveAdmin) {
|
} else if (op == UserOperation.LeaveAdmin) {
|
||||||
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
let data = wsJsonToRes<GetSiteResponse>(msg);
|
||||||
this.setState(s => ((s.siteRes.site_view = data.site_view), s));
|
this.setState(s => ((s.siteRes.site_view = data.site_view), s));
|
||||||
this.setState({ leaveAdminTeamLoading: false });
|
this.setState({ leaveAdminTeamLoading: false });
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { T } from "inferno-i18next-dess";
|
import { T } from "inferno-i18next-dess";
|
||||||
import { Link } from "inferno-router";
|
import { Link } from "inferno-router";
|
||||||
|
@ -37,7 +36,6 @@ import {
|
||||||
} from "../../interfaces";
|
} from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
|
||||||
canCreateCommunity,
|
canCreateCommunity,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
createCommentLikeRes,
|
createCommentLikeRes,
|
||||||
|
@ -55,6 +53,7 @@ import {
|
||||||
isBrowser,
|
isBrowser,
|
||||||
isPostBlocked,
|
isPostBlocked,
|
||||||
mdToHtml,
|
mdToHtml,
|
||||||
|
myAuth,
|
||||||
notifyPost,
|
notifyPost,
|
||||||
nsfwCheck,
|
nsfwCheck,
|
||||||
postToCommentSortType,
|
postToCommentSortType,
|
||||||
|
@ -96,7 +95,7 @@ interface HomeState {
|
||||||
showSidebarMobile: boolean;
|
showSidebarMobile: boolean;
|
||||||
subscribedCollapsed: boolean;
|
subscribedCollapsed: boolean;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
tagline: Option<string>;
|
tagline?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HomeProps {
|
interface HomeProps {
|
||||||
|
@ -114,14 +113,9 @@ interface UrlParams {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Home extends Component<any, HomeState> {
|
export class Home extends Component<any, HomeState> {
|
||||||
private isoData = setIsoData(
|
private isoData = setIsoData(this.context);
|
||||||
this.context,
|
private subscription?: Subscription;
|
||||||
GetPostsResponse,
|
state: HomeState = {
|
||||||
GetCommentsResponse,
|
|
||||||
ListCommunitiesResponse
|
|
||||||
);
|
|
||||||
private subscription: Subscription;
|
|
||||||
private emptyState: HomeState = {
|
|
||||||
trendingCommunities: [],
|
trendingCommunities: [],
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
showSubscribedMobile: false,
|
showSubscribedMobile: false,
|
||||||
|
@ -140,13 +134,11 @@ export class Home extends Component<any, HomeState> {
|
||||||
dataType: getDataTypeFromProps(this.props),
|
dataType: getDataTypeFromProps(this.props),
|
||||||
sort: getSortTypeFromProps(this.props),
|
sort: getSortTypeFromProps(this.props),
|
||||||
page: getPageFromProps(this.props),
|
page: getPageFromProps(this.props),
|
||||||
tagline: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
this.handleSortChange = this.handleSortChange.bind(this);
|
this.handleSortChange = this.handleSortChange.bind(this);
|
||||||
this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
|
this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
|
||||||
this.handleDataTypeChange = this.handleDataTypeChange.bind(this);
|
this.handleDataTypeChange = this.handleDataTypeChange.bind(this);
|
||||||
|
@ -157,16 +149,20 @@ export class Home extends Component<any, HomeState> {
|
||||||
|
|
||||||
// Only fetch the data if coming from another route
|
// Only fetch the data if coming from another route
|
||||||
if (this.isoData.path == this.context.router.route.match.url) {
|
if (this.isoData.path == this.context.router.route.match.url) {
|
||||||
let postsRes = Some(this.isoData.routeData[0] as GetPostsResponse);
|
let postsRes = this.isoData.routeData[0] as GetPostsResponse | undefined;
|
||||||
let commentsRes = Some(this.isoData.routeData[1] as GetCommentsResponse);
|
let commentsRes = this.isoData.routeData[1] as
|
||||||
let trendingRes = this.isoData.routeData[2] as ListCommunitiesResponse;
|
| GetCommentsResponse
|
||||||
|
| undefined;
|
||||||
|
let trendingRes = this.isoData.routeData[2] as
|
||||||
|
| ListCommunitiesResponse
|
||||||
|
| undefined;
|
||||||
|
|
||||||
if (postsRes.isSome()) {
|
if (postsRes) {
|
||||||
this.state = { ...this.state, posts: postsRes.unwrap().posts };
|
this.state = { ...this.state, posts: postsRes.posts };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commentsRes.isSome()) {
|
if (commentsRes) {
|
||||||
this.state = { ...this.state, comments: commentsRes.unwrap().comments };
|
this.state = { ...this.state, comments: commentsRes.comments };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
|
@ -177,9 +173,9 @@ export class Home extends Component<any, HomeState> {
|
||||||
const taglines = this.state.siteRes.taglines;
|
const taglines = this.state.siteRes.taglines;
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
trendingCommunities: trendingRes.communities,
|
trendingCommunities: trendingRes?.communities ?? [],
|
||||||
loading: false,
|
loading: false,
|
||||||
tagline: taglines.map(tls => getRandomFromList(tls).content),
|
tagline: getRandomFromList(taglines)?.content,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
this.fetchTrendingCommunities();
|
this.fetchTrendingCommunities();
|
||||||
|
@ -188,13 +184,12 @@ export class Home extends Component<any, HomeState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchTrendingCommunities() {
|
fetchTrendingCommunities() {
|
||||||
let listCommunitiesForm = new ListCommunities({
|
let listCommunitiesForm: ListCommunities = {
|
||||||
type_: Some(ListingType.Local),
|
type_: ListingType.Local,
|
||||||
sort: Some(SortType.Hot),
|
sort: SortType.Hot,
|
||||||
limit: Some(trendingFetchLimit),
|
limit: trendingFetchLimit,
|
||||||
page: None,
|
auth: myAuth(false),
|
||||||
auth: auth(false).ok(),
|
};
|
||||||
});
|
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.listCommunities(listCommunitiesForm)
|
wsClient.listCommunities(listCommunitiesForm)
|
||||||
);
|
);
|
||||||
|
@ -210,7 +205,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
saveScrollPosition(this.context);
|
saveScrollPosition(this.context);
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
static getDerivedStateFromProps(
|
static getDerivedStateFromProps(
|
||||||
|
@ -230,74 +225,60 @@ export class Home extends Component<any, HomeState> {
|
||||||
let dataType: DataType = pathSplit[3]
|
let dataType: DataType = pathSplit[3]
|
||||||
? DataType[pathSplit[3]]
|
? DataType[pathSplit[3]]
|
||||||
: DataType.Post;
|
: DataType.Post;
|
||||||
|
let mui = UserService.Instance.myUserInfo;
|
||||||
|
let auth = req.auth;
|
||||||
|
|
||||||
// TODO figure out auth default_listingType, default_sort_type
|
// TODO figure out auth default_listingType, default_sort_type
|
||||||
let type_: Option<ListingType> = Some(
|
let type_: ListingType = pathSplit[5]
|
||||||
pathSplit[5]
|
? ListingType[pathSplit[5]]
|
||||||
? ListingType[pathSplit[5]]
|
: mui
|
||||||
: UserService.Instance.myUserInfo.match({
|
? Object.values(ListingType)[
|
||||||
some: mui =>
|
mui.local_user_view.local_user.default_listing_type
|
||||||
Object.values(ListingType)[
|
]
|
||||||
mui.local_user_view.local_user.default_listing_type
|
: ListingType.Local;
|
||||||
],
|
let sort: SortType = pathSplit[7]
|
||||||
none: ListingType.Local,
|
? SortType[pathSplit[7]]
|
||||||
})
|
: mui
|
||||||
);
|
? (Object.values(SortType)[
|
||||||
let sort: Option<SortType> = Some(
|
mui.local_user_view.local_user.default_sort_type
|
||||||
pathSplit[7]
|
] as SortType)
|
||||||
? SortType[pathSplit[7]]
|
: SortType.Active;
|
||||||
: UserService.Instance.myUserInfo.match({
|
|
||||||
some: mui =>
|
|
||||||
Object.values(SortType)[
|
|
||||||
mui.local_user_view.local_user.default_sort_type
|
|
||||||
],
|
|
||||||
none: SortType.Active,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
let page = Some(pathSplit[9] ? Number(pathSplit[9]) : 1);
|
let page = pathSplit[9] ? Number(pathSplit[9]) : 1;
|
||||||
|
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
if (dataType == DataType.Post) {
|
if (dataType == DataType.Post) {
|
||||||
let getPostsForm = new GetPosts({
|
let getPostsForm: GetPosts = {
|
||||||
community_id: None,
|
|
||||||
community_name: None,
|
|
||||||
type_,
|
type_,
|
||||||
page,
|
page,
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
sort,
|
sort,
|
||||||
saved_only: Some(false),
|
saved_only: false,
|
||||||
auth: req.auth,
|
auth,
|
||||||
});
|
};
|
||||||
|
|
||||||
promises.push(req.client.getPosts(getPostsForm));
|
promises.push(req.client.getPosts(getPostsForm));
|
||||||
promises.push(Promise.resolve());
|
promises.push(Promise.resolve());
|
||||||
} else {
|
} else {
|
||||||
let getCommentsForm = new GetComments({
|
let getCommentsForm: GetComments = {
|
||||||
community_id: None,
|
|
||||||
community_name: None,
|
|
||||||
page,
|
page,
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
max_depth: None,
|
sort: postToCommentSortType(sort),
|
||||||
sort: sort.map(postToCommentSortType),
|
|
||||||
type_,
|
type_,
|
||||||
saved_only: Some(false),
|
saved_only: false,
|
||||||
post_id: None,
|
auth,
|
||||||
parent_id: None,
|
};
|
||||||
auth: req.auth,
|
|
||||||
});
|
|
||||||
promises.push(Promise.resolve());
|
promises.push(Promise.resolve());
|
||||||
promises.push(req.client.getComments(getCommentsForm));
|
promises.push(req.client.getComments(getCommentsForm));
|
||||||
}
|
}
|
||||||
|
|
||||||
let trendingCommunitiesForm = new ListCommunities({
|
let trendingCommunitiesForm: ListCommunities = {
|
||||||
type_: Some(ListingType.Local),
|
type_: ListingType.Local,
|
||||||
sort: Some(SortType.Hot),
|
sort: SortType.Hot,
|
||||||
limit: Some(trendingFetchLimit),
|
limit: trendingFetchLimit,
|
||||||
page: None,
|
auth,
|
||||||
auth: req.auth,
|
};
|
||||||
});
|
|
||||||
promises.push(req.client.listCommunities(trendingCommunitiesForm));
|
promises.push(req.client.listCommunities(trendingCommunitiesForm));
|
||||||
|
|
||||||
return promises;
|
return promises;
|
||||||
|
@ -317,33 +298,28 @@ export class Home extends Component<any, HomeState> {
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
let siteView = this.state.siteRes.site_view;
|
let siteView = this.state.siteRes.site_view;
|
||||||
return this.state.siteRes.site_view.site.description.match({
|
let desc = this.state.siteRes.site_view.site.description;
|
||||||
some: desc => `${siteView.site.name} - ${desc}`,
|
return desc ? `${siteView.site.name} - ${desc}` : siteView.site.name;
|
||||||
none: siteView.site.name,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let tagline = this.state.tagline;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container-lg">
|
<div className="container-lg">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
{this.state.siteRes.site_view.local_site.site_setup && (
|
{this.state.siteRes.site_view.local_site.site_setup && (
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<main role="main" className="col-12 col-md-8">
|
<main role="main" className="col-12 col-md-8">
|
||||||
{this.state.tagline.match({
|
{tagline && (
|
||||||
some: tagline => (
|
<div
|
||||||
<div
|
id="tagline"
|
||||||
id="tagline"
|
dangerouslySetInnerHTML={mdToHtml(tagline)}
|
||||||
dangerouslySetInnerHTML={mdToHtml(tagline)}
|
></div>
|
||||||
></div>
|
)}
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
<div className="d-block d-md-none">{this.mobileView()}</div>
|
<div className="d-block d-md-none">{this.mobileView()}</div>
|
||||||
{this.posts()}
|
{this.posts()}
|
||||||
</main>
|
</main>
|
||||||
|
@ -357,10 +333,8 @@ export class Home extends Component<any, HomeState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get hasFollows(): boolean {
|
get hasFollows(): boolean {
|
||||||
return UserService.Instance.myUserInfo.match({
|
let mui = UserService.Instance.myUserInfo;
|
||||||
some: mui => mui.follows.length > 0,
|
return !!mui && mui.follows.length > 0;
|
||||||
none: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mobileView() {
|
mobileView() {
|
||||||
|
@ -412,9 +386,9 @@ export class Home extends Component<any, HomeState> {
|
||||||
{this.state.showSidebarMobile && (
|
{this.state.showSidebarMobile && (
|
||||||
<SiteSidebar
|
<SiteSidebar
|
||||||
site={siteView.site}
|
site={siteView.site}
|
||||||
admins={Some(siteRes.admins)}
|
admins={siteRes.admins}
|
||||||
counts={Some(siteView.counts)}
|
counts={siteView.counts}
|
||||||
online={Some(siteRes.online)}
|
online={siteRes.online}
|
||||||
showLocal={showLocal(this.isoData)}
|
showLocal={showLocal(this.isoData)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -450,9 +424,9 @@ export class Home extends Component<any, HomeState> {
|
||||||
</div>
|
</div>
|
||||||
<SiteSidebar
|
<SiteSidebar
|
||||||
site={siteView.site}
|
site={siteView.site}
|
||||||
admins={Some(siteRes.admins)}
|
admins={siteRes.admins}
|
||||||
counts={Some(siteView.counts)}
|
counts={siteView.counts}
|
||||||
online={Some(siteRes.online)}
|
online={siteRes.online}
|
||||||
showLocal={showLocal(this.isoData)}
|
showLocal={showLocal(this.isoData)}
|
||||||
/>
|
/>
|
||||||
{this.hasFollows && (
|
{this.hasFollows && (
|
||||||
|
@ -532,17 +506,14 @@ export class Home extends Component<any, HomeState> {
|
||||||
</h5>
|
</h5>
|
||||||
{!this.state.subscribedCollapsed && (
|
{!this.state.subscribedCollapsed && (
|
||||||
<ul className="list-inline mb-0">
|
<ul className="list-inline mb-0">
|
||||||
{UserService.Instance.myUserInfo
|
{UserService.Instance.myUserInfo?.follows.map(cfv => (
|
||||||
.map(m => m.follows)
|
<li
|
||||||
.unwrapOr([])
|
key={cfv.community.id}
|
||||||
.map(cfv => (
|
className="list-inline-item d-inline-block"
|
||||||
<li
|
>
|
||||||
key={cfv.community.id}
|
<CommunityLink community={cfv.community} />
|
||||||
className="list-inline-item d-inline-block"
|
</li>
|
||||||
>
|
))}
|
||||||
<CommunityLink community={cfv.community} />
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -595,9 +566,6 @@ export class Home extends Component<any, HomeState> {
|
||||||
<CommentNodes
|
<CommentNodes
|
||||||
nodes={commentsToFlatNodes(this.state.comments)}
|
nodes={commentsToFlatNodes(this.state.comments)}
|
||||||
viewType={CommentViewType.Flat}
|
viewType={CommentViewType.Flat}
|
||||||
moderators={None}
|
|
||||||
admins={None}
|
|
||||||
maxCommentsShown={None}
|
|
||||||
noIndent
|
noIndent
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
|
@ -611,9 +579,10 @@ export class Home extends Component<any, HomeState> {
|
||||||
selects() {
|
selects() {
|
||||||
let allRss = `/feeds/all.xml?sort=${this.state.sort}`;
|
let allRss = `/feeds/all.xml?sort=${this.state.sort}`;
|
||||||
let localRss = `/feeds/local.xml?sort=${this.state.sort}`;
|
let localRss = `/feeds/local.xml?sort=${this.state.sort}`;
|
||||||
let frontRss = auth(false)
|
let auth = myAuth(false);
|
||||||
.ok()
|
let frontRss = auth
|
||||||
.map(auth => `/feeds/front/${auth}.xml?sort=${this.state.sort}`);
|
? `/feeds/front/${auth}.xml?sort=${this.state.sort}`
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
|
@ -650,18 +619,14 @@ export class Home extends Component<any, HomeState> {
|
||||||
<link rel="alternate" type="application/atom+xml" href={localRss} />
|
<link rel="alternate" type="application/atom+xml" href={localRss} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{this.state.listingType == ListingType.Subscribed &&
|
{this.state.listingType == ListingType.Subscribed && frontRss && (
|
||||||
frontRss.match({
|
<>
|
||||||
some: rss => (
|
<a href={frontRss} title="RSS" rel={relTags}>
|
||||||
<>
|
<Icon icon="rss" classes="text-muted small" />
|
||||||
<a href={rss} title="RSS" rel={relTags}>
|
</a>
|
||||||
<Icon icon="rss" classes="text-muted small" />
|
<link rel="alternate" type="application/atom+xml" href={frontRss} />
|
||||||
</a>
|
</>
|
||||||
<link rel="alternate" type="application/atom+xml" href={rss} />
|
)}
|
||||||
</>
|
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -703,33 +668,27 @@ export class Home extends Component<any, HomeState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchData() {
|
fetchData() {
|
||||||
|
let auth = myAuth(false);
|
||||||
if (this.state.dataType == DataType.Post) {
|
if (this.state.dataType == DataType.Post) {
|
||||||
let getPostsForm = new GetPosts({
|
let getPostsForm: GetPosts = {
|
||||||
community_id: None,
|
page: this.state.page,
|
||||||
community_name: None,
|
limit: fetchLimit,
|
||||||
page: Some(this.state.page),
|
sort: this.state.sort,
|
||||||
limit: Some(fetchLimit),
|
saved_only: false,
|
||||||
sort: Some(this.state.sort),
|
type_: this.state.listingType,
|
||||||
saved_only: Some(false),
|
auth,
|
||||||
auth: auth(false).ok(),
|
};
|
||||||
type_: Some(this.state.listingType),
|
|
||||||
});
|
|
||||||
|
|
||||||
WebSocketService.Instance.send(wsClient.getPosts(getPostsForm));
|
WebSocketService.Instance.send(wsClient.getPosts(getPostsForm));
|
||||||
} else {
|
} else {
|
||||||
let getCommentsForm = new GetComments({
|
let getCommentsForm: GetComments = {
|
||||||
community_id: None,
|
page: this.state.page,
|
||||||
community_name: None,
|
limit: fetchLimit,
|
||||||
page: Some(this.state.page),
|
sort: postToCommentSortType(this.state.sort),
|
||||||
limit: Some(fetchLimit),
|
saved_only: false,
|
||||||
max_depth: None,
|
type_: this.state.listingType,
|
||||||
sort: Some(postToCommentSortType(this.state.sort)),
|
auth,
|
||||||
saved_only: Some(false),
|
};
|
||||||
post_id: None,
|
|
||||||
parent_id: None,
|
|
||||||
auth: auth(false).ok(),
|
|
||||||
type_: Some(this.state.listingType),
|
|
||||||
});
|
|
||||||
WebSocketService.Instance.send(wsClient.getComments(getCommentsForm));
|
WebSocketService.Instance.send(wsClient.getComments(getCommentsForm));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -746,17 +705,14 @@ export class Home extends Component<any, HomeState> {
|
||||||
);
|
);
|
||||||
this.fetchData();
|
this.fetchData();
|
||||||
} else if (op == UserOperation.ListCommunities) {
|
} else if (op == UserOperation.ListCommunities) {
|
||||||
let data = wsJsonToRes<ListCommunitiesResponse>(
|
let data = wsJsonToRes<ListCommunitiesResponse>(msg);
|
||||||
msg,
|
|
||||||
ListCommunitiesResponse
|
|
||||||
);
|
|
||||||
this.setState({ trendingCommunities: data.communities });
|
this.setState({ trendingCommunities: data.communities });
|
||||||
} else if (op == UserOperation.EditSite) {
|
} else if (op == UserOperation.EditSite) {
|
||||||
let data = wsJsonToRes<SiteResponse>(msg, SiteResponse);
|
let data = wsJsonToRes<SiteResponse>(msg);
|
||||||
this.setState(s => ((s.siteRes.site_view = data.site_view), s));
|
this.setState(s => ((s.siteRes.site_view = data.site_view), s));
|
||||||
toast(i18n.t("site_saved"));
|
toast(i18n.t("site_saved"));
|
||||||
} else if (op == UserOperation.GetPosts) {
|
} else if (op == UserOperation.GetPosts) {
|
||||||
let data = wsJsonToRes<GetPostsResponse>(msg, GetPostsResponse);
|
let data = wsJsonToRes<GetPostsResponse>(msg);
|
||||||
this.setState({ posts: data.posts, loading: false });
|
this.setState({ posts: data.posts, loading: false });
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.communityJoin({ community_id: 0 })
|
wsClient.communityJoin({ community_id: 0 })
|
||||||
|
@ -764,11 +720,10 @@ export class Home extends Component<any, HomeState> {
|
||||||
restoreScrollPosition(this.context);
|
restoreScrollPosition(this.context);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.CreatePost) {
|
} else if (op == UserOperation.CreatePost) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
let data = wsJsonToRes<PostResponse>(msg);
|
||||||
|
let mui = UserService.Instance.myUserInfo;
|
||||||
|
|
||||||
let showPostNotifs = UserService.Instance.myUserInfo
|
let showPostNotifs = mui?.local_user_view.local_user.show_new_post_notifs;
|
||||||
.map(m => m.local_user_view.local_user.show_new_post_notifs)
|
|
||||||
.unwrapOr(false);
|
|
||||||
|
|
||||||
// Only push these if you're on the first page, you pass the nsfw check, and it isn't blocked
|
// Only push these if you're on the first page, you pass the nsfw check, and it isn't blocked
|
||||||
if (
|
if (
|
||||||
|
@ -779,9 +734,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
// If you're on subscribed, only push it if you're subscribed.
|
// If you're on subscribed, only push it if you're subscribed.
|
||||||
if (this.state.listingType == ListingType.Subscribed) {
|
if (this.state.listingType == ListingType.Subscribed) {
|
||||||
if (
|
if (
|
||||||
UserService.Instance.myUserInfo
|
mui?.follows
|
||||||
.map(m => m.follows)
|
|
||||||
.unwrapOr([])
|
|
||||||
.map(c => c.community.id)
|
.map(c => c.community.id)
|
||||||
.includes(data.post_view.community.id)
|
.includes(data.post_view.community.id)
|
||||||
) {
|
) {
|
||||||
|
@ -814,45 +767,43 @@ export class Home extends Component<any, HomeState> {
|
||||||
op == UserOperation.FeaturePost ||
|
op == UserOperation.FeaturePost ||
|
||||||
op == UserOperation.SavePost
|
op == UserOperation.SavePost
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
let data = wsJsonToRes<PostResponse>(msg);
|
||||||
editPostFindRes(data.post_view, this.state.posts);
|
editPostFindRes(data.post_view, this.state.posts);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.CreatePostLike) {
|
} else if (op == UserOperation.CreatePostLike) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
let data = wsJsonToRes<PostResponse>(msg);
|
||||||
createPostLikeFindRes(data.post_view, this.state.posts);
|
createPostLikeFindRes(data.post_view, this.state.posts);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.AddAdmin) {
|
} else if (op == UserOperation.AddAdmin) {
|
||||||
let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
|
let data = wsJsonToRes<AddAdminResponse>(msg);
|
||||||
this.setState(s => ((s.siteRes.admins = data.admins), s));
|
this.setState(s => ((s.siteRes.admins = data.admins), s));
|
||||||
} else if (op == UserOperation.BanPerson) {
|
} else if (op == UserOperation.BanPerson) {
|
||||||
let data = wsJsonToRes<BanPersonResponse>(msg, BanPersonResponse);
|
let data = wsJsonToRes<BanPersonResponse>(msg);
|
||||||
this.state.posts
|
this.state.posts
|
||||||
.filter(p => p.creator.id == data.person_view.person.id)
|
.filter(p => p.creator.id == data.person_view.person.id)
|
||||||
.forEach(p => (p.creator.banned = data.banned));
|
.forEach(p => (p.creator.banned = data.banned));
|
||||||
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.GetComments) {
|
} else if (op == UserOperation.GetComments) {
|
||||||
let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
|
let data = wsJsonToRes<GetCommentsResponse>(msg);
|
||||||
this.setState({ comments: data.comments, loading: false });
|
this.setState({ comments: data.comments, loading: false });
|
||||||
} else if (
|
} else if (
|
||||||
op == UserOperation.EditComment ||
|
op == UserOperation.EditComment ||
|
||||||
op == UserOperation.DeleteComment ||
|
op == UserOperation.DeleteComment ||
|
||||||
op == UserOperation.RemoveComment
|
op == UserOperation.RemoveComment
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
editCommentRes(data.comment_view, this.state.comments);
|
editCommentRes(data.comment_view, this.state.comments);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.CreateComment) {
|
} else if (op == UserOperation.CreateComment) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
|
|
||||||
// Necessary since it might be a user reply
|
// Necessary since it might be a user reply
|
||||||
if (data.form_id) {
|
if (data.form_id) {
|
||||||
// If you're on subscribed, only push it if you're subscribed.
|
// If you're on subscribed, only push it if you're subscribed.
|
||||||
if (this.state.listingType == ListingType.Subscribed) {
|
if (this.state.listingType == ListingType.Subscribed) {
|
||||||
if (
|
if (
|
||||||
UserService.Instance.myUserInfo
|
UserService.Instance.myUserInfo?.follows
|
||||||
.map(m => m.follows)
|
|
||||||
.unwrapOr([])
|
|
||||||
.map(c => c.community.id)
|
.map(c => c.community.id)
|
||||||
.includes(data.comment_view.community.id)
|
.includes(data.comment_view.community.id)
|
||||||
) {
|
) {
|
||||||
|
@ -864,23 +815,23 @@ export class Home extends Component<any, HomeState> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.SaveComment) {
|
} else if (op == UserOperation.SaveComment) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
saveCommentRes(data.comment_view, this.state.comments);
|
saveCommentRes(data.comment_view, this.state.comments);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.CreateCommentLike) {
|
} else if (op == UserOperation.CreateCommentLike) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
createCommentLikeRes(data.comment_view, this.state.comments);
|
createCommentLikeRes(data.comment_view, this.state.comments);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.BlockPerson) {
|
} else if (op == UserOperation.BlockPerson) {
|
||||||
let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
|
let data = wsJsonToRes<BlockPersonResponse>(msg);
|
||||||
updatePersonBlock(data);
|
updatePersonBlock(data);
|
||||||
} else if (op == UserOperation.CreatePostReport) {
|
} else if (op == UserOperation.CreatePostReport) {
|
||||||
let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
|
let data = wsJsonToRes<PostReportResponse>(msg);
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
toast(i18n.t("report_created"));
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.CreateCommentReport) {
|
} else if (op == UserOperation.CreateCommentReport) {
|
||||||
let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
|
let data = wsJsonToRes<CommentReportResponse>(msg);
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
toast(i18n.t("report_created"));
|
||||||
}
|
}
|
||||||
|
@ -890,7 +841,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
op == UserOperation.PurgeComment ||
|
op == UserOperation.PurgeComment ||
|
||||||
op == UserOperation.PurgeCommunity
|
op == UserOperation.PurgeCommunity
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<PurgeItemResponse>(msg, PurgeItemResponse);
|
let data = wsJsonToRes<PurgeItemResponse>(msg);
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
toast(i18n.t("purge_success"));
|
toast(i18n.t("purge_success"));
|
||||||
this.context.router.history.push(`/`);
|
this.context.router.history.push(`/`);
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None } from "@sniptt/monads";
|
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import { GetSiteResponse } from "lemmy-js-client";
|
import { GetSiteResponse } from "lemmy-js-client";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
|
@ -11,13 +10,12 @@ interface InstancesState {
|
||||||
|
|
||||||
export class Instances extends Component<any, InstancesState> {
|
export class Instances extends Component<any, InstancesState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(this.context);
|
||||||
private emptyState: InstancesState = {
|
state: InstancesState = {
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
|
@ -25,45 +23,37 @@ export class Instances extends Component<any, InstancesState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return this.state.siteRes.federated_instances.match({
|
let federated_instances = this.state.siteRes.federated_instances;
|
||||||
some: federated_instances => (
|
return federated_instances ? (
|
||||||
<div className="container-lg">
|
<div className="container-lg">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
/>
|
||||||
image={None}
|
<div className="row">
|
||||||
/>
|
<div className="col-md-6">
|
||||||
<div className="row">
|
<h5>{i18n.t("linked_instances")}</h5>
|
||||||
<div className="col-md-6">
|
{this.itemList(federated_instances.linked)}
|
||||||
<h5>{i18n.t("linked_instances")}</h5>
|
|
||||||
{this.itemList(federated_instances.linked)}
|
|
||||||
</div>
|
|
||||||
{federated_instances.allowed.match({
|
|
||||||
some: allowed =>
|
|
||||||
allowed.length > 0 && (
|
|
||||||
<div className="col-md-6">
|
|
||||||
<h5>{i18n.t("allowed_instances")}</h5>
|
|
||||||
{this.itemList(allowed)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
{federated_instances.blocked.match({
|
|
||||||
some: blocked =>
|
|
||||||
blocked.length > 0 && (
|
|
||||||
<div className="col-md-6">
|
|
||||||
<h5>{i18n.t("blocked_instances")}</h5>
|
|
||||||
{this.itemList(blocked)}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
|
{federated_instances.allowed &&
|
||||||
|
federated_instances.allowed.length > 0 && (
|
||||||
|
<div className="col-md-6">
|
||||||
|
<h5>{i18n.t("allowed_instances")}</h5>
|
||||||
|
{this.itemList(federated_instances.allowed)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{federated_instances.blocked &&
|
||||||
|
federated_instances.blocked.length > 0 && (
|
||||||
|
<div className="col-md-6">
|
||||||
|
<h5>{i18n.t("blocked_instances")}</h5>
|
||||||
|
{this.itemList(federated_instances.blocked)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
),
|
</div>
|
||||||
none: <></>,
|
) : (
|
||||||
});
|
<></>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
itemList(items: string[]) {
|
itemList(items: string[]) {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None } from "@sniptt/monads";
|
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import { GetSiteResponse } from "lemmy-js-client";
|
import { GetSiteResponse } from "lemmy-js-client";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
|
@ -11,13 +10,12 @@ interface LegalState {
|
||||||
|
|
||||||
export class Legal extends Component<any, LegalState> {
|
export class Legal extends Component<any, LegalState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(this.context);
|
||||||
private emptyState: LegalState = {
|
state: LegalState = {
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
|
@ -25,20 +23,16 @@ export class Legal extends Component<any, LegalState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let legal = this.state.siteRes.site_view.local_site.legal_information;
|
||||||
return (
|
return (
|
||||||
<div className="container-lg">
|
<div className="container-lg">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
{this.state.siteRes.site_view.local_site.legal_information.match({
|
{legal && (
|
||||||
some: legal => (
|
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(legal)} />
|
||||||
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(legal)} />
|
)}
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
import { None } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
Login as LoginForm,
|
Login as LoginI,
|
||||||
LoginResponse,
|
LoginResponse,
|
||||||
PasswordReset,
|
PasswordReset,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
@ -24,20 +23,20 @@ import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
loginForm: LoginForm;
|
form: {
|
||||||
|
username_or_email?: string;
|
||||||
|
password?: string;
|
||||||
|
};
|
||||||
loginLoading: boolean;
|
loginLoading: boolean;
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Login extends Component<any, State> {
|
export class Login extends Component<any, State> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(this.context);
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
|
|
||||||
emptyState: State = {
|
state: State = {
|
||||||
loginForm: new LoginForm({
|
form: {},
|
||||||
username_or_email: undefined,
|
|
||||||
password: undefined,
|
|
||||||
}),
|
|
||||||
loginLoading: false,
|
loginLoading: false,
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
};
|
};
|
||||||
|
@ -45,8 +44,6 @@ export class Login extends Component<any, State> {
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
|
@ -57,14 +54,14 @@ export class Login extends Component<any, State> {
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
// Navigate to home if already logged in
|
// Navigate to home if already logged in
|
||||||
if (UserService.Instance.myUserInfo.isSome()) {
|
if (UserService.Instance.myUserInfo) {
|
||||||
this.context.router.history.push("/");
|
this.context.router.history.push("/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,8 +79,6 @@ export class Login extends Component<any, State> {
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-12 col-lg-6 offset-lg-3">{this.loginForm()}</div>
|
<div className="col-12 col-lg-6 offset-lg-3">{this.loginForm()}</div>
|
||||||
|
@ -109,7 +104,7 @@ export class Login extends Component<any, State> {
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
id="login-email-or-username"
|
id="login-email-or-username"
|
||||||
value={this.state.loginForm.username_or_email}
|
value={this.state.form.username_or_email}
|
||||||
onInput={linkEvent(this, this.handleLoginUsernameChange)}
|
onInput={linkEvent(this, this.handleLoginUsernameChange)}
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
required
|
required
|
||||||
|
@ -125,7 +120,7 @@ export class Login extends Component<any, State> {
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="login-password"
|
id="login-password"
|
||||||
value={this.state.loginForm.password}
|
value={this.state.form.password}
|
||||||
onInput={linkEvent(this, this.handleLoginPasswordChange)}
|
onInput={linkEvent(this, this.handleLoginPasswordChange)}
|
||||||
className="form-control"
|
className="form-control"
|
||||||
autoComplete="current-password"
|
autoComplete="current-password"
|
||||||
|
@ -136,7 +131,10 @@ export class Login extends Component<any, State> {
|
||||||
type="button"
|
type="button"
|
||||||
onClick={linkEvent(this, this.handlePasswordReset)}
|
onClick={linkEvent(this, this.handlePasswordReset)}
|
||||||
className="btn p-0 btn-link d-inline-block float-right text-muted small font-weight-bold pointer-events not-allowed"
|
className="btn p-0 btn-link d-inline-block float-right text-muted small font-weight-bold pointer-events not-allowed"
|
||||||
disabled={!validEmail(this.state.loginForm.username_or_email)}
|
disabled={
|
||||||
|
!!this.state.form.username_or_email &&
|
||||||
|
!validEmail(this.state.form.username_or_email)
|
||||||
|
}
|
||||||
title={i18n.t("no_password_reset")}
|
title={i18n.t("no_password_reset")}
|
||||||
>
|
>
|
||||||
{i18n.t("forgot_password")}
|
{i18n.t("forgot_password")}
|
||||||
|
@ -158,25 +156,35 @@ export class Login extends Component<any, State> {
|
||||||
handleLoginSubmit(i: Login, event: any) {
|
handleLoginSubmit(i: Login, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.setState({ loginLoading: true });
|
i.setState({ loginLoading: true });
|
||||||
WebSocketService.Instance.send(wsClient.login(i.state.loginForm));
|
let lForm = i.state.form;
|
||||||
|
let username_or_email = lForm.username_or_email;
|
||||||
|
let password = lForm.password;
|
||||||
|
if (username_or_email && password) {
|
||||||
|
let form: LoginI = {
|
||||||
|
username_or_email,
|
||||||
|
password,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.login(form));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLoginUsernameChange(i: Login, event: any) {
|
handleLoginUsernameChange(i: Login, event: any) {
|
||||||
i.state.loginForm.username_or_email = event.target.value;
|
i.state.form.username_or_email = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLoginPasswordChange(i: Login, event: any) {
|
handleLoginPasswordChange(i: Login, event: any) {
|
||||||
i.state.loginForm.password = event.target.value;
|
i.state.form.password = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePasswordReset(i: Login, event: any) {
|
handlePasswordReset(i: Login, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let resetForm = new PasswordReset({
|
let email = i.state.form.username_or_email;
|
||||||
email: i.state.loginForm.username_or_email,
|
if (email) {
|
||||||
});
|
let resetForm: PasswordReset = { email };
|
||||||
WebSocketService.Instance.send(wsClient.passwordReset(resetForm));
|
WebSocketService.Instance.send(wsClient.passwordReset(resetForm));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -184,19 +192,18 @@ export class Login extends Component<any, State> {
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
this.setState(this.emptyState);
|
this.setState({ form: {} });
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (op == UserOperation.Login) {
|
if (op == UserOperation.Login) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
let data = wsJsonToRes<LoginResponse>(msg);
|
||||||
this.setState(this.emptyState);
|
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
this.props.history.push("/");
|
this.props.history.push("/");
|
||||||
location.reload();
|
location.reload();
|
||||||
} else if (op == UserOperation.PasswordReset) {
|
} else if (op == UserOperation.PasswordReset) {
|
||||||
toast(i18n.t("reset_password_mail_sent"));
|
toast(i18n.t("reset_password_mail_sent"));
|
||||||
} else if (op == UserOperation.GetSite) {
|
} else if (op == UserOperation.GetSite) {
|
||||||
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
let data = wsJsonToRes<GetSiteResponse>(msg);
|
||||||
this.setState({ siteRes: data });
|
this.setState({ siteRes: data });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
import { None, Some } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { Helmet } from "inferno-helmet";
|
import { Helmet } from "inferno-helmet";
|
||||||
import {
|
import {
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
LoginResponse,
|
LoginResponse,
|
||||||
Register,
|
Register,
|
||||||
toUndefined,
|
|
||||||
UserOperation,
|
UserOperation,
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
wsUserOp,
|
wsUserOp,
|
||||||
|
@ -19,7 +17,17 @@ import { Spinner } from "../common/icon";
|
||||||
import { SiteForm } from "./site-form";
|
import { SiteForm } from "./site-form";
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
userForm: Register;
|
form: {
|
||||||
|
username?: string;
|
||||||
|
email?: string;
|
||||||
|
password?: string;
|
||||||
|
password_verify?: string;
|
||||||
|
show_nsfw: boolean;
|
||||||
|
captcha_uuid?: string;
|
||||||
|
captcha_answer?: string;
|
||||||
|
honeypot?: string;
|
||||||
|
answer?: string;
|
||||||
|
};
|
||||||
doneRegisteringUser: boolean;
|
doneRegisteringUser: boolean;
|
||||||
userLoading: boolean;
|
userLoading: boolean;
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
|
@ -29,20 +37,11 @@ export class Setup extends Component<any, State> {
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(this.context);
|
||||||
|
|
||||||
private emptyState: State = {
|
state: State = {
|
||||||
userForm: new Register({
|
form: {
|
||||||
username: undefined,
|
|
||||||
password: undefined,
|
|
||||||
password_verify: undefined,
|
|
||||||
show_nsfw: true,
|
show_nsfw: true,
|
||||||
// The first admin signup doesn't need a captcha
|
},
|
||||||
captcha_uuid: None,
|
doneRegisteringUser: !!UserService.Instance.myUserInfo,
|
||||||
captcha_answer: None,
|
|
||||||
email: None,
|
|
||||||
honeypot: None,
|
|
||||||
answer: None,
|
|
||||||
}),
|
|
||||||
doneRegisteringUser: UserService.Instance.myUserInfo.isSome(),
|
|
||||||
userLoading: false,
|
userLoading: false,
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
};
|
};
|
||||||
|
@ -50,8 +49,6 @@ export class Setup extends Component<any, State> {
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
|
|
||||||
this.subscription = WebSocketService.Instance.subject
|
this.subscription = WebSocketService.Instance.subject
|
||||||
.pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
|
.pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
|
||||||
.subscribe(
|
.subscribe(
|
||||||
|
@ -100,7 +97,7 @@ export class Setup extends Component<any, State> {
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
id="username"
|
id="username"
|
||||||
value={this.state.userForm.username}
|
value={this.state.form.username}
|
||||||
onInput={linkEvent(this, this.handleRegisterUsernameChange)}
|
onInput={linkEvent(this, this.handleRegisterUsernameChange)}
|
||||||
required
|
required
|
||||||
minLength={3}
|
minLength={3}
|
||||||
|
@ -119,7 +116,7 @@ export class Setup extends Component<any, State> {
|
||||||
id="email"
|
id="email"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
placeholder={i18n.t("optional")}
|
placeholder={i18n.t("optional")}
|
||||||
value={toUndefined(this.state.userForm.email)}
|
value={this.state.form.email}
|
||||||
onInput={linkEvent(this, this.handleRegisterEmailChange)}
|
onInput={linkEvent(this, this.handleRegisterEmailChange)}
|
||||||
minLength={3}
|
minLength={3}
|
||||||
/>
|
/>
|
||||||
|
@ -133,7 +130,7 @@ export class Setup extends Component<any, State> {
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="password"
|
id="password"
|
||||||
value={this.state.userForm.password}
|
value={this.state.form.password}
|
||||||
onInput={linkEvent(this, this.handleRegisterPasswordChange)}
|
onInput={linkEvent(this, this.handleRegisterPasswordChange)}
|
||||||
className="form-control"
|
className="form-control"
|
||||||
required
|
required
|
||||||
|
@ -151,7 +148,7 @@ export class Setup extends Component<any, State> {
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="verify-password"
|
id="verify-password"
|
||||||
value={this.state.userForm.password_verify}
|
value={this.state.form.password_verify}
|
||||||
onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
|
onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
|
||||||
className="form-control"
|
className="form-control"
|
||||||
required
|
required
|
||||||
|
@ -176,26 +173,40 @@ export class Setup extends Component<any, State> {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.setState({ userLoading: true });
|
i.setState({ userLoading: true });
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
WebSocketService.Instance.send(wsClient.register(i.state.userForm));
|
let cForm = i.state.form;
|
||||||
|
if (cForm.username && cForm.password && cForm.password_verify) {
|
||||||
|
let form: Register = {
|
||||||
|
username: cForm.username,
|
||||||
|
password: cForm.password,
|
||||||
|
password_verify: cForm.password_verify,
|
||||||
|
email: cForm.email,
|
||||||
|
show_nsfw: cForm.show_nsfw,
|
||||||
|
captcha_uuid: cForm.captcha_uuid,
|
||||||
|
captcha_answer: cForm.captcha_answer,
|
||||||
|
honeypot: cForm.honeypot,
|
||||||
|
answer: cForm.answer,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.register(form));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegisterUsernameChange(i: Setup, event: any) {
|
handleRegisterUsernameChange(i: Setup, event: any) {
|
||||||
i.state.userForm.username = event.target.value;
|
i.state.form.username = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegisterEmailChange(i: Setup, event: any) {
|
handleRegisterEmailChange(i: Setup, event: any) {
|
||||||
i.state.userForm.email = Some(event.target.value);
|
i.state.form.email = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegisterPasswordChange(i: Setup, event: any) {
|
handleRegisterPasswordChange(i: Setup, event: any) {
|
||||||
i.state.userForm.password = event.target.value;
|
i.state.form.password = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegisterPasswordVerifyChange(i: Setup, event: any) {
|
handleRegisterPasswordVerifyChange(i: Setup, event: any) {
|
||||||
i.state.userForm.password_verify = event.target.value;
|
i.state.form.password_verify = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,10 +217,10 @@ export class Setup extends Component<any, State> {
|
||||||
this.setState({ userLoading: false });
|
this.setState({ userLoading: false });
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.Register) {
|
} else if (op == UserOperation.Register) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
let data = wsJsonToRes<LoginResponse>(msg);
|
||||||
this.setState({ userLoading: false });
|
this.setState({ userLoading: false });
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
if (UserService.Instance.jwtInfo.isSome()) {
|
if (UserService.Instance.jwtInfo) {
|
||||||
this.setState({ doneRegisteringUser: true });
|
this.setState({ doneRegisteringUser: true });
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.CreateSite) {
|
} else if (op == UserOperation.CreateSite) {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import { Options, passwordStrength } from "check-password-strength";
|
import { Options, passwordStrength } from "check-password-strength";
|
||||||
import { I18nKeys } from "i18next";
|
import { I18nKeys } from "i18next";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
|
@ -9,8 +8,8 @@ import {
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
LoginResponse,
|
LoginResponse,
|
||||||
Register,
|
Register,
|
||||||
|
RegistrationMode,
|
||||||
SiteView,
|
SiteView,
|
||||||
toUndefined,
|
|
||||||
UserOperation,
|
UserOperation,
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
wsUserOp,
|
wsUserOp,
|
||||||
|
@ -60,32 +59,33 @@ const passwordStrengthOptions: Options<string> = [
|
||||||
];
|
];
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
registerForm: Register;
|
form: {
|
||||||
|
username?: string;
|
||||||
|
email?: string;
|
||||||
|
password?: string;
|
||||||
|
password_verify?: string;
|
||||||
|
show_nsfw: boolean;
|
||||||
|
captcha_uuid?: string;
|
||||||
|
captcha_answer?: string;
|
||||||
|
honeypot?: string;
|
||||||
|
answer?: string;
|
||||||
|
};
|
||||||
registerLoading: boolean;
|
registerLoading: boolean;
|
||||||
captcha: Option<GetCaptchaResponse>;
|
captcha?: GetCaptchaResponse;
|
||||||
captchaPlaying: boolean;
|
captchaPlaying: boolean;
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Signup extends Component<any, State> {
|
export class Signup extends Component<any, State> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(this.context);
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
private audio: HTMLAudioElement;
|
private audio?: HTMLAudioElement;
|
||||||
|
|
||||||
emptyState: State = {
|
state: State = {
|
||||||
registerForm: new Register({
|
form: {
|
||||||
username: undefined,
|
|
||||||
password: undefined,
|
|
||||||
password_verify: undefined,
|
|
||||||
show_nsfw: false,
|
show_nsfw: false,
|
||||||
captcha_uuid: None,
|
},
|
||||||
captcha_answer: None,
|
|
||||||
honeypot: None,
|
|
||||||
answer: None,
|
|
||||||
email: None,
|
|
||||||
}),
|
|
||||||
registerLoading: false,
|
registerLoading: false,
|
||||||
captcha: None,
|
|
||||||
captchaPlaying: false,
|
captchaPlaying: false,
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
};
|
};
|
||||||
|
@ -93,7 +93,6 @@ export class Signup extends Component<any, State> {
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
this.handleAnswerChange = this.handleAnswerChange.bind(this);
|
this.handleAnswerChange = this.handleAnswerChange.bind(this);
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
|
@ -106,7 +105,7 @@ export class Signup extends Component<any, State> {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,8 +130,6 @@ export class Signup extends Component<any, State> {
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-12 col-lg-6 offset-lg-3">
|
<div className="col-12 col-lg-6 offset-lg-3">
|
||||||
|
@ -172,7 +169,7 @@ export class Signup extends Component<any, State> {
|
||||||
type="text"
|
type="text"
|
||||||
id="register-username"
|
id="register-username"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
value={this.state.registerForm.username}
|
value={this.state.form.username}
|
||||||
onInput={linkEvent(this, this.handleRegisterUsernameChange)}
|
onInput={linkEvent(this, this.handleRegisterUsernameChange)}
|
||||||
required
|
required
|
||||||
minLength={3}
|
minLength={3}
|
||||||
|
@ -196,14 +193,15 @@ export class Signup extends Component<any, State> {
|
||||||
? i18n.t("required")
|
? i18n.t("required")
|
||||||
: i18n.t("optional")
|
: i18n.t("optional")
|
||||||
}
|
}
|
||||||
value={toUndefined(this.state.registerForm.email)}
|
value={this.state.form.email}
|
||||||
autoComplete="email"
|
autoComplete="email"
|
||||||
onInput={linkEvent(this, this.handleRegisterEmailChange)}
|
onInput={linkEvent(this, this.handleRegisterEmailChange)}
|
||||||
required={siteView.local_site.require_email_verification}
|
required={siteView.local_site.require_email_verification}
|
||||||
minLength={3}
|
minLength={3}
|
||||||
/>
|
/>
|
||||||
{!siteView.local_site.require_email_verification &&
|
{!siteView.local_site.require_email_verification &&
|
||||||
!this.state.registerForm.email.map(validEmail).unwrapOr(true) && (
|
this.state.form.email &&
|
||||||
|
!validEmail(this.state.form.email) && (
|
||||||
<div className="mt-2 mb-0 alert alert-warning" role="alert">
|
<div className="mt-2 mb-0 alert alert-warning" role="alert">
|
||||||
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
||||||
{i18n.t("no_password_reset")}
|
{i18n.t("no_password_reset")}
|
||||||
|
@ -223,7 +221,7 @@ export class Signup extends Component<any, State> {
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="register-password"
|
id="register-password"
|
||||||
value={this.state.registerForm.password}
|
value={this.state.form.password}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
onInput={linkEvent(this, this.handleRegisterPasswordChange)}
|
onInput={linkEvent(this, this.handleRegisterPasswordChange)}
|
||||||
minLength={10}
|
minLength={10}
|
||||||
|
@ -231,7 +229,7 @@ export class Signup extends Component<any, State> {
|
||||||
className="form-control"
|
className="form-control"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
{this.state.registerForm.password && (
|
{this.state.form.password && (
|
||||||
<div className={this.passwordColorClass}>
|
<div className={this.passwordColorClass}>
|
||||||
{i18n.t(this.passwordStrength as I18nKeys)}
|
{i18n.t(this.passwordStrength as I18nKeys)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -250,7 +248,7 @@ export class Signup extends Component<any, State> {
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="register-verify-password"
|
id="register-verify-password"
|
||||||
value={this.state.registerForm.password_verify}
|
value={this.state.form.password_verify}
|
||||||
autoComplete="new-password"
|
autoComplete="new-password"
|
||||||
onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
|
onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
|
||||||
maxLength={60}
|
maxLength={60}
|
||||||
|
@ -260,7 +258,8 @@ export class Signup extends Component<any, State> {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{siteView.local_site.require_application && (
|
{siteView.local_site.registration_mode ==
|
||||||
|
RegistrationMode.RequireApplication && (
|
||||||
<>
|
<>
|
||||||
<div className="form-group row">
|
<div className="form-group row">
|
||||||
<div className="offset-sm-2 col-sm-10">
|
<div className="offset-sm-2 col-sm-10">
|
||||||
|
@ -268,15 +267,14 @@ export class Signup extends Component<any, State> {
|
||||||
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
||||||
{i18n.t("fill_out_application")}
|
{i18n.t("fill_out_application")}
|
||||||
</div>
|
</div>
|
||||||
{siteView.local_site.application_question.match({
|
{siteView.local_site.application_question && (
|
||||||
some: question => (
|
<div
|
||||||
<div
|
className="md-div"
|
||||||
className="md-div"
|
dangerouslySetInnerHTML={mdToHtml(
|
||||||
dangerouslySetInnerHTML={mdToHtml(question)}
|
siteView.local_site.application_question
|
||||||
/>
|
)}
|
||||||
),
|
/>
|
||||||
none: <></>,
|
)}
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -289,11 +287,6 @@ export class Signup extends Component<any, State> {
|
||||||
</label>
|
</label>
|
||||||
<div className="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={None}
|
|
||||||
initialLanguageId={None}
|
|
||||||
placeholder={None}
|
|
||||||
buttonTitle={None}
|
|
||||||
maxLength={None}
|
|
||||||
onContentChange={this.handleAnswerChange}
|
onContentChange={this.handleAnswerChange}
|
||||||
hideNavigationWarnings
|
hideNavigationWarnings
|
||||||
allLanguages={[]}
|
allLanguages={[]}
|
||||||
|
@ -304,7 +297,7 @@ export class Signup extends Component<any, State> {
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{this.state.captcha.isSome() && (
|
{this.state.captcha && (
|
||||||
<div className="form-group row">
|
<div className="form-group row">
|
||||||
<label className="col-sm-2" htmlFor="register-captcha">
|
<label className="col-sm-2" htmlFor="register-captcha">
|
||||||
<span className="mr-2">{i18n.t("enter_code")}</span>
|
<span className="mr-2">{i18n.t("enter_code")}</span>
|
||||||
|
@ -323,7 +316,7 @@ export class Signup extends Component<any, State> {
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
id="register-captcha"
|
id="register-captcha"
|
||||||
value={toUndefined(this.state.registerForm.captcha_answer)}
|
value={this.state.form.captcha_answer}
|
||||||
onInput={linkEvent(
|
onInput={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleRegisterCaptchaAnswerChange
|
this.handleRegisterCaptchaAnswerChange
|
||||||
|
@ -341,7 +334,7 @@ export class Signup extends Component<any, State> {
|
||||||
className="form-check-input"
|
className="form-check-input"
|
||||||
id="register-show-nsfw"
|
id="register-show-nsfw"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.registerForm.show_nsfw}
|
checked={this.state.form.show_nsfw}
|
||||||
onChange={linkEvent(this, this.handleRegisterShowNsfwChange)}
|
onChange={linkEvent(this, this.handleRegisterShowNsfwChange)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
|
@ -361,7 +354,7 @@ export class Signup extends Component<any, State> {
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control honeypot"
|
className="form-control honeypot"
|
||||||
id="register-honey"
|
id="register-honey"
|
||||||
value={toUndefined(this.state.registerForm.honeypot)}
|
value={this.state.form.honeypot}
|
||||||
onInput={linkEvent(this, this.handleHoneyPotChange)}
|
onInput={linkEvent(this, this.handleHoneyPotChange)}
|
||||||
/>
|
/>
|
||||||
<div className="form-group row">
|
<div className="form-group row">
|
||||||
|
@ -380,51 +373,46 @@ export class Signup extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
showCaptcha() {
|
showCaptcha() {
|
||||||
return this.state.captcha.match({
|
let captchaRes = this.state.captcha?.ok;
|
||||||
some: captcha => (
|
return captchaRes ? (
|
||||||
<div className="col-sm-4">
|
<div className="col-sm-4">
|
||||||
{captcha.ok.match({
|
<>
|
||||||
some: res => (
|
<img
|
||||||
<>
|
className="rounded-top img-fluid"
|
||||||
<img
|
src={this.captchaPngSrc(captchaRes)}
|
||||||
className="rounded-top img-fluid"
|
style="border-bottom-right-radius: 0; border-bottom-left-radius: 0;"
|
||||||
src={this.captchaPngSrc(res)}
|
alt={i18n.t("captcha")}
|
||||||
style="border-bottom-right-radius: 0; border-bottom-left-radius: 0;"
|
/>
|
||||||
alt={i18n.t("captcha")}
|
{captchaRes.wav && (
|
||||||
/>
|
<button
|
||||||
{res.wav.isSome() && (
|
className="rounded-bottom btn btn-sm btn-secondary btn-block"
|
||||||
<button
|
style="border-top-right-radius: 0; border-top-left-radius: 0;"
|
||||||
className="rounded-bottom btn btn-sm btn-secondary btn-block"
|
title={i18n.t("play_captcha_audio")}
|
||||||
style="border-top-right-radius: 0; border-top-left-radius: 0;"
|
onClick={linkEvent(this, this.handleCaptchaPlay)}
|
||||||
title={i18n.t("play_captcha_audio")}
|
type="button"
|
||||||
onClick={linkEvent(this, this.handleCaptchaPlay)}
|
disabled={this.state.captchaPlaying}
|
||||||
type="button"
|
>
|
||||||
disabled={this.state.captchaPlaying}
|
<Icon icon="play" classes="icon-play" />
|
||||||
>
|
</button>
|
||||||
<Icon icon="play" classes="icon-play" />
|
)}
|
||||||
</button>
|
</>
|
||||||
)}
|
</div>
|
||||||
</>
|
) : (
|
||||||
),
|
<></>
|
||||||
none: <></>,
|
);
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get passwordStrength() {
|
get passwordStrength(): string | undefined {
|
||||||
return passwordStrength(
|
let password = this.state.form.password;
|
||||||
this.state.registerForm.password,
|
return password
|
||||||
passwordStrengthOptions
|
? passwordStrength(password, passwordStrengthOptions).value
|
||||||
).value;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
get passwordColorClass(): string {
|
get passwordColorClass(): string {
|
||||||
let strength = this.passwordStrength;
|
let strength = this.passwordStrength;
|
||||||
|
|
||||||
if (["weak", "medium"].includes(strength)) {
|
if (strength && ["weak", "medium"].includes(strength)) {
|
||||||
return "text-warning";
|
return "text-warning";
|
||||||
} else if (strength == "strong") {
|
} else if (strength == "strong") {
|
||||||
return "text-success";
|
return "text-success";
|
||||||
|
@ -436,53 +424,67 @@ export class Signup extends Component<any, State> {
|
||||||
handleRegisterSubmit(i: Signup, event: any) {
|
handleRegisterSubmit(i: Signup, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.setState({ registerLoading: true });
|
i.setState({ registerLoading: true });
|
||||||
WebSocketService.Instance.send(wsClient.register(i.state.registerForm));
|
let cForm = i.state.form;
|
||||||
|
if (cForm.username && cForm.password && cForm.password_verify) {
|
||||||
|
let form: Register = {
|
||||||
|
username: cForm.username,
|
||||||
|
password: cForm.password,
|
||||||
|
password_verify: cForm.password_verify,
|
||||||
|
email: cForm.email,
|
||||||
|
show_nsfw: cForm.show_nsfw,
|
||||||
|
captcha_uuid: cForm.captcha_uuid,
|
||||||
|
captcha_answer: cForm.captcha_answer,
|
||||||
|
honeypot: cForm.honeypot,
|
||||||
|
answer: cForm.answer,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.register(form));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegisterUsernameChange(i: Signup, event: any) {
|
handleRegisterUsernameChange(i: Signup, event: any) {
|
||||||
i.state.registerForm.username = event.target.value;
|
i.state.form.username = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegisterEmailChange(i: Signup, event: any) {
|
handleRegisterEmailChange(i: Signup, event: any) {
|
||||||
i.state.registerForm.email = Some(event.target.value);
|
i.state.form.email = event.target.value;
|
||||||
if (i.state.registerForm.email.unwrap() == "") {
|
if (i.state.form.email == "") {
|
||||||
i.state.registerForm.email = None;
|
i.state.form.email = undefined;
|
||||||
}
|
}
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegisterPasswordChange(i: Signup, event: any) {
|
handleRegisterPasswordChange(i: Signup, event: any) {
|
||||||
i.state.registerForm.password = event.target.value;
|
i.state.form.password = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegisterPasswordVerifyChange(i: Signup, event: any) {
|
handleRegisterPasswordVerifyChange(i: Signup, event: any) {
|
||||||
i.state.registerForm.password_verify = event.target.value;
|
i.state.form.password_verify = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegisterShowNsfwChange(i: Signup, event: any) {
|
handleRegisterShowNsfwChange(i: Signup, event: any) {
|
||||||
i.state.registerForm.show_nsfw = event.target.checked;
|
i.state.form.show_nsfw = event.target.checked;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegisterCaptchaAnswerChange(i: Signup, event: any) {
|
handleRegisterCaptchaAnswerChange(i: Signup, event: any) {
|
||||||
i.state.registerForm.captcha_answer = Some(event.target.value);
|
i.state.form.captcha_answer = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAnswerChange(val: string) {
|
handleAnswerChange(val: string) {
|
||||||
this.setState(s => ((s.registerForm.answer = Some(val)), s));
|
this.setState(s => ((s.form.answer = val), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleHoneyPotChange(i: Signup, event: any) {
|
handleHoneyPotChange(i: Signup, event: any) {
|
||||||
i.state.registerForm.honeypot = Some(event.target.value);
|
i.state.form.honeypot = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegenCaptcha(i: Signup) {
|
handleRegenCaptcha(i: Signup) {
|
||||||
i.audio = null;
|
i.audio = undefined;
|
||||||
i.setState({ captchaPlaying: false });
|
i.setState({ captchaPlaying: false });
|
||||||
WebSocketService.Instance.send(wsClient.getCaptcha());
|
WebSocketService.Instance.send(wsClient.getCaptcha());
|
||||||
}
|
}
|
||||||
|
@ -490,28 +492,23 @@ export class Signup extends Component<any, State> {
|
||||||
handleCaptchaPlay(i: Signup) {
|
handleCaptchaPlay(i: Signup) {
|
||||||
// This was a bad bug, it should only build the new audio on a new file.
|
// This was a bad bug, it should only build the new audio on a new file.
|
||||||
// Replays would stop prematurely if this was rebuilt every time.
|
// Replays would stop prematurely if this was rebuilt every time.
|
||||||
i.state.captcha.match({
|
let captchaRes = i.state.captcha?.ok;
|
||||||
some: captcha =>
|
if (captchaRes) {
|
||||||
captcha.ok.match({
|
if (!i.audio) {
|
||||||
some: res => {
|
let base64 = `data:audio/wav;base64,${captchaRes.wav}`;
|
||||||
if (i.audio == null) {
|
i.audio = new Audio(base64);
|
||||||
let base64 = `data:audio/wav;base64,${res.wav}`;
|
i.audio.play();
|
||||||
i.audio = new Audio(base64);
|
|
||||||
}
|
|
||||||
|
|
||||||
i.audio.play();
|
i.setState({ captchaPlaying: true });
|
||||||
|
|
||||||
i.setState({ captchaPlaying: true });
|
i.audio.addEventListener("ended", () => {
|
||||||
|
if (i.audio) {
|
||||||
i.audio.addEventListener("ended", () => {
|
i.audio.currentTime = 0;
|
||||||
i.audio.currentTime = 0;
|
i.setState({ captchaPlaying: false });
|
||||||
i.setState({ captchaPlaying: false });
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
none: void 0,
|
}
|
||||||
}),
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
captchaPngSrc(captcha: CaptchaResponse) {
|
captchaPngSrc(captcha: CaptchaResponse) {
|
||||||
|
@ -523,17 +520,15 @@ export class Signup extends Component<any, State> {
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
this.setState(this.emptyState);
|
this.setState(s => ((s.form.captcha_answer = undefined), s));
|
||||||
this.setState(s => ((s.registerForm.captcha_answer = undefined), s));
|
|
||||||
// Refetch another captcha
|
// Refetch another captcha
|
||||||
// WebSocketService.Instance.send(wsClient.getCaptcha());
|
// WebSocketService.Instance.send(wsClient.getCaptcha());
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (op == UserOperation.Register) {
|
if (op == UserOperation.Register) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
let data = wsJsonToRes<LoginResponse>(msg);
|
||||||
this.setState(this.emptyState);
|
|
||||||
// Only log them in if a jwt was set
|
// Only log them in if a jwt was set
|
||||||
if (data.jwt.isSome()) {
|
if (data.jwt) {
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
this.props.history.push("/communities");
|
this.props.history.push("/communities");
|
||||||
location.reload();
|
location.reload();
|
||||||
|
@ -547,20 +542,15 @@ export class Signup extends Component<any, State> {
|
||||||
this.props.history.push("/");
|
this.props.history.push("/");
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.GetCaptcha) {
|
} else if (op == UserOperation.GetCaptcha) {
|
||||||
let data = wsJsonToRes<GetCaptchaResponse>(msg, GetCaptchaResponse);
|
let data = wsJsonToRes<GetCaptchaResponse>(msg);
|
||||||
data.ok.match({
|
if (data.ok) {
|
||||||
some: res => {
|
this.setState({ captcha: data });
|
||||||
this.setState({ captcha: Some(data) });
|
this.setState(s => ((s.form.captcha_uuid = data.ok?.uuid), s));
|
||||||
this.setState(
|
}
|
||||||
s => ((s.registerForm.captcha_uuid = Some(res.uuid)), s)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
} else if (op == UserOperation.PasswordReset) {
|
} else if (op == UserOperation.PasswordReset) {
|
||||||
toast(i18n.t("reset_password_mail_sent"));
|
toast(i18n.t("reset_password_mail_sent"));
|
||||||
} else if (op == UserOperation.GetSite) {
|
} else if (op == UserOperation.GetSite) {
|
||||||
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
let data = wsJsonToRes<GetSiteResponse>(msg);
|
||||||
this.setState({ siteRes: data });
|
this.setState({ siteRes: data });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { Link } from "inferno-router";
|
import { Link } from "inferno-router";
|
||||||
import { PersonViewSafe, Site, SiteAggregates } from "lemmy-js-client";
|
import { PersonViewSafe, Site, SiteAggregates } from "lemmy-js-client";
|
||||||
|
@ -11,9 +10,9 @@ import { PersonListing } from "../person/person-listing";
|
||||||
interface SiteSidebarProps {
|
interface SiteSidebarProps {
|
||||||
site: Site;
|
site: Site;
|
||||||
showLocal: boolean;
|
showLocal: boolean;
|
||||||
counts: Option<SiteAggregates>;
|
counts?: SiteAggregates;
|
||||||
admins: Option<PersonViewSafe[]>;
|
admins?: PersonViewSafe[];
|
||||||
online: Option<number>;
|
online?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SiteSidebarState {
|
interface SiteSidebarState {
|
||||||
|
@ -21,13 +20,12 @@ interface SiteSidebarState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
private emptyState: SiteSidebarState = {
|
state: SiteSidebarState = {
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -38,7 +36,7 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
<div className="mb-2">{this.siteName()}</div>
|
<div className="mb-2">{this.siteName()}</div>
|
||||||
{!this.state.collapsed && (
|
{!this.state.collapsed && (
|
||||||
<>
|
<>
|
||||||
<BannerIconHeader banner={this.props.site.banner} icon={None} />
|
<BannerIconHeader banner={this.props.site.banner} />
|
||||||
{this.siteInfo()}
|
{this.siteInfo()}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
@ -72,22 +70,10 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
let site = this.props.site;
|
let site = this.props.site;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{site.description.match({
|
{site.description && <h6>{site.description}</h6>}
|
||||||
some: description => <h6>{description}</h6>,
|
{site.sidebar && this.siteSidebar(site.sidebar)}
|
||||||
none: <></>,
|
{this.props.counts && this.badges(this.props.counts)}
|
||||||
})}
|
{this.props.admins && this.admins(this.props.admins)}
|
||||||
{site.sidebar.match({
|
|
||||||
some: sidebar => this.siteSidebar(sidebar),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
{this.props.counts.match({
|
|
||||||
some: counts => this.badges(counts),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
{this.props.admins.match({
|
|
||||||
some: admins => this.admins(admins),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -113,7 +99,7 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
|
|
||||||
badges(siteAggregates: SiteAggregates) {
|
badges(siteAggregates: SiteAggregates) {
|
||||||
let counts = siteAggregates;
|
let counts = siteAggregates;
|
||||||
let online = this.props.online.unwrapOr(1);
|
let online = this.props.online ?? 1;
|
||||||
return (
|
return (
|
||||||
<ul className="my-2 list-inline">
|
<ul className="my-2 list-inline">
|
||||||
<li className="list-inline-item badge badge-secondary">
|
<li className="list-inline-item badge badge-secondary">
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { Link } from "inferno-router";
|
import { Link } from "inferno-router";
|
||||||
import {
|
import {
|
||||||
|
@ -24,7 +23,6 @@ import {
|
||||||
ModRemovePostView,
|
ModRemovePostView,
|
||||||
ModTransferCommunityView,
|
ModTransferCommunityView,
|
||||||
PersonSafe,
|
PersonSafe,
|
||||||
toUndefined,
|
|
||||||
UserOperation,
|
UserOperation,
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
wsUserOp,
|
wsUserOp,
|
||||||
|
@ -37,12 +35,12 @@ import { WebSocketService } from "../services";
|
||||||
import {
|
import {
|
||||||
amAdmin,
|
amAdmin,
|
||||||
amMod,
|
amMod,
|
||||||
auth,
|
|
||||||
choicesConfig,
|
choicesConfig,
|
||||||
debounce,
|
debounce,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
fetchUsers,
|
fetchUsers,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
|
myAuth,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
toast,
|
toast,
|
||||||
wsClient,
|
wsClient,
|
||||||
|
@ -57,7 +55,7 @@ import { PersonListing } from "./person/person-listing";
|
||||||
type ModlogType = {
|
type ModlogType = {
|
||||||
id: number;
|
id: number;
|
||||||
type_: ModlogActionType;
|
type_: ModlogActionType;
|
||||||
moderator: Option<PersonSafe>;
|
moderator?: PersonSafe;
|
||||||
view:
|
view:
|
||||||
| ModRemovePostView
|
| ModRemovePostView
|
||||||
| ModLockPostView
|
| ModLockPostView
|
||||||
|
@ -81,43 +79,32 @@ if (isBrowser()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ModlogState {
|
interface ModlogState {
|
||||||
res: Option<GetModlogResponse>;
|
res?: GetModlogResponse;
|
||||||
communityId: Option<number>;
|
communityId?: number;
|
||||||
communityMods: Option<CommunityModeratorView[]>;
|
communityMods?: CommunityModeratorView[];
|
||||||
communityName: Option<string>;
|
communityName?: string;
|
||||||
page: number;
|
page: number;
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
filter_action: ModlogActionType;
|
filter_action: ModlogActionType;
|
||||||
filter_user: Option<number>;
|
filter_user?: number;
|
||||||
filter_mod: Option<number>;
|
filter_mod?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Modlog extends Component<any, ModlogState> {
|
export class Modlog extends Component<any, ModlogState> {
|
||||||
private isoData = setIsoData(
|
private isoData = setIsoData(this.context);
|
||||||
this.context,
|
private subscription?: Subscription;
|
||||||
GetModlogResponse,
|
|
||||||
GetCommunityResponse
|
|
||||||
);
|
|
||||||
private subscription: Subscription;
|
|
||||||
private userChoices: any;
|
private userChoices: any;
|
||||||
private modChoices: any;
|
private modChoices: any;
|
||||||
private emptyState: ModlogState = {
|
state: ModlogState = {
|
||||||
res: None,
|
|
||||||
communityId: None,
|
|
||||||
communityMods: None,
|
|
||||||
communityName: None,
|
|
||||||
page: 1,
|
page: 1,
|
||||||
loading: true,
|
loading: true,
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
filter_action: ModlogActionType.All,
|
filter_action: ModlogActionType.All,
|
||||||
filter_user: None,
|
|
||||||
filter_mod: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
|
||||||
this.handlePageChange = this.handlePageChange.bind(this);
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
|
@ -126,27 +113,25 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
communityId: this.props.match.params.community_id
|
communityId: this.props.match.params.community_id
|
||||||
? Some(Number(this.props.match.params.community_id))
|
? Number(this.props.match.params.community_id)
|
||||||
: None,
|
: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Only fetch the data if coming from another route
|
// Only fetch the data if coming from another route
|
||||||
if (this.isoData.path == this.context.router.route.match.url) {
|
if (this.isoData.path == this.context.router.route.match.url) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
res: Some(this.isoData.routeData[0] as GetModlogResponse),
|
res: this.isoData.routeData[0] as GetModlogResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.isoData.routeData[1]) {
|
let communityRes: GetCommunityResponse | undefined =
|
||||||
// Getting the moderators
|
this.isoData.routeData[1];
|
||||||
let communityRes = Some(
|
|
||||||
this.isoData.routeData[1] as GetCommunityResponse
|
// Getting the moderators
|
||||||
);
|
this.state = {
|
||||||
this.state = {
|
...this.state,
|
||||||
...this.state,
|
communityMods: communityRes?.moderators,
|
||||||
communityMods: communityRes.map(c => c.moderators),
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
this.state = { ...this.state, loading: false };
|
this.state = { ...this.state, loading: false };
|
||||||
} else {
|
} else {
|
||||||
|
@ -161,7 +146,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,22 +295,20 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
switch (i.type_) {
|
switch (i.type_) {
|
||||||
case ModlogActionType.ModRemovePost: {
|
case ModlogActionType.ModRemovePost: {
|
||||||
let mrpv = i.view as ModRemovePostView;
|
let mrpv = i.view as ModRemovePostView;
|
||||||
|
let reason = mrpv.mod_remove_post.reason;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>
|
<span>
|
||||||
{mrpv.mod_remove_post.removed.unwrapOr(false)
|
{mrpv.mod_remove_post.removed ? "Removed " : "Restored "}
|
||||||
? "Removed "
|
|
||||||
: "Restored "}
|
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
|
Post <Link to={`/post/${mrpv.post.id}`}>{mrpv.post.name}</Link>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
{reason && (
|
||||||
{mrpv.mod_remove_post.reason.match({
|
<span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
<div>reason: {reason}</div>
|
||||||
none: <></>,
|
</span>
|
||||||
})}
|
)}
|
||||||
</span>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -333,11 +316,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
let mlpv = i.view as ModLockPostView;
|
let mlpv = i.view as ModLockPostView;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>
|
<span>{mlpv.mod_lock_post.locked ? "Locked " : "Unlocked "}</span>
|
||||||
{mlpv.mod_lock_post.locked.unwrapOr(false)
|
|
||||||
? "Locked "
|
|
||||||
: "Unlocked "}
|
|
||||||
</span>
|
|
||||||
<span>
|
<span>
|
||||||
Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
|
Post <Link to={`/post/${mlpv.post.id}`}>{mlpv.post.name}</Link>
|
||||||
</span>
|
</span>
|
||||||
|
@ -364,12 +343,11 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
}
|
}
|
||||||
case ModlogActionType.ModRemoveComment: {
|
case ModlogActionType.ModRemoveComment: {
|
||||||
let mrc = i.view as ModRemoveCommentView;
|
let mrc = i.view as ModRemoveCommentView;
|
||||||
|
let reason = mrc.mod_remove_comment.reason;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>
|
<span>
|
||||||
{mrc.mod_remove_comment.removed.unwrapOr(false)
|
{mrc.mod_remove_comment.removed ? "Removed " : "Restored "}
|
||||||
? "Removed "
|
|
||||||
: "Restored "}
|
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
Comment{" "}
|
Comment{" "}
|
||||||
|
@ -381,52 +359,47 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
{" "}
|
{" "}
|
||||||
by <PersonListing person={mrc.commenter} />
|
by <PersonListing person={mrc.commenter} />
|
||||||
</span>
|
</span>
|
||||||
<span>
|
{reason && (
|
||||||
{mrc.mod_remove_comment.reason.match({
|
<span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
<div>reason: {reason}</div>
|
||||||
none: <></>,
|
</span>
|
||||||
})}
|
)}
|
||||||
</span>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.ModRemoveCommunity: {
|
case ModlogActionType.ModRemoveCommunity: {
|
||||||
let mrco = i.view as ModRemoveCommunityView;
|
let mrco = i.view as ModRemoveCommunityView;
|
||||||
|
let reason = mrco.mod_remove_community.reason;
|
||||||
|
let expires = mrco.mod_remove_community.expires;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>
|
<span>
|
||||||
{mrco.mod_remove_community.removed.unwrapOr(false)
|
{mrco.mod_remove_community.removed ? "Removed " : "Restored "}
|
||||||
? "Removed "
|
|
||||||
: "Restored "}
|
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
Community <CommunityLink community={mrco.community} />
|
Community <CommunityLink community={mrco.community} />
|
||||||
</span>
|
</span>
|
||||||
<span>
|
{reason && (
|
||||||
{mrco.mod_remove_community.reason.match({
|
<span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
<div>reason: {reason}</div>
|
||||||
none: <></>,
|
</span>
|
||||||
})}
|
)}
|
||||||
</span>
|
{expires && (
|
||||||
<span>
|
<span>
|
||||||
{mrco.mod_remove_community.expires.match({
|
<div>expires: {moment.utc(expires).fromNow()}</div>
|
||||||
some: expires => (
|
</span>
|
||||||
<div>expires: {moment.utc(expires).fromNow()}</div>
|
)}
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.ModBanFromCommunity: {
|
case ModlogActionType.ModBanFromCommunity: {
|
||||||
let mbfc = i.view as ModBanFromCommunityView;
|
let mbfc = i.view as ModBanFromCommunityView;
|
||||||
|
let reason = mbfc.mod_ban_from_community.reason;
|
||||||
|
let expires = mbfc.mod_ban_from_community.expires;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>
|
<span>
|
||||||
{mbfc.mod_ban_from_community.banned.unwrapOr(false)
|
{mbfc.mod_ban_from_community.banned ? "Banned " : "Unbanned "}{" "}
|
||||||
? "Banned "
|
|
||||||
: "Unbanned "}{" "}
|
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<PersonListing person={mbfc.banned_person} />
|
<PersonListing person={mbfc.banned_person} />
|
||||||
|
@ -435,20 +408,16 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
<span>
|
<span>
|
||||||
<CommunityLink community={mbfc.community} />
|
<CommunityLink community={mbfc.community} />
|
||||||
</span>
|
</span>
|
||||||
<span>
|
{reason && (
|
||||||
{mbfc.mod_ban_from_community.reason.match({
|
<span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
<div>reason: {reason}</div>
|
||||||
none: <></>,
|
</span>
|
||||||
})}
|
)}
|
||||||
</span>
|
{expires && (
|
||||||
<span>
|
<span>
|
||||||
{mbfc.mod_ban_from_community.expires.match({
|
<div>expires: {moment.utc(expires).fromNow()}</div>
|
||||||
some: expires => (
|
</span>
|
||||||
<div>expires: {moment.utc(expires).fromNow()}</div>
|
)}
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -457,9 +426,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>
|
<span>
|
||||||
{mac.mod_add_community.removed.unwrapOr(false)
|
{mac.mod_add_community.removed ? "Removed " : "Appointed "}{" "}
|
||||||
? "Removed "
|
|
||||||
: "Appointed "}{" "}
|
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<PersonListing person={mac.modded_person} />
|
<PersonListing person={mac.modded_person} />
|
||||||
|
@ -476,9 +443,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>
|
<span>
|
||||||
{mtc.mod_transfer_community.removed.unwrapOr(false)
|
{mtc.mod_transfer_community.removed ? "Removed " : "Transferred "}{" "}
|
||||||
? "Removed "
|
|
||||||
: "Transferred "}{" "}
|
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
<CommunityLink community={mtc.community} />
|
<CommunityLink community={mtc.community} />
|
||||||
|
@ -492,28 +457,24 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
}
|
}
|
||||||
case ModlogActionType.ModBan: {
|
case ModlogActionType.ModBan: {
|
||||||
let mb = i.view as ModBanView;
|
let mb = i.view as ModBanView;
|
||||||
|
let reason = mb.mod_ban.reason;
|
||||||
|
let expires = mb.mod_ban.expires;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>
|
<span>{mb.mod_ban.banned ? "Banned " : "Unbanned "} </span>
|
||||||
{mb.mod_ban.banned.unwrapOr(false) ? "Banned " : "Unbanned "}{" "}
|
|
||||||
</span>
|
|
||||||
<span>
|
<span>
|
||||||
<PersonListing person={mb.banned_person} />
|
<PersonListing person={mb.banned_person} />
|
||||||
</span>
|
</span>
|
||||||
<span>
|
{reason && (
|
||||||
{mb.mod_ban.reason.match({
|
<span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
<div>reason: {reason}</div>
|
||||||
none: <></>,
|
</span>
|
||||||
})}
|
)}
|
||||||
</span>
|
{expires && (
|
||||||
<span>
|
<span>
|
||||||
{mb.mod_ban.expires.match({
|
<div>expires: {moment.utc(expires).fromNow()}</div>
|
||||||
some: expires => (
|
</span>
|
||||||
<div>expires: {moment.utc(expires).fromNow()}</div>
|
)}
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</span>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -521,9 +482,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
let ma = i.view as ModAddView;
|
let ma = i.view as ModAddView;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>
|
<span>{ma.mod_add.removed ? "Removed " : "Appointed "} </span>
|
||||||
{ma.mod_add.removed.unwrapOr(false) ? "Removed " : "Appointed "}{" "}
|
|
||||||
</span>
|
|
||||||
<span>
|
<span>
|
||||||
<PersonListing person={ma.modded_person} />
|
<PersonListing person={ma.modded_person} />
|
||||||
</span>
|
</span>
|
||||||
|
@ -533,61 +492,61 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
}
|
}
|
||||||
case ModlogActionType.AdminPurgePerson: {
|
case ModlogActionType.AdminPurgePerson: {
|
||||||
let ap = i.view as AdminPurgePersonView;
|
let ap = i.view as AdminPurgePersonView;
|
||||||
|
let reason = ap.admin_purge_person.reason;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>Purged a Person</span>
|
<span>Purged a Person</span>
|
||||||
<span>
|
{reason && (
|
||||||
{ap.admin_purge_person.reason.match({
|
<span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
<div>reason: {reason}</div>
|
||||||
none: <></>,
|
</span>
|
||||||
})}
|
)}
|
||||||
</span>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.AdminPurgeCommunity: {
|
case ModlogActionType.AdminPurgeCommunity: {
|
||||||
let ap = i.view as AdminPurgeCommunityView;
|
let ap = i.view as AdminPurgeCommunityView;
|
||||||
|
let reason = ap.admin_purge_community.reason;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>Purged a Community</span>
|
<span>Purged a Community</span>
|
||||||
<span>
|
{reason && (
|
||||||
{ap.admin_purge_community.reason.match({
|
<span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
<div>reason: {reason}</div>
|
||||||
none: <></>,
|
</span>
|
||||||
})}
|
)}
|
||||||
</span>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.AdminPurgePost: {
|
case ModlogActionType.AdminPurgePost: {
|
||||||
let ap = i.view as AdminPurgePostView;
|
let ap = i.view as AdminPurgePostView;
|
||||||
|
let reason = ap.admin_purge_post.reason;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>Purged a Post from from </span>
|
<span>Purged a Post from from </span>
|
||||||
<CommunityLink community={ap.community} />
|
<CommunityLink community={ap.community} />
|
||||||
<span>
|
{reason && (
|
||||||
{ap.admin_purge_post.reason.match({
|
<span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
<div>reason: {reason}</div>
|
||||||
none: <></>,
|
</span>
|
||||||
})}
|
)}
|
||||||
</span>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
case ModlogActionType.AdminPurgeComment: {
|
case ModlogActionType.AdminPurgeComment: {
|
||||||
let ap = i.view as AdminPurgeCommentView;
|
let ap = i.view as AdminPurgeCommentView;
|
||||||
|
let reason = ap.admin_purge_comment.reason;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>
|
<span>
|
||||||
Purged a Comment from{" "}
|
Purged a Comment from{" "}
|
||||||
<Link to={`/post/${ap.post.id}`}>{ap.post.name}</Link>
|
<Link to={`/post/${ap.post.id}`}>{ap.post.name}</Link>
|
||||||
</span>
|
</span>
|
||||||
<span>
|
{reason && (
|
||||||
{ap.admin_purge_comment.reason.match({
|
<span>
|
||||||
some: reason => <div>reason: {reason}</div>,
|
<div>reason: {reason}</div>
|
||||||
none: <></>,
|
</span>
|
||||||
})}
|
)}
|
||||||
</span>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -597,18 +556,19 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
combined() {
|
combined() {
|
||||||
let combined = this.state.res.map(this.buildCombined).unwrapOr([]);
|
let res = this.state.res;
|
||||||
|
let combined = res ? this.buildCombined(res) : [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tbody>
|
<tbody>
|
||||||
{combined.map(i => (
|
{combined.map(i => (
|
||||||
<tr key={i.id}>
|
<tr key={i.id}>
|
||||||
<td>
|
<td>
|
||||||
<MomentTime published={i.when_} updated={None} />
|
<MomentTime published={i.when_} />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{this.amAdminOrMod ? (
|
{this.amAdminOrMod && i.moderator ? (
|
||||||
<PersonListing person={i.moderator.unwrap()} />
|
<PersonListing person={i.moderator} />
|
||||||
) : (
|
) : (
|
||||||
<div>{this.modOrAdminText(i.moderator)}</div>
|
<div>{this.modOrAdminText(i.moderator)}</div>
|
||||||
)}
|
)}
|
||||||
|
@ -624,14 +584,12 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
return amAdmin() || amMod(this.state.communityMods);
|
return amAdmin() || amMod(this.state.communityMods);
|
||||||
}
|
}
|
||||||
|
|
||||||
modOrAdminText(person: Option<PersonSafe>): string {
|
modOrAdminText(person?: PersonSafe): string {
|
||||||
return person.match({
|
return person
|
||||||
some: res =>
|
? this.isoData.site_res.admins.map(a => a.person.id).includes(person.id)
|
||||||
this.isoData.site_res.admins.map(a => a.person.id).includes(res.id)
|
? i18n.t("admin")
|
||||||
? i18n.t("admin")
|
: i18n.t("mod")
|
||||||
: i18n.t("mod"),
|
: i18n.t("mod");
|
||||||
none: i18n.t("mod"),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
|
@ -639,13 +597,12 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let communityName = this.state.communityName;
|
||||||
return (
|
return (
|
||||||
<div className="container-lg">
|
<div className="container-lg">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<h5>
|
<h5>
|
||||||
|
@ -654,14 +611,11 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<h5>
|
<h5>
|
||||||
{this.state.communityName.match({
|
{communityName && (
|
||||||
some: name => (
|
<Link className="text-body" to={`/c/${communityName}`}>
|
||||||
<Link className="text-body" to={`/c/${name}`}>
|
/c/{communityName}{" "}
|
||||||
/c/{name}{" "}
|
</Link>
|
||||||
</Link>
|
)}
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
<span>{i18n.t("modlog")}</span>
|
<span>{i18n.t("modlog")}</span>
|
||||||
</h5>
|
</h5>
|
||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
|
@ -714,7 +668,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
<select
|
<select
|
||||||
id="filter-mod"
|
id="filter-mod"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
value={toUndefined(this.state.filter_mod)}
|
value={this.state.filter_mod}
|
||||||
>
|
>
|
||||||
<option>{i18n.t("filter_by_mod")}</option>
|
<option>{i18n.t("filter_by_mod")}</option>
|
||||||
</select>
|
</select>
|
||||||
|
@ -724,7 +678,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
<select
|
<select
|
||||||
id="filter-user"
|
id="filter-user"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
value={toUndefined(this.state.filter_user)}
|
value={this.state.filter_user}
|
||||||
>
|
>
|
||||||
<option>{i18n.t("filter_by_user")}</option>
|
<option>{i18n.t("filter_by_user")}</option>
|
||||||
</select>
|
</select>
|
||||||
|
@ -763,28 +717,26 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
refetch() {
|
refetch() {
|
||||||
let modlogForm = new GetModlog({
|
let auth = myAuth(false);
|
||||||
|
let modlogForm: GetModlog = {
|
||||||
community_id: this.state.communityId,
|
community_id: this.state.communityId,
|
||||||
page: Some(this.state.page),
|
page: this.state.page,
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
auth: auth(false).ok(),
|
|
||||||
type_: this.state.filter_action,
|
type_: this.state.filter_action,
|
||||||
other_person_id: this.state.filter_user,
|
other_person_id: this.state.filter_user,
|
||||||
mod_person_id: this.state.filter_mod,
|
mod_person_id: this.state.filter_mod,
|
||||||
});
|
auth,
|
||||||
|
};
|
||||||
WebSocketService.Instance.send(wsClient.getModlog(modlogForm));
|
WebSocketService.Instance.send(wsClient.getModlog(modlogForm));
|
||||||
|
|
||||||
this.state.communityId.match({
|
let communityId = this.state.communityId;
|
||||||
some: id => {
|
if (communityId) {
|
||||||
let communityForm = new GetCommunity({
|
let communityForm: GetCommunity = {
|
||||||
id: Some(id),
|
id: communityId,
|
||||||
name: None,
|
auth,
|
||||||
auth: auth(false).ok(),
|
};
|
||||||
});
|
WebSocketService.Instance.send(wsClient.getCommunity(communityForm));
|
||||||
WebSocketService.Instance.send(wsClient.getCommunity(communityForm));
|
}
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setupUserFilter() {
|
setupUserFilter() {
|
||||||
|
@ -795,7 +747,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
this.userChoices.passedElement.element.addEventListener(
|
this.userChoices.passedElement.element.addEventListener(
|
||||||
"choice",
|
"choice",
|
||||||
(e: any) => {
|
(e: any) => {
|
||||||
this.setState({ filter_user: Some(Number(e.detail.choice.value)) });
|
this.setState({ filter_user: Number(e.detail.choice.value) });
|
||||||
this.refetch();
|
this.refetch();
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
|
@ -834,7 +786,7 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
this.modChoices.passedElement.element.addEventListener(
|
this.modChoices.passedElement.element.addEventListener(
|
||||||
"choice",
|
"choice",
|
||||||
(e: any) => {
|
(e: any) => {
|
||||||
this.setState({ filter_mod: Some(Number(e.detail.choice.value)) });
|
this.setState({ filter_mod: Number(e.detail.choice.value) });
|
||||||
this.refetch();
|
this.refetch();
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
|
@ -867,27 +819,25 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
|
|
||||||
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
||||||
let pathSplit = req.path.split("/");
|
let pathSplit = req.path.split("/");
|
||||||
let communityId = Some(pathSplit[3]).map(Number);
|
let communityId = pathSplit[3] ? Number(pathSplit[3]) : undefined;
|
||||||
|
let auth = req.auth;
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
let modlogForm = new GetModlog({
|
let modlogForm: GetModlog = {
|
||||||
page: Some(1),
|
page: 1,
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
community_id: communityId,
|
community_id: communityId,
|
||||||
mod_person_id: None,
|
|
||||||
auth: req.auth,
|
|
||||||
type_: ModlogActionType.All,
|
type_: ModlogActionType.All,
|
||||||
other_person_id: None,
|
auth,
|
||||||
});
|
};
|
||||||
|
|
||||||
promises.push(req.client.getModlog(modlogForm));
|
promises.push(req.client.getModlog(modlogForm));
|
||||||
|
|
||||||
if (communityId.isSome()) {
|
if (communityId) {
|
||||||
let communityForm = new GetCommunity({
|
let communityForm: GetCommunity = {
|
||||||
id: communityId,
|
id: communityId,
|
||||||
name: None,
|
|
||||||
auth: req.auth,
|
auth: req.auth,
|
||||||
});
|
};
|
||||||
promises.push(req.client.getCommunity(communityForm));
|
promises.push(req.client.getCommunity(communityForm));
|
||||||
} else {
|
} else {
|
||||||
promises.push(Promise.resolve());
|
promises.push(Promise.resolve());
|
||||||
|
@ -902,16 +852,16 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.GetModlog) {
|
} else if (op == UserOperation.GetModlog) {
|
||||||
let data = wsJsonToRes<GetModlogResponse>(msg, GetModlogResponse);
|
let data = wsJsonToRes<GetModlogResponse>(msg);
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.setState({ res: Some(data), loading: false });
|
this.setState({ res: data, loading: false });
|
||||||
this.setupUserFilter();
|
this.setupUserFilter();
|
||||||
this.setupModFilter();
|
this.setupModFilter();
|
||||||
} else if (op == UserOperation.GetCommunity) {
|
} else if (op == UserOperation.GetCommunity) {
|
||||||
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
let data = wsJsonToRes<GetCommunityResponse>(msg);
|
||||||
this.setState({
|
this.setState({
|
||||||
communityMods: Some(data.moderators),
|
communityMods: data.moderators,
|
||||||
communityName: Some(data.community_view.community.name),
|
communityName: data.community_view.community.name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Some } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
BlockPersonResponse,
|
BlockPersonResponse,
|
||||||
|
@ -30,13 +29,13 @@ import { i18n } from "../../i18next";
|
||||||
import { CommentViewType, InitialFetchRequest } from "../../interfaces";
|
import { CommentViewType, InitialFetchRequest } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
createCommentLikeRes,
|
createCommentLikeRes,
|
||||||
editCommentRes,
|
editCommentRes,
|
||||||
enableDownvotes,
|
enableDownvotes,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
|
myAuth,
|
||||||
relTags,
|
relTags,
|
||||||
saveCommentRes,
|
saveCommentRes,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
|
@ -91,14 +90,9 @@ interface InboxState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Inbox extends Component<any, InboxState> {
|
export class Inbox extends Component<any, InboxState> {
|
||||||
private isoData = setIsoData(
|
private isoData = setIsoData(this.context);
|
||||||
this.context,
|
private subscription?: Subscription;
|
||||||
GetRepliesResponse,
|
state: InboxState = {
|
||||||
GetPersonMentionsResponse,
|
|
||||||
PrivateMessagesResponse
|
|
||||||
);
|
|
||||||
private subscription: Subscription;
|
|
||||||
private emptyState: InboxState = {
|
|
||||||
unreadOrAll: UnreadOrAll.Unread,
|
unreadOrAll: UnreadOrAll.Unread,
|
||||||
messageType: MessageType.All,
|
messageType: MessageType.All,
|
||||||
replies: [],
|
replies: [],
|
||||||
|
@ -114,11 +108,10 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
this.handleSortChange = this.handleSortChange.bind(this);
|
this.handleSortChange = this.handleSortChange.bind(this);
|
||||||
this.handlePageChange = this.handlePageChange.bind(this);
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
|
|
||||||
if (UserService.Instance.myUserInfo.isNone() && isBrowser()) {
|
if (!UserService.Instance.myUserInfo && isBrowser()) {
|
||||||
toast(i18n.t("not_logged_in"), "danger");
|
toast(i18n.t("not_logged_in"), "danger");
|
||||||
this.context.router.history.push(`/login`);
|
this.context.router.history.push(`/login`);
|
||||||
}
|
}
|
||||||
|
@ -148,24 +141,22 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return UserService.Instance.myUserInfo.match({
|
let mui = UserService.Instance.myUserInfo;
|
||||||
some: mui =>
|
return mui
|
||||||
`@${mui.local_user_view.person.name} ${i18n.t("inbox")} - ${
|
? `@${mui.local_user_view.person.name} ${i18n.t("inbox")} - ${
|
||||||
this.state.siteRes.site_view.site.name
|
this.state.siteRes.site_view.site.name
|
||||||
}`,
|
}`
|
||||||
none: "",
|
: "";
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let inboxRss = auth()
|
let auth = myAuth();
|
||||||
.ok()
|
let inboxRss = auth ? `/feeds/inbox/${auth}.xml` : undefined;
|
||||||
.map(a => `/feeds/inbox/${a}.xml`);
|
|
||||||
return (
|
return (
|
||||||
<div className="container-lg">
|
<div className="container-lg">
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
|
@ -178,26 +169,21 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
<h5 className="mb-2">
|
<h5 className="mb-2">
|
||||||
{i18n.t("inbox")}
|
{i18n.t("inbox")}
|
||||||
{inboxRss.match({
|
{inboxRss && (
|
||||||
some: rss => (
|
<small>
|
||||||
<small>
|
<a href={inboxRss} title="RSS" rel={relTags}>
|
||||||
<a href={rss} title="RSS" rel={relTags}>
|
<Icon icon="rss" classes="ml-2 text-muted small" />
|
||||||
<Icon icon="rss" classes="ml-2 text-muted small" />
|
</a>
|
||||||
</a>
|
<link
|
||||||
<link
|
rel="alternate"
|
||||||
rel="alternate"
|
type="application/atom+xml"
|
||||||
type="application/atom+xml"
|
href={inboxRss}
|
||||||
href={rss}
|
/>
|
||||||
/>
|
</small>
|
||||||
</small>
|
)}
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</h5>
|
</h5>
|
||||||
{this.state.replies.length +
|
{this.state.replies.length +
|
||||||
this.state.mentions.length +
|
this.state.mentions.length +
|
||||||
|
@ -387,9 +373,6 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
{ comment_view: i.view as CommentView, children: [], depth: 0 },
|
{ comment_view: i.view as CommentView, children: [], depth: 0 },
|
||||||
]}
|
]}
|
||||||
viewType={CommentViewType.Flat}
|
viewType={CommentViewType.Flat}
|
||||||
moderators={None}
|
|
||||||
admins={None}
|
|
||||||
maxCommentsShown={None}
|
|
||||||
noIndent
|
noIndent
|
||||||
markable
|
markable
|
||||||
showCommunity
|
showCommunity
|
||||||
|
@ -411,9 +394,6 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
viewType={CommentViewType.Flat}
|
viewType={CommentViewType.Flat}
|
||||||
moderators={None}
|
|
||||||
admins={None}
|
|
||||||
maxCommentsShown={None}
|
|
||||||
noIndent
|
noIndent
|
||||||
markable
|
markable
|
||||||
showCommunity
|
showCommunity
|
||||||
|
@ -445,9 +425,6 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
<CommentNodes
|
<CommentNodes
|
||||||
nodes={commentsToFlatNodes(this.state.replies)}
|
nodes={commentsToFlatNodes(this.state.replies)}
|
||||||
viewType={CommentViewType.Flat}
|
viewType={CommentViewType.Flat}
|
||||||
moderators={None}
|
|
||||||
admins={None}
|
|
||||||
maxCommentsShown={None}
|
|
||||||
noIndent
|
noIndent
|
||||||
markable
|
markable
|
||||||
showCommunity
|
showCommunity
|
||||||
|
@ -468,9 +445,6 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
key={umv.person_mention.id}
|
key={umv.person_mention.id}
|
||||||
nodes={[{ comment_view: umv, children: [], depth: 0 }]}
|
nodes={[{ comment_view: umv, children: [], depth: 0 }]}
|
||||||
viewType={CommentViewType.Flat}
|
viewType={CommentViewType.Flat}
|
||||||
moderators={None}
|
|
||||||
admins={None}
|
|
||||||
maxCommentsShown={None}
|
|
||||||
noIndent
|
noIndent
|
||||||
markable
|
markable
|
||||||
showCommunity
|
showCommunity
|
||||||
|
@ -515,73 +489,79 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
let sort = Some(CommentSortType.New);
|
let sort = CommentSortType.New;
|
||||||
|
let auth = req.auth;
|
||||||
|
|
||||||
// It can be /u/me, or /username/1
|
if (auth) {
|
||||||
let repliesForm = new GetReplies({
|
// It can be /u/me, or /username/1
|
||||||
sort,
|
let repliesForm: GetReplies = {
|
||||||
unread_only: Some(true),
|
sort,
|
||||||
page: Some(1),
|
unread_only: true,
|
||||||
limit: Some(fetchLimit),
|
page: 1,
|
||||||
auth: req.auth.unwrap(),
|
limit: fetchLimit,
|
||||||
});
|
auth,
|
||||||
promises.push(req.client.getReplies(repliesForm));
|
};
|
||||||
|
promises.push(req.client.getReplies(repliesForm));
|
||||||
|
|
||||||
let personMentionsForm = new GetPersonMentions({
|
let personMentionsForm: GetPersonMentions = {
|
||||||
sort,
|
sort,
|
||||||
unread_only: Some(true),
|
unread_only: true,
|
||||||
page: Some(1),
|
page: 1,
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
auth: req.auth.unwrap(),
|
auth,
|
||||||
});
|
};
|
||||||
promises.push(req.client.getPersonMentions(personMentionsForm));
|
promises.push(req.client.getPersonMentions(personMentionsForm));
|
||||||
|
|
||||||
let privateMessagesForm = new GetPrivateMessages({
|
let privateMessagesForm: GetPrivateMessages = {
|
||||||
unread_only: Some(true),
|
unread_only: true,
|
||||||
page: Some(1),
|
page: 1,
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
auth: req.auth.unwrap(),
|
auth,
|
||||||
});
|
};
|
||||||
promises.push(req.client.getPrivateMessages(privateMessagesForm));
|
promises.push(req.client.getPrivateMessages(privateMessagesForm));
|
||||||
|
}
|
||||||
|
|
||||||
return promises;
|
return promises;
|
||||||
}
|
}
|
||||||
|
|
||||||
refetch() {
|
refetch() {
|
||||||
let sort = Some(this.state.sort);
|
let sort = this.state.sort;
|
||||||
let unread_only = Some(this.state.unreadOrAll == UnreadOrAll.Unread);
|
let unread_only = this.state.unreadOrAll == UnreadOrAll.Unread;
|
||||||
let page = Some(this.state.page);
|
let page = this.state.page;
|
||||||
let limit = Some(fetchLimit);
|
let limit = fetchLimit;
|
||||||
|
let auth = myAuth();
|
||||||
|
|
||||||
let repliesForm = new GetReplies({
|
if (auth) {
|
||||||
sort,
|
let repliesForm: GetReplies = {
|
||||||
unread_only,
|
sort,
|
||||||
page,
|
unread_only,
|
||||||
limit,
|
page,
|
||||||
auth: auth().unwrap(),
|
limit,
|
||||||
});
|
auth,
|
||||||
WebSocketService.Instance.send(wsClient.getReplies(repliesForm));
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.getReplies(repliesForm));
|
||||||
|
|
||||||
let personMentionsForm = new GetPersonMentions({
|
let personMentionsForm: GetPersonMentions = {
|
||||||
sort,
|
sort,
|
||||||
unread_only,
|
unread_only,
|
||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
auth: auth().unwrap(),
|
auth,
|
||||||
});
|
};
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.getPersonMentions(personMentionsForm)
|
wsClient.getPersonMentions(personMentionsForm)
|
||||||
);
|
);
|
||||||
|
|
||||||
let privateMessagesForm = new GetPrivateMessages({
|
let privateMessagesForm: GetPrivateMessages = {
|
||||||
unread_only,
|
unread_only,
|
||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
auth: auth().unwrap(),
|
auth,
|
||||||
});
|
};
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.getPrivateMessages(privateMessagesForm)
|
wsClient.getPrivateMessages(privateMessagesForm)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSortChange(val: CommentSortType) {
|
handleSortChange(val: CommentSortType) {
|
||||||
|
@ -590,16 +570,19 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
markAllAsRead(i: Inbox) {
|
markAllAsRead(i: Inbox) {
|
||||||
WebSocketService.Instance.send(
|
let auth = myAuth();
|
||||||
wsClient.markAllAsRead({
|
if (auth) {
|
||||||
auth: auth().unwrap(),
|
WebSocketService.Instance.send(
|
||||||
})
|
wsClient.markAllAsRead({
|
||||||
);
|
auth,
|
||||||
i.setState({ replies: [], mentions: [], messages: [] });
|
})
|
||||||
i.setState({ combined: i.buildCombined() });
|
);
|
||||||
UserService.Instance.unreadInboxCountSub.next(0);
|
i.setState({ replies: [], mentions: [], messages: [] });
|
||||||
window.scrollTo(0, 0);
|
i.setState({ combined: i.buildCombined() });
|
||||||
i.setState(i.state);
|
UserService.Instance.unreadInboxCountSub.next(0);
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
i.setState(i.state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sendUnreadCount(read: boolean) {
|
sendUnreadCount(read: boolean) {
|
||||||
|
@ -620,73 +603,62 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
} else if (msg.reconnect) {
|
} else if (msg.reconnect) {
|
||||||
this.refetch();
|
this.refetch();
|
||||||
} else if (op == UserOperation.GetReplies) {
|
} else if (op == UserOperation.GetReplies) {
|
||||||
let data = wsJsonToRes<GetRepliesResponse>(msg, GetRepliesResponse);
|
let data = wsJsonToRes<GetRepliesResponse>(msg);
|
||||||
this.setState({ replies: data.replies });
|
this.setState({ replies: data.replies });
|
||||||
this.setState({ combined: this.buildCombined(), loading: false });
|
this.setState({ combined: this.buildCombined(), loading: false });
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.GetPersonMentions) {
|
} else if (op == UserOperation.GetPersonMentions) {
|
||||||
let data = wsJsonToRes<GetPersonMentionsResponse>(
|
let data = wsJsonToRes<GetPersonMentionsResponse>(msg);
|
||||||
msg,
|
|
||||||
GetPersonMentionsResponse
|
|
||||||
);
|
|
||||||
this.setState({ mentions: data.mentions });
|
this.setState({ mentions: data.mentions });
|
||||||
this.setState({ combined: this.buildCombined() });
|
this.setState({ combined: this.buildCombined() });
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.GetPrivateMessages) {
|
} else if (op == UserOperation.GetPrivateMessages) {
|
||||||
let data = wsJsonToRes<PrivateMessagesResponse>(
|
let data = wsJsonToRes<PrivateMessagesResponse>(msg);
|
||||||
msg,
|
|
||||||
PrivateMessagesResponse
|
|
||||||
);
|
|
||||||
this.setState({ messages: data.private_messages });
|
this.setState({ messages: data.private_messages });
|
||||||
this.setState({ combined: this.buildCombined() });
|
this.setState({ combined: this.buildCombined() });
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.EditPrivateMessage) {
|
} else if (op == UserOperation.EditPrivateMessage) {
|
||||||
let data = wsJsonToRes<PrivateMessageResponse>(
|
let data = wsJsonToRes<PrivateMessageResponse>(msg);
|
||||||
msg,
|
let found = this.state.messages.find(
|
||||||
PrivateMessageResponse
|
|
||||||
);
|
|
||||||
let found: PrivateMessageView = this.state.messages.find(
|
|
||||||
m =>
|
m =>
|
||||||
m.private_message.id === data.private_message_view.private_message.id
|
m.private_message.id === data.private_message_view.private_message.id
|
||||||
);
|
);
|
||||||
if (found) {
|
if (found) {
|
||||||
let combinedView = this.state.combined.find(
|
let combinedView = this.state.combined.find(
|
||||||
i => i.id == data.private_message_view.private_message.id
|
i => i.id == data.private_message_view.private_message.id
|
||||||
).view as PrivateMessageView;
|
)?.view as PrivateMessageView | undefined;
|
||||||
found.private_message.content = combinedView.private_message.content =
|
if (combinedView) {
|
||||||
data.private_message_view.private_message.content;
|
found.private_message.content = combinedView.private_message.content =
|
||||||
found.private_message.updated = combinedView.private_message.updated =
|
data.private_message_view.private_message.content;
|
||||||
data.private_message_view.private_message.updated;
|
found.private_message.updated = combinedView.private_message.updated =
|
||||||
|
data.private_message_view.private_message.updated;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.DeletePrivateMessage) {
|
} else if (op == UserOperation.DeletePrivateMessage) {
|
||||||
let data = wsJsonToRes<PrivateMessageResponse>(
|
let data = wsJsonToRes<PrivateMessageResponse>(msg);
|
||||||
msg,
|
let found = this.state.messages.find(
|
||||||
PrivateMessageResponse
|
|
||||||
);
|
|
||||||
let found: PrivateMessageView = this.state.messages.find(
|
|
||||||
m =>
|
m =>
|
||||||
m.private_message.id === data.private_message_view.private_message.id
|
m.private_message.id === data.private_message_view.private_message.id
|
||||||
);
|
);
|
||||||
if (found) {
|
if (found) {
|
||||||
let combinedView = this.state.combined.find(
|
let combinedView = this.state.combined.find(
|
||||||
i => i.id == data.private_message_view.private_message.id
|
i => i.id == data.private_message_view.private_message.id
|
||||||
).view as PrivateMessageView;
|
)?.view as PrivateMessageView | undefined;
|
||||||
found.private_message.deleted = combinedView.private_message.deleted =
|
if (combinedView) {
|
||||||
data.private_message_view.private_message.deleted;
|
found.private_message.deleted = combinedView.private_message.deleted =
|
||||||
found.private_message.updated = combinedView.private_message.updated =
|
data.private_message_view.private_message.deleted;
|
||||||
data.private_message_view.private_message.updated;
|
found.private_message.updated = combinedView.private_message.updated =
|
||||||
|
data.private_message_view.private_message.updated;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.MarkPrivateMessageAsRead) {
|
} else if (op == UserOperation.MarkPrivateMessageAsRead) {
|
||||||
let data = wsJsonToRes<PrivateMessageResponse>(
|
let data = wsJsonToRes<PrivateMessageResponse>(msg);
|
||||||
msg,
|
let found = this.state.messages.find(
|
||||||
PrivateMessageResponse
|
|
||||||
);
|
|
||||||
let found: PrivateMessageView = this.state.messages.find(
|
|
||||||
m =>
|
m =>
|
||||||
m.private_message.id === data.private_message_view.private_message.id
|
m.private_message.id === data.private_message_view.private_message.id
|
||||||
);
|
);
|
||||||
|
@ -696,30 +668,32 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
i =>
|
i =>
|
||||||
i.id == data.private_message_view.private_message.id &&
|
i.id == data.private_message_view.private_message.id &&
|
||||||
i.type_ == ReplyEnum.Message
|
i.type_ == ReplyEnum.Message
|
||||||
).view as PrivateMessageView;
|
)?.view as PrivateMessageView | undefined;
|
||||||
found.private_message.updated = combinedView.private_message.updated =
|
if (combinedView) {
|
||||||
data.private_message_view.private_message.updated;
|
found.private_message.updated = combinedView.private_message.updated =
|
||||||
|
data.private_message_view.private_message.updated;
|
||||||
|
|
||||||
// If youre in the unread view, just remove it from the list
|
// If youre in the unread view, just remove it from the list
|
||||||
if (
|
if (
|
||||||
this.state.unreadOrAll == UnreadOrAll.Unread &&
|
this.state.unreadOrAll == UnreadOrAll.Unread &&
|
||||||
data.private_message_view.private_message.read
|
data.private_message_view.private_message.read
|
||||||
) {
|
) {
|
||||||
this.setState({
|
this.setState({
|
||||||
messages: this.state.messages.filter(
|
messages: this.state.messages.filter(
|
||||||
r =>
|
r =>
|
||||||
r.private_message.id !==
|
r.private_message.id !==
|
||||||
data.private_message_view.private_message.id
|
data.private_message_view.private_message.id
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
this.setState({
|
this.setState({
|
||||||
combined: this.state.combined.filter(
|
combined: this.state.combined.filter(
|
||||||
r => r.id !== data.private_message_view.private_message.id
|
r => r.id !== data.private_message_view.private_message.id
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
found.private_message.read = combinedView.private_message.read =
|
found.private_message.read = combinedView.private_message.read =
|
||||||
data.private_message_view.private_message.read;
|
data.private_message_view.private_message.read;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.sendUnreadCount(data.private_message_view.private_message.read);
|
this.sendUnreadCount(data.private_message_view.private_message.read);
|
||||||
|
@ -731,11 +705,11 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
op == UserOperation.DeleteComment ||
|
op == UserOperation.DeleteComment ||
|
||||||
op == UserOperation.RemoveComment
|
op == UserOperation.RemoveComment
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
editCommentRes(data.comment_view, this.state.replies);
|
editCommentRes(data.comment_view, this.state.replies);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.MarkCommentReplyAsRead) {
|
} else if (op == UserOperation.MarkCommentReplyAsRead) {
|
||||||
let data = wsJsonToRes<CommentReplyResponse>(msg, CommentReplyResponse);
|
let data = wsJsonToRes<CommentReplyResponse>(msg);
|
||||||
|
|
||||||
let found = this.state.replies.find(
|
let found = this.state.replies.find(
|
||||||
c => c.comment_reply.id == data.comment_reply_view.comment_reply.id
|
c => c.comment_reply.id == data.comment_reply_view.comment_reply.id
|
||||||
|
@ -746,47 +720,50 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
i =>
|
i =>
|
||||||
i.id == data.comment_reply_view.comment_reply.id &&
|
i.id == data.comment_reply_view.comment_reply.id &&
|
||||||
i.type_ == ReplyEnum.Reply
|
i.type_ == ReplyEnum.Reply
|
||||||
).view as CommentReplyView;
|
)?.view as CommentReplyView | undefined;
|
||||||
found.comment.content = combinedView.comment.content =
|
if (combinedView) {
|
||||||
data.comment_reply_view.comment.content;
|
found.comment.content = combinedView.comment.content =
|
||||||
found.comment.updated = combinedView.comment.updated =
|
data.comment_reply_view.comment.content;
|
||||||
data.comment_reply_view.comment.updated;
|
found.comment.updated = combinedView.comment.updated =
|
||||||
found.comment.removed = combinedView.comment.removed =
|
data.comment_reply_view.comment.updated;
|
||||||
data.comment_reply_view.comment.removed;
|
found.comment.removed = combinedView.comment.removed =
|
||||||
found.comment.deleted = combinedView.comment.deleted =
|
data.comment_reply_view.comment.removed;
|
||||||
data.comment_reply_view.comment.deleted;
|
found.comment.deleted = combinedView.comment.deleted =
|
||||||
found.counts.upvotes = combinedView.counts.upvotes =
|
data.comment_reply_view.comment.deleted;
|
||||||
data.comment_reply_view.counts.upvotes;
|
found.counts.upvotes = combinedView.counts.upvotes =
|
||||||
found.counts.downvotes = combinedView.counts.downvotes =
|
data.comment_reply_view.counts.upvotes;
|
||||||
data.comment_reply_view.counts.downvotes;
|
found.counts.downvotes = combinedView.counts.downvotes =
|
||||||
found.counts.score = combinedView.counts.score =
|
data.comment_reply_view.counts.downvotes;
|
||||||
data.comment_reply_view.counts.score;
|
found.counts.score = combinedView.counts.score =
|
||||||
|
data.comment_reply_view.counts.score;
|
||||||
|
|
||||||
// If youre in the unread view, just remove it from the list
|
// If youre in the unread view, just remove it from the list
|
||||||
if (
|
if (
|
||||||
this.state.unreadOrAll == UnreadOrAll.Unread &&
|
this.state.unreadOrAll == UnreadOrAll.Unread &&
|
||||||
data.comment_reply_view.comment_reply.read
|
data.comment_reply_view.comment_reply.read
|
||||||
) {
|
) {
|
||||||
this.setState({
|
this.setState({
|
||||||
replies: this.state.replies.filter(
|
replies: this.state.replies.filter(
|
||||||
r =>
|
r =>
|
||||||
r.comment_reply.id !== data.comment_reply_view.comment_reply.id
|
r.comment_reply.id !==
|
||||||
),
|
data.comment_reply_view.comment_reply.id
|
||||||
});
|
),
|
||||||
this.setState({
|
});
|
||||||
combined: this.state.combined.filter(
|
this.setState({
|
||||||
r => r.id !== data.comment_reply_view.comment_reply.id
|
combined: this.state.combined.filter(
|
||||||
),
|
r => r.id !== data.comment_reply_view.comment_reply.id
|
||||||
});
|
),
|
||||||
} else {
|
});
|
||||||
found.comment_reply.read = combinedView.comment_reply.read =
|
} else {
|
||||||
data.comment_reply_view.comment_reply.read;
|
found.comment_reply.read = combinedView.comment_reply.read =
|
||||||
|
data.comment_reply_view.comment_reply.read;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.sendUnreadCount(data.comment_reply_view.comment_reply.read);
|
this.sendUnreadCount(data.comment_reply_view.comment_reply.read);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.MarkPersonMentionAsRead) {
|
} else if (op == UserOperation.MarkPersonMentionAsRead) {
|
||||||
let data = wsJsonToRes<PersonMentionResponse>(msg, PersonMentionResponse);
|
let data = wsJsonToRes<PersonMentionResponse>(msg);
|
||||||
|
|
||||||
// TODO this might not be correct, it might need to use the comment id
|
// TODO this might not be correct, it might need to use the comment id
|
||||||
let found = this.state.mentions.find(
|
let found = this.state.mentions.find(
|
||||||
|
@ -798,94 +775,85 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
i =>
|
i =>
|
||||||
i.id == data.person_mention_view.person_mention.id &&
|
i.id == data.person_mention_view.person_mention.id &&
|
||||||
i.type_ == ReplyEnum.Mention
|
i.type_ == ReplyEnum.Mention
|
||||||
).view as PersonMentionView;
|
)?.view as PersonMentionView | undefined;
|
||||||
found.comment.content = combinedView.comment.content =
|
if (combinedView) {
|
||||||
data.person_mention_view.comment.content;
|
found.comment.content = combinedView.comment.content =
|
||||||
found.comment.updated = combinedView.comment.updated =
|
data.person_mention_view.comment.content;
|
||||||
data.person_mention_view.comment.updated;
|
found.comment.updated = combinedView.comment.updated =
|
||||||
found.comment.removed = combinedView.comment.removed =
|
data.person_mention_view.comment.updated;
|
||||||
data.person_mention_view.comment.removed;
|
found.comment.removed = combinedView.comment.removed =
|
||||||
found.comment.deleted = combinedView.comment.deleted =
|
data.person_mention_view.comment.removed;
|
||||||
data.person_mention_view.comment.deleted;
|
found.comment.deleted = combinedView.comment.deleted =
|
||||||
found.counts.upvotes = combinedView.counts.upvotes =
|
data.person_mention_view.comment.deleted;
|
||||||
data.person_mention_view.counts.upvotes;
|
found.counts.upvotes = combinedView.counts.upvotes =
|
||||||
found.counts.downvotes = combinedView.counts.downvotes =
|
data.person_mention_view.counts.upvotes;
|
||||||
data.person_mention_view.counts.downvotes;
|
found.counts.downvotes = combinedView.counts.downvotes =
|
||||||
found.counts.score = combinedView.counts.score =
|
data.person_mention_view.counts.downvotes;
|
||||||
data.person_mention_view.counts.score;
|
found.counts.score = combinedView.counts.score =
|
||||||
|
data.person_mention_view.counts.score;
|
||||||
|
|
||||||
// If youre in the unread view, just remove it from the list
|
// If youre in the unread view, just remove it from the list
|
||||||
if (
|
if (
|
||||||
this.state.unreadOrAll == UnreadOrAll.Unread &&
|
this.state.unreadOrAll == UnreadOrAll.Unread &&
|
||||||
data.person_mention_view.person_mention.read
|
data.person_mention_view.person_mention.read
|
||||||
) {
|
) {
|
||||||
this.setState({
|
this.setState({
|
||||||
mentions: this.state.mentions.filter(
|
mentions: this.state.mentions.filter(
|
||||||
r =>
|
r =>
|
||||||
r.person_mention.id !==
|
r.person_mention.id !==
|
||||||
data.person_mention_view.person_mention.id
|
data.person_mention_view.person_mention.id
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
this.setState({
|
this.setState({
|
||||||
combined: this.state.combined.filter(
|
combined: this.state.combined.filter(
|
||||||
r => r.id !== data.person_mention_view.person_mention.id
|
r => r.id !== data.person_mention_view.person_mention.id
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// TODO test to make sure these mentions are getting marked as read
|
// TODO test to make sure these mentions are getting marked as read
|
||||||
found.person_mention.read = combinedView.person_mention.read =
|
found.person_mention.read = combinedView.person_mention.read =
|
||||||
data.person_mention_view.person_mention.read;
|
data.person_mention_view.person_mention.read;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.sendUnreadCount(data.person_mention_view.person_mention.read);
|
this.sendUnreadCount(data.person_mention_view.person_mention.read);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.CreatePrivateMessage) {
|
} else if (op == UserOperation.CreatePrivateMessage) {
|
||||||
let data = wsJsonToRes<PrivateMessageResponse>(
|
let data = wsJsonToRes<PrivateMessageResponse>(msg);
|
||||||
msg,
|
let mui = UserService.Instance.myUserInfo;
|
||||||
PrivateMessageResponse
|
if (
|
||||||
);
|
data.private_message_view.recipient.id == mui?.local_user_view.person.id
|
||||||
UserService.Instance.myUserInfo.match({
|
) {
|
||||||
some: mui => {
|
this.state.messages.unshift(data.private_message_view);
|
||||||
if (
|
this.state.combined.unshift(
|
||||||
data.private_message_view.recipient.id ==
|
this.messageToReplyType(data.private_message_view)
|
||||||
mui.local_user_view.person.id
|
);
|
||||||
) {
|
this.setState(this.state);
|
||||||
this.state.messages.unshift(data.private_message_view);
|
}
|
||||||
this.state.combined.unshift(
|
|
||||||
this.messageToReplyType(data.private_message_view)
|
|
||||||
);
|
|
||||||
this.setState(this.state);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
} else if (op == UserOperation.SaveComment) {
|
} else if (op == UserOperation.SaveComment) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
saveCommentRes(data.comment_view, this.state.replies);
|
saveCommentRes(data.comment_view, this.state.replies);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.CreateCommentLike) {
|
} else if (op == UserOperation.CreateCommentLike) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
createCommentLikeRes(data.comment_view, this.state.replies);
|
createCommentLikeRes(data.comment_view, this.state.replies);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.BlockPerson) {
|
} else if (op == UserOperation.BlockPerson) {
|
||||||
let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
|
let data = wsJsonToRes<BlockPersonResponse>(msg);
|
||||||
updatePersonBlock(data);
|
updatePersonBlock(data);
|
||||||
} else if (op == UserOperation.CreatePostReport) {
|
} else if (op == UserOperation.CreatePostReport) {
|
||||||
let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
|
let data = wsJsonToRes<PostReportResponse>(msg);
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
toast(i18n.t("report_created"));
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.CreateCommentReport) {
|
} else if (op == UserOperation.CreateCommentReport) {
|
||||||
let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
|
let data = wsJsonToRes<CommentReportResponse>(msg);
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
toast(i18n.t("report_created"));
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.CreatePrivateMessageReport) {
|
} else if (op == UserOperation.CreatePrivateMessageReport) {
|
||||||
let data = wsJsonToRes<PrivateMessageReportResponse>(
|
let data = wsJsonToRes<PrivateMessageReportResponse>(msg);
|
||||||
msg,
|
|
||||||
PrivateMessageReportResponse
|
|
||||||
);
|
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
toast(i18n.t("report_created"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
import { None } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
LoginResponse,
|
LoginResponse,
|
||||||
PasswordChange as PasswordChangeForm,
|
PasswordChange as PWordChange,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
wsUserOp,
|
wsUserOp,
|
||||||
|
@ -23,21 +22,23 @@ import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
passwordChangeForm: PasswordChangeForm;
|
form: {
|
||||||
|
token: string;
|
||||||
|
password?: string;
|
||||||
|
password_verify?: string;
|
||||||
|
};
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PasswordChange extends Component<any, State> {
|
export class PasswordChange extends Component<any, State> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(this.context);
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
|
|
||||||
emptyState: State = {
|
state: State = {
|
||||||
passwordChangeForm: new PasswordChangeForm({
|
form: {
|
||||||
token: this.props.match.params.token,
|
token: this.props.match.params.token,
|
||||||
password: undefined,
|
},
|
||||||
password_verify: undefined,
|
|
||||||
}),
|
|
||||||
loading: false,
|
loading: false,
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
};
|
};
|
||||||
|
@ -45,15 +46,13 @@ export class PasswordChange extends Component<any, State> {
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,8 +68,6 @@ export class PasswordChange extends Component<any, State> {
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-12 col-lg-6 offset-lg-3 mb-4">
|
<div className="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||||
|
@ -93,7 +90,7 @@ export class PasswordChange extends Component<any, State> {
|
||||||
<input
|
<input
|
||||||
id="new-password"
|
id="new-password"
|
||||||
type="password"
|
type="password"
|
||||||
value={this.state.passwordChangeForm.password}
|
value={this.state.form.password}
|
||||||
onInput={linkEvent(this, this.handlePasswordChange)}
|
onInput={linkEvent(this, this.handlePasswordChange)}
|
||||||
className="form-control"
|
className="form-control"
|
||||||
required
|
required
|
||||||
|
@ -109,7 +106,7 @@ export class PasswordChange extends Component<any, State> {
|
||||||
<input
|
<input
|
||||||
id="verify-password"
|
id="verify-password"
|
||||||
type="password"
|
type="password"
|
||||||
value={this.state.passwordChangeForm.password_verify}
|
value={this.state.form.password_verify}
|
||||||
onInput={linkEvent(this, this.handleVerifyPasswordChange)}
|
onInput={linkEvent(this, this.handleVerifyPasswordChange)}
|
||||||
className="form-control"
|
className="form-control"
|
||||||
required
|
required
|
||||||
|
@ -133,12 +130,12 @@ export class PasswordChange extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePasswordChange(i: PasswordChange, event: any) {
|
handlePasswordChange(i: PasswordChange, event: any) {
|
||||||
i.state.passwordChangeForm.password = event.target.value;
|
i.state.form.password = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleVerifyPasswordChange(i: PasswordChange, event: any) {
|
handleVerifyPasswordChange(i: PasswordChange, event: any) {
|
||||||
i.state.passwordChangeForm.password_verify = event.target.value;
|
i.state.form.password_verify = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,9 +143,18 @@ export class PasswordChange extends Component<any, State> {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.setState({ loading: true });
|
i.setState({ loading: true });
|
||||||
|
|
||||||
WebSocketService.Instance.send(
|
let password = i.state.form.password;
|
||||||
wsClient.passwordChange(i.state.passwordChangeForm)
|
let password_verify = i.state.form.password_verify;
|
||||||
);
|
|
||||||
|
if (password && password_verify) {
|
||||||
|
let form: PWordChange = {
|
||||||
|
token: i.state.form.token,
|
||||||
|
password,
|
||||||
|
password_verify,
|
||||||
|
};
|
||||||
|
|
||||||
|
WebSocketService.Instance.send(wsClient.passwordChange(form));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -159,8 +165,7 @@ export class PasswordChange extends Component<any, State> {
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.PasswordChange) {
|
} else if (op == UserOperation.PasswordChange) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
let data = wsJsonToRes<LoginResponse>(msg);
|
||||||
this.setState(this.emptyState);
|
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
this.props.history.push("/");
|
this.props.history.push("/");
|
||||||
location.reload();
|
location.reload();
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Some } from "@sniptt/monads/build";
|
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommentView,
|
CommentView,
|
||||||
|
@ -94,9 +93,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
key={i.id}
|
key={i.id}
|
||||||
nodes={[{ comment_view: c, children: [], depth: 0 }]}
|
nodes={[{ comment_view: c, children: [], depth: 0 }]}
|
||||||
viewType={CommentViewType.Flat}
|
viewType={CommentViewType.Flat}
|
||||||
admins={Some(this.props.admins)}
|
admins={this.props.admins}
|
||||||
moderators={None}
|
|
||||||
maxCommentsShown={None}
|
|
||||||
noBorder
|
noBorder
|
||||||
noIndent
|
noIndent
|
||||||
showCommunity
|
showCommunity
|
||||||
|
@ -113,9 +110,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
<PostListing
|
<PostListing
|
||||||
key={i.id}
|
key={i.id}
|
||||||
post_view={p}
|
post_view={p}
|
||||||
admins={Some(this.props.admins)}
|
admins={this.props.admins}
|
||||||
duplicates={None}
|
|
||||||
moderators={None}
|
|
||||||
showCommunity
|
showCommunity
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
|
@ -171,9 +166,7 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
<CommentNodes
|
<CommentNodes
|
||||||
nodes={commentsToFlatNodes(this.props.personRes.comments)}
|
nodes={commentsToFlatNodes(this.props.personRes.comments)}
|
||||||
viewType={CommentViewType.Flat}
|
viewType={CommentViewType.Flat}
|
||||||
admins={Some(this.props.admins)}
|
admins={this.props.admins}
|
||||||
moderators={None}
|
|
||||||
maxCommentsShown={None}
|
|
||||||
noIndent
|
noIndent
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
|
@ -192,10 +185,8 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
<>
|
<>
|
||||||
<PostListing
|
<PostListing
|
||||||
post_view={post}
|
post_view={post}
|
||||||
admins={Some(this.props.admins)}
|
admins={this.props.admins}
|
||||||
showCommunity
|
showCommunity
|
||||||
duplicates={None}
|
|
||||||
moderators={None}
|
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
|
|
|
@ -37,9 +37,9 @@ export class PersonListing extends Component<PersonListingProps, any> {
|
||||||
|
|
||||||
let displayName = this.props.useApubName
|
let displayName = this.props.useApubName
|
||||||
? apubName
|
? apubName
|
||||||
: person.display_name.unwrapOr(apubName);
|
: person.display_name ?? apubName;
|
||||||
|
|
||||||
if (this.props.showApubName && !local && person.display_name.isSome()) {
|
if (this.props.showApubName && !local && person.display_name) {
|
||||||
displayName = `${displayName} (${apubName})`;
|
displayName = `${displayName} (${apubName})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,14 +70,12 @@ export class PersonListing extends Component<PersonListingProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
avatarAndName(displayName: string) {
|
avatarAndName(displayName: string) {
|
||||||
|
let avatar = this.props.person.avatar;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{this.props.person.avatar.match({
|
{avatar && !this.props.hideAvatar && showAvatars() && (
|
||||||
some: avatar =>
|
<PictrsImage src={avatar} icon />
|
||||||
!this.props.hideAvatar &&
|
)}
|
||||||
showAvatars() && <PictrsImage src={avatar} icon />,
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
<span>{displayName}</span>
|
<span>{displayName}</span>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
|
@ -14,9 +13,9 @@ import { i18n } from "../../i18next";
|
||||||
import { InitialFetchRequest } from "../../interfaces";
|
import { InitialFetchRequest } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
|
myAuth,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
setupTippy,
|
setupTippy,
|
||||||
toast,
|
toast,
|
||||||
|
@ -35,7 +34,7 @@ enum UnreadOrAll {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RegistrationApplicationsState {
|
interface RegistrationApplicationsState {
|
||||||
listRegistrationApplicationsResponse: Option<ListRegistrationApplicationsResponse>;
|
listRegistrationApplicationsResponse?: ListRegistrationApplicationsResponse;
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
unreadOrAll: UnreadOrAll;
|
unreadOrAll: UnreadOrAll;
|
||||||
page: number;
|
page: number;
|
||||||
|
@ -46,13 +45,9 @@ export class RegistrationApplications extends Component<
|
||||||
any,
|
any,
|
||||||
RegistrationApplicationsState
|
RegistrationApplicationsState
|
||||||
> {
|
> {
|
||||||
private isoData = setIsoData(
|
private isoData = setIsoData(this.context);
|
||||||
this.context,
|
private subscription?: Subscription;
|
||||||
ListRegistrationApplicationsResponse
|
state: RegistrationApplicationsState = {
|
||||||
);
|
|
||||||
private subscription: Subscription;
|
|
||||||
private emptyState: RegistrationApplicationsState = {
|
|
||||||
listRegistrationApplicationsResponse: None,
|
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
unreadOrAll: UnreadOrAll.Unread,
|
unreadOrAll: UnreadOrAll.Unread,
|
||||||
page: 1,
|
page: 1,
|
||||||
|
@ -62,10 +57,9 @@ export class RegistrationApplications extends Component<
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
this.handlePageChange = this.handlePageChange.bind(this);
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
|
|
||||||
if (UserService.Instance.myUserInfo.isNone() && isBrowser()) {
|
if (!UserService.Instance.myUserInfo && isBrowser()) {
|
||||||
toast(i18n.t("not_logged_in"), "danger");
|
toast(i18n.t("not_logged_in"), "danger");
|
||||||
this.context.router.history.push(`/login`);
|
this.context.router.history.push(`/login`);
|
||||||
}
|
}
|
||||||
|
@ -77,9 +71,8 @@ export class RegistrationApplications extends Component<
|
||||||
if (this.isoData.path == this.context.router.route.match.url) {
|
if (this.isoData.path == this.context.router.route.match.url) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
listRegistrationApplicationsResponse: Some(
|
listRegistrationApplicationsResponse: this.isoData
|
||||||
this.isoData.routeData[0] as ListRegistrationApplicationsResponse
|
.routeData[0] as ListRegistrationApplicationsResponse,
|
||||||
),
|
|
||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
@ -93,18 +86,17 @@ export class RegistrationApplications extends Component<
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return UserService.Instance.myUserInfo.match({
|
let mui = UserService.Instance.myUserInfo;
|
||||||
some: mui =>
|
return mui
|
||||||
`@${mui.local_user_view.person.name} ${i18n.t(
|
? `@${mui.local_user_view.person.name} ${i18n.t(
|
||||||
"registration_applications"
|
"registration_applications"
|
||||||
)} - ${this.state.siteRes.site_view.site.name}`,
|
)} - ${this.state.siteRes.site_view.site.name}`
|
||||||
none: "",
|
: "";
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -120,8 +112,6 @@ export class RegistrationApplications extends Component<
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
<h5 className="mb-2">{i18n.t("registration_applications")}</h5>
|
<h5 className="mb-2">{i18n.t("registration_applications")}</h5>
|
||||||
{this.selects()}
|
{this.selects()}
|
||||||
|
@ -179,8 +169,9 @@ export class RegistrationApplications extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
applicationList() {
|
applicationList() {
|
||||||
return this.state.listRegistrationApplicationsResponse.match({
|
let res = this.state.listRegistrationApplicationsResponse;
|
||||||
some: res => (
|
return (
|
||||||
|
res && (
|
||||||
<div>
|
<div>
|
||||||
{res.registration_applications.map(ra => (
|
{res.registration_applications.map(ra => (
|
||||||
<>
|
<>
|
||||||
|
@ -192,9 +183,8 @@ export class RegistrationApplications extends Component<
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
),
|
)
|
||||||
none: <></>,
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnreadOrAllChange(i: RegistrationApplications, event: any) {
|
handleUnreadOrAllChange(i: RegistrationApplications, event: any) {
|
||||||
|
@ -210,26 +200,34 @@ export class RegistrationApplications extends Component<
|
||||||
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
let form = new ListRegistrationApplications({
|
let auth = req.auth;
|
||||||
unread_only: Some(true),
|
if (auth) {
|
||||||
page: Some(1),
|
let form: ListRegistrationApplications = {
|
||||||
limit: Some(fetchLimit),
|
unread_only: true,
|
||||||
auth: req.auth.unwrap(),
|
page: 1,
|
||||||
});
|
limit: fetchLimit,
|
||||||
promises.push(req.client.listRegistrationApplications(form));
|
auth,
|
||||||
|
};
|
||||||
|
promises.push(req.client.listRegistrationApplications(form));
|
||||||
|
}
|
||||||
|
|
||||||
return promises;
|
return promises;
|
||||||
}
|
}
|
||||||
|
|
||||||
refetch() {
|
refetch() {
|
||||||
let unread_only = this.state.unreadOrAll == UnreadOrAll.Unread;
|
let unread_only = this.state.unreadOrAll == UnreadOrAll.Unread;
|
||||||
let form = new ListRegistrationApplications({
|
let auth = myAuth();
|
||||||
unread_only: Some(unread_only),
|
if (auth) {
|
||||||
page: Some(this.state.page),
|
let form: ListRegistrationApplications = {
|
||||||
limit: Some(fetchLimit),
|
unread_only: unread_only,
|
||||||
auth: auth().unwrap(),
|
page: this.state.page,
|
||||||
});
|
limit: fetchLimit,
|
||||||
WebSocketService.Instance.send(wsClient.listRegistrationApplications(form));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.listRegistrationApplications(form)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -241,25 +239,18 @@ export class RegistrationApplications extends Component<
|
||||||
} else if (msg.reconnect) {
|
} else if (msg.reconnect) {
|
||||||
this.refetch();
|
this.refetch();
|
||||||
} else if (op == UserOperation.ListRegistrationApplications) {
|
} else if (op == UserOperation.ListRegistrationApplications) {
|
||||||
let data = wsJsonToRes<ListRegistrationApplicationsResponse>(
|
let data = wsJsonToRes<ListRegistrationApplicationsResponse>(msg);
|
||||||
msg,
|
|
||||||
ListRegistrationApplicationsResponse
|
|
||||||
);
|
|
||||||
this.setState({
|
this.setState({
|
||||||
listRegistrationApplicationsResponse: Some(data),
|
listRegistrationApplicationsResponse: data,
|
||||||
loading: false,
|
loading: false,
|
||||||
});
|
});
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
} else if (op == UserOperation.ApproveRegistrationApplication) {
|
} else if (op == UserOperation.ApproveRegistrationApplication) {
|
||||||
let data = wsJsonToRes<RegistrationApplicationResponse>(
|
let data = wsJsonToRes<RegistrationApplicationResponse>(msg);
|
||||||
msg,
|
|
||||||
RegistrationApplicationResponse
|
|
||||||
);
|
|
||||||
updateRegistrationApplicationRes(
|
updateRegistrationApplicationRes(
|
||||||
data.registration_application,
|
data.registration_application,
|
||||||
this.state.listRegistrationApplicationsResponse
|
this.state.listRegistrationApplicationsResponse
|
||||||
.map(r => r.registration_applications)
|
?.registration_applications
|
||||||
.unwrapOr([])
|
|
||||||
);
|
);
|
||||||
let uacs = UserService.Instance.unreadApplicationCountSub;
|
let uacs = UserService.Instance.unreadApplicationCountSub;
|
||||||
// Minor bug, where if the application switches from deny to approve, the count will still go down
|
// Minor bug, where if the application switches from deny to approve, the count will still go down
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommentReportResponse,
|
CommentReportResponse,
|
||||||
|
@ -24,9 +23,9 @@ import { InitialFetchRequest } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
amAdmin,
|
amAdmin,
|
||||||
auth,
|
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
|
myAuth,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
setupTippy,
|
setupTippy,
|
||||||
toast,
|
toast,
|
||||||
|
@ -69,9 +68,9 @@ type ItemType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ReportsState {
|
interface ReportsState {
|
||||||
listCommentReportsResponse: Option<ListCommentReportsResponse>;
|
listCommentReportsResponse?: ListCommentReportsResponse;
|
||||||
listPostReportsResponse: Option<ListPostReportsResponse>;
|
listPostReportsResponse?: ListPostReportsResponse;
|
||||||
listPrivateMessageReportsResponse: Option<ListPrivateMessageReportsResponse>;
|
listPrivateMessageReportsResponse?: ListPrivateMessageReportsResponse;
|
||||||
unreadOrAll: UnreadOrAll;
|
unreadOrAll: UnreadOrAll;
|
||||||
messageType: MessageType;
|
messageType: MessageType;
|
||||||
combined: ItemType[];
|
combined: ItemType[];
|
||||||
|
@ -81,17 +80,9 @@ interface ReportsState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Reports extends Component<any, ReportsState> {
|
export class Reports extends Component<any, ReportsState> {
|
||||||
private isoData = setIsoData(
|
private isoData = setIsoData(this.context);
|
||||||
this.context,
|
private subscription?: Subscription;
|
||||||
ListCommentReportsResponse,
|
state: ReportsState = {
|
||||||
ListPostReportsResponse,
|
|
||||||
ListPrivateMessageReportsResponse
|
|
||||||
);
|
|
||||||
private subscription: Subscription;
|
|
||||||
private emptyState: ReportsState = {
|
|
||||||
listCommentReportsResponse: None,
|
|
||||||
listPostReportsResponse: None,
|
|
||||||
listPrivateMessageReportsResponse: None,
|
|
||||||
unreadOrAll: UnreadOrAll.Unread,
|
unreadOrAll: UnreadOrAll.Unread,
|
||||||
messageType: MessageType.All,
|
messageType: MessageType.All,
|
||||||
combined: [],
|
combined: [],
|
||||||
|
@ -103,10 +94,9 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
this.handlePageChange = this.handlePageChange.bind(this);
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
|
|
||||||
if (UserService.Instance.myUserInfo.isNone() && isBrowser()) {
|
if (!UserService.Instance.myUserInfo && isBrowser()) {
|
||||||
toast(i18n.t("not_logged_in"), "danger");
|
toast(i18n.t("not_logged_in"), "danger");
|
||||||
this.context.router.history.push(`/login`);
|
this.context.router.history.push(`/login`);
|
||||||
}
|
}
|
||||||
|
@ -118,19 +108,16 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
if (this.isoData.path == this.context.router.route.match.url) {
|
if (this.isoData.path == this.context.router.route.match.url) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
listCommentReportsResponse: Some(
|
listCommentReportsResponse: this.isoData
|
||||||
this.isoData.routeData[0] as ListCommentReportsResponse
|
.routeData[0] as ListCommentReportsResponse,
|
||||||
),
|
listPostReportsResponse: this.isoData
|
||||||
listPostReportsResponse: Some(
|
.routeData[1] as ListPostReportsResponse,
|
||||||
this.isoData.routeData[1] as ListPostReportsResponse
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
if (amAdmin()) {
|
if (amAdmin()) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
listPrivateMessageReportsResponse: Some(
|
listPrivateMessageReportsResponse: this.isoData
|
||||||
this.isoData.routeData[2] as ListPrivateMessageReportsResponse
|
.routeData[2] as ListPrivateMessageReportsResponse,
|
||||||
),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
this.state = {
|
this.state = {
|
||||||
|
@ -145,18 +132,17 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return UserService.Instance.myUserInfo.match({
|
let mui = UserService.Instance.myUserInfo;
|
||||||
some: mui =>
|
return mui
|
||||||
`@${mui.local_user_view.person.name} ${i18n.t("reports")} - ${
|
? `@${mui.local_user_view.person.name} ${i18n.t("reports")} - ${
|
||||||
this.state.siteRes.site_view.site.name
|
this.state.siteRes.site_view.site.name
|
||||||
}`,
|
}`
|
||||||
none: "",
|
: "";
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -172,8 +158,6 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
<h5 className="mb-2">{i18n.t("reports")}</h5>
|
<h5 className="mb-2">{i18n.t("reports")}</h5>
|
||||||
{this.selects()}
|
{this.selects()}
|
||||||
|
@ -331,19 +315,22 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildCombined(): ItemType[] {
|
buildCombined(): ItemType[] {
|
||||||
let comments: ItemType[] = this.state.listCommentReportsResponse
|
// let comments: ItemType[] = this.state.listCommentReportsResponse
|
||||||
.map(r => r.comment_reports)
|
// .map(r => r.comment_reports)
|
||||||
.unwrapOr([])
|
// .unwrapOr([])
|
||||||
.map(r => this.commentReportToItemType(r));
|
// .map(r => this.commentReportToItemType(r));
|
||||||
let posts: ItemType[] = this.state.listPostReportsResponse
|
let comments =
|
||||||
.map(r => r.post_reports)
|
this.state.listCommentReportsResponse?.comment_reports.map(
|
||||||
.unwrapOr([])
|
this.commentReportToItemType
|
||||||
.map(r => this.postReportToItemType(r));
|
) ?? [];
|
||||||
let privateMessages: ItemType[] =
|
let posts =
|
||||||
this.state.listPrivateMessageReportsResponse
|
this.state.listPostReportsResponse?.post_reports.map(
|
||||||
.map(r => r.private_message_reports)
|
this.postReportToItemType
|
||||||
.unwrapOr([])
|
) ?? [];
|
||||||
.map(r => this.privateMessageReportToItemType(r));
|
let privateMessages =
|
||||||
|
this.state.listPrivateMessageReportsResponse?.private_message_reports.map(
|
||||||
|
this.privateMessageReportToItemType
|
||||||
|
) ?? [];
|
||||||
|
|
||||||
return [...comments, ...posts, ...privateMessages].sort((a, b) =>
|
return [...comments, ...posts, ...privateMessages].sort((a, b) =>
|
||||||
b.published.localeCompare(a.published)
|
b.published.localeCompare(a.published)
|
||||||
|
@ -384,42 +371,44 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
commentReports() {
|
commentReports() {
|
||||||
return this.state.listCommentReportsResponse.match({
|
let reports = this.state.listCommentReportsResponse?.comment_reports;
|
||||||
some: res => (
|
return (
|
||||||
|
reports && (
|
||||||
<div>
|
<div>
|
||||||
{res.comment_reports.map(cr => (
|
{reports.map(cr => (
|
||||||
<>
|
<>
|
||||||
<hr />
|
<hr />
|
||||||
<CommentReport key={cr.comment_report.id} report={cr} />
|
<CommentReport key={cr.comment_report.id} report={cr} />
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
),
|
)
|
||||||
none: <></>,
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
postReports() {
|
postReports() {
|
||||||
return this.state.listPostReportsResponse.match({
|
let reports = this.state.listPostReportsResponse?.post_reports;
|
||||||
some: res => (
|
return (
|
||||||
|
reports && (
|
||||||
<div>
|
<div>
|
||||||
{res.post_reports.map(pr => (
|
{reports.map(pr => (
|
||||||
<>
|
<>
|
||||||
<hr />
|
<hr />
|
||||||
<PostReport key={pr.post_report.id} report={pr} />
|
<PostReport key={pr.post_report.id} report={pr} />
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
),
|
)
|
||||||
none: <></>,
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
privateMessageReports() {
|
privateMessageReports() {
|
||||||
return this.state.listPrivateMessageReportsResponse.match({
|
let reports =
|
||||||
some: res => (
|
this.state.listPrivateMessageReportsResponse?.private_message_reports;
|
||||||
|
return (
|
||||||
|
reports && (
|
||||||
<div>
|
<div>
|
||||||
{res.private_message_reports.map(pmr => (
|
{reports.map(pmr => (
|
||||||
<>
|
<>
|
||||||
<hr />
|
<hr />
|
||||||
<PrivateMessageReport
|
<PrivateMessageReport
|
||||||
|
@ -429,9 +418,8 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
),
|
)
|
||||||
none: <></>,
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePageChange(page: number) {
|
handlePageChange(page: number) {
|
||||||
|
@ -452,85 +440,79 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
let unresolved_only = Some(true);
|
let unresolved_only = true;
|
||||||
let page = Some(1);
|
let page = 1;
|
||||||
let limit = Some(fetchLimit);
|
let limit = fetchLimit;
|
||||||
let community_id = None;
|
let auth = req.auth;
|
||||||
let auth = req.auth.unwrap();
|
|
||||||
|
|
||||||
let commentReportsForm = new ListCommentReports({
|
if (auth) {
|
||||||
// TODO community_id
|
let commentReportsForm: ListCommentReports = {
|
||||||
unresolved_only,
|
|
||||||
community_id,
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
auth,
|
|
||||||
});
|
|
||||||
promises.push(req.client.listCommentReports(commentReportsForm));
|
|
||||||
|
|
||||||
let postReportsForm = new ListPostReports({
|
|
||||||
// TODO community_id
|
|
||||||
unresolved_only,
|
|
||||||
community_id,
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
auth,
|
|
||||||
});
|
|
||||||
promises.push(req.client.listPostReports(postReportsForm));
|
|
||||||
|
|
||||||
if (amAdmin()) {
|
|
||||||
let privateMessageReportsForm = new ListPrivateMessageReports({
|
|
||||||
unresolved_only,
|
unresolved_only,
|
||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
auth,
|
auth,
|
||||||
});
|
};
|
||||||
promises.push(
|
promises.push(req.client.listCommentReports(commentReportsForm));
|
||||||
req.client.listPrivateMessageReports(privateMessageReportsForm)
|
|
||||||
);
|
let postReportsForm: ListPostReports = {
|
||||||
|
unresolved_only,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
auth,
|
||||||
|
};
|
||||||
|
promises.push(req.client.listPostReports(postReportsForm));
|
||||||
|
|
||||||
|
if (amAdmin()) {
|
||||||
|
let privateMessageReportsForm: ListPrivateMessageReports = {
|
||||||
|
unresolved_only,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
auth,
|
||||||
|
};
|
||||||
|
promises.push(
|
||||||
|
req.client.listPrivateMessageReports(privateMessageReportsForm)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return promises;
|
return promises;
|
||||||
}
|
}
|
||||||
|
|
||||||
refetch() {
|
refetch() {
|
||||||
let unresolved_only = Some(this.state.unreadOrAll == UnreadOrAll.Unread);
|
let unresolved_only = this.state.unreadOrAll == UnreadOrAll.Unread;
|
||||||
let community_id = None;
|
let page = this.state.page;
|
||||||
let page = Some(this.state.page);
|
let limit = fetchLimit;
|
||||||
let limit = Some(fetchLimit);
|
let auth = myAuth();
|
||||||
|
if (auth) {
|
||||||
let commentReportsForm = new ListCommentReports({
|
let commentReportsForm: ListCommentReports = {
|
||||||
unresolved_only,
|
|
||||||
// TODO community_id
|
|
||||||
community_id,
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
auth: auth().unwrap(),
|
|
||||||
});
|
|
||||||
WebSocketService.Instance.send(
|
|
||||||
wsClient.listCommentReports(commentReportsForm)
|
|
||||||
);
|
|
||||||
|
|
||||||
let postReportsForm = new ListPostReports({
|
|
||||||
unresolved_only,
|
|
||||||
// TODO community_id
|
|
||||||
community_id,
|
|
||||||
page,
|
|
||||||
limit,
|
|
||||||
auth: auth().unwrap(),
|
|
||||||
});
|
|
||||||
WebSocketService.Instance.send(wsClient.listPostReports(postReportsForm));
|
|
||||||
|
|
||||||
if (amAdmin()) {
|
|
||||||
let privateMessageReportsForm = new ListPrivateMessageReports({
|
|
||||||
unresolved_only,
|
unresolved_only,
|
||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
auth: auth().unwrap(),
|
auth,
|
||||||
});
|
};
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.listPrivateMessageReports(privateMessageReportsForm)
|
wsClient.listCommentReports(commentReportsForm)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let postReportsForm: ListPostReports = {
|
||||||
|
unresolved_only,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.listPostReports(postReportsForm));
|
||||||
|
|
||||||
|
if (amAdmin()) {
|
||||||
|
let privateMessageReportsForm: ListPrivateMessageReports = {
|
||||||
|
unresolved_only,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.listPrivateMessageReports(privateMessageReportsForm)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,40 +525,31 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
} else if (msg.reconnect) {
|
} else if (msg.reconnect) {
|
||||||
this.refetch();
|
this.refetch();
|
||||||
} else if (op == UserOperation.ListCommentReports) {
|
} else if (op == UserOperation.ListCommentReports) {
|
||||||
let data = wsJsonToRes<ListCommentReportsResponse>(
|
let data = wsJsonToRes<ListCommentReportsResponse>(msg);
|
||||||
msg,
|
this.setState({ listCommentReportsResponse: data });
|
||||||
ListCommentReportsResponse
|
|
||||||
);
|
|
||||||
this.setState({ listCommentReportsResponse: Some(data) });
|
|
||||||
this.setState({ combined: this.buildCombined(), loading: false });
|
this.setState({ combined: this.buildCombined(), loading: false });
|
||||||
// this.sendUnreadCount();
|
// this.sendUnreadCount();
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.ListPostReports) {
|
} else if (op == UserOperation.ListPostReports) {
|
||||||
let data = wsJsonToRes<ListPostReportsResponse>(
|
let data = wsJsonToRes<ListPostReportsResponse>(msg);
|
||||||
msg,
|
this.setState({ listPostReportsResponse: data });
|
||||||
ListPostReportsResponse
|
|
||||||
);
|
|
||||||
this.setState({ listPostReportsResponse: Some(data) });
|
|
||||||
this.setState({ combined: this.buildCombined(), loading: false });
|
this.setState({ combined: this.buildCombined(), loading: false });
|
||||||
// this.sendUnreadCount();
|
// this.sendUnreadCount();
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.ListPrivateMessageReports) {
|
} else if (op == UserOperation.ListPrivateMessageReports) {
|
||||||
let data = wsJsonToRes<ListPrivateMessageReportsResponse>(
|
let data = wsJsonToRes<ListPrivateMessageReportsResponse>(msg);
|
||||||
msg,
|
this.setState({ listPrivateMessageReportsResponse: data });
|
||||||
ListPrivateMessageReportsResponse
|
|
||||||
);
|
|
||||||
this.setState({ listPrivateMessageReportsResponse: Some(data) });
|
|
||||||
this.setState({ combined: this.buildCombined(), loading: false });
|
this.setState({ combined: this.buildCombined(), loading: false });
|
||||||
// this.sendUnreadCount();
|
// this.sendUnreadCount();
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.ResolvePostReport) {
|
} else if (op == UserOperation.ResolvePostReport) {
|
||||||
let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
|
let data = wsJsonToRes<PostReportResponse>(msg);
|
||||||
updatePostReportRes(
|
updatePostReportRes(
|
||||||
data.post_report_view,
|
data.post_report_view,
|
||||||
this.state.listPostReportsResponse.map(r => r.post_reports).unwrapOr([])
|
this.state.listPostReportsResponse?.post_reports
|
||||||
);
|
);
|
||||||
let urcs = UserService.Instance.unreadReportCountSub;
|
let urcs = UserService.Instance.unreadReportCountSub;
|
||||||
if (data.post_report_view.post_report.resolved) {
|
if (data.post_report_view.post_report.resolved) {
|
||||||
|
@ -586,12 +559,10 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.ResolveCommentReport) {
|
} else if (op == UserOperation.ResolveCommentReport) {
|
||||||
let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
|
let data = wsJsonToRes<CommentReportResponse>(msg);
|
||||||
updateCommentReportRes(
|
updateCommentReportRes(
|
||||||
data.comment_report_view,
|
data.comment_report_view,
|
||||||
this.state.listCommentReportsResponse
|
this.state.listCommentReportsResponse?.comment_reports
|
||||||
.map(r => r.comment_reports)
|
|
||||||
.unwrapOr([])
|
|
||||||
);
|
);
|
||||||
let urcs = UserService.Instance.unreadReportCountSub;
|
let urcs = UserService.Instance.unreadReportCountSub;
|
||||||
if (data.comment_report_view.comment_report.resolved) {
|
if (data.comment_report_view.comment_report.resolved) {
|
||||||
|
@ -601,15 +572,10 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.ResolvePrivateMessageReport) {
|
} else if (op == UserOperation.ResolvePrivateMessageReport) {
|
||||||
let data = wsJsonToRes<PrivateMessageReportResponse>(
|
let data = wsJsonToRes<PrivateMessageReportResponse>(msg);
|
||||||
msg,
|
|
||||||
PrivateMessageReportResponse
|
|
||||||
);
|
|
||||||
updatePrivateMessageReportRes(
|
updatePrivateMessageReportRes(
|
||||||
data.private_message_report_view,
|
data.private_message_report_view,
|
||||||
this.state.listPrivateMessageReportsResponse
|
this.state.listPrivateMessageReportsResponse?.private_message_reports
|
||||||
.map(r => r.private_message_reports)
|
|
||||||
.unwrapOr([])
|
|
||||||
);
|
);
|
||||||
let urcs = UserService.Instance.unreadReportCountSub;
|
let urcs = UserService.Instance.unreadReportCountSub;
|
||||||
if (data.private_message_report_view.private_message_report.resolved) {
|
if (data.private_message_report_view.private_message_report.resolved) {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
BlockCommunity,
|
BlockCommunity,
|
||||||
|
@ -16,7 +15,6 @@ import {
|
||||||
PersonViewSafe,
|
PersonViewSafe,
|
||||||
SaveUserSettings,
|
SaveUserSettings,
|
||||||
SortType,
|
SortType,
|
||||||
toUndefined,
|
|
||||||
UserOperation,
|
UserOperation,
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
wsUserOp,
|
wsUserOp,
|
||||||
|
@ -25,7 +23,6 @@ import { Subscription } from "rxjs";
|
||||||
import { i18n, languages } from "../../i18next";
|
import { i18n, languages } from "../../i18next";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
choicesConfig,
|
choicesConfig,
|
||||||
communitySelectName,
|
communitySelectName,
|
||||||
|
@ -38,6 +35,7 @@ import {
|
||||||
fetchUsers,
|
fetchUsers,
|
||||||
getLanguages,
|
getLanguages,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
|
myAuth,
|
||||||
personSelectName,
|
personSelectName,
|
||||||
personToChoice,
|
personToChoice,
|
||||||
relTags,
|
relTags,
|
||||||
|
@ -67,11 +65,38 @@ if (isBrowser()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SettingsState {
|
interface SettingsState {
|
||||||
saveUserSettingsForm: SaveUserSettings;
|
// TODO redo these forms
|
||||||
changePasswordForm: ChangePassword;
|
saveUserSettingsForm: {
|
||||||
deleteAccountForm: DeleteAccount;
|
show_nsfw?: boolean;
|
||||||
|
theme?: string;
|
||||||
|
default_sort_type?: number;
|
||||||
|
default_listing_type?: number;
|
||||||
|
interface_language?: string;
|
||||||
|
avatar?: string;
|
||||||
|
banner?: string;
|
||||||
|
display_name?: string;
|
||||||
|
email?: string;
|
||||||
|
bio?: string;
|
||||||
|
matrix_user_id?: string;
|
||||||
|
show_avatars?: boolean;
|
||||||
|
show_scores?: boolean;
|
||||||
|
send_notifications_to_email?: boolean;
|
||||||
|
bot_account?: boolean;
|
||||||
|
show_bot_accounts?: boolean;
|
||||||
|
show_read_posts?: boolean;
|
||||||
|
show_new_post_notifs?: boolean;
|
||||||
|
discussion_languages?: number[];
|
||||||
|
};
|
||||||
|
changePasswordForm: {
|
||||||
|
new_password?: string;
|
||||||
|
new_password_verify?: string;
|
||||||
|
old_password?: string;
|
||||||
|
};
|
||||||
|
deleteAccountForm: {
|
||||||
|
password?: string;
|
||||||
|
};
|
||||||
personBlocks: PersonBlockView[];
|
personBlocks: PersonBlockView[];
|
||||||
blockPerson: Option<PersonViewSafe>;
|
blockPerson?: PersonViewSafe;
|
||||||
communityBlocks: CommunityBlockView[];
|
communityBlocks: CommunityBlockView[];
|
||||||
blockCommunityId: number;
|
blockCommunityId: number;
|
||||||
blockCommunity?: CommunityView;
|
blockCommunity?: CommunityView;
|
||||||
|
@ -88,46 +113,16 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(this.context);
|
||||||
private blockPersonChoices: any;
|
private blockPersonChoices: any;
|
||||||
private blockCommunityChoices: any;
|
private blockCommunityChoices: any;
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
private emptyState: SettingsState = {
|
state: SettingsState = {
|
||||||
saveUserSettingsForm: new SaveUserSettings({
|
saveUserSettingsForm: {},
|
||||||
show_nsfw: None,
|
changePasswordForm: {},
|
||||||
show_scores: None,
|
|
||||||
show_avatars: None,
|
|
||||||
show_read_posts: None,
|
|
||||||
show_bot_accounts: None,
|
|
||||||
show_new_post_notifs: None,
|
|
||||||
default_sort_type: None,
|
|
||||||
default_listing_type: None,
|
|
||||||
theme: None,
|
|
||||||
interface_language: None,
|
|
||||||
discussion_languages: None,
|
|
||||||
avatar: None,
|
|
||||||
banner: None,
|
|
||||||
display_name: None,
|
|
||||||
email: None,
|
|
||||||
bio: None,
|
|
||||||
matrix_user_id: None,
|
|
||||||
send_notifications_to_email: None,
|
|
||||||
bot_account: None,
|
|
||||||
auth: undefined,
|
|
||||||
}),
|
|
||||||
changePasswordForm: new ChangePassword({
|
|
||||||
new_password: undefined,
|
|
||||||
new_password_verify: undefined,
|
|
||||||
old_password: undefined,
|
|
||||||
auth: undefined,
|
|
||||||
}),
|
|
||||||
saveUserSettingsLoading: false,
|
saveUserSettingsLoading: false,
|
||||||
changePasswordLoading: false,
|
changePasswordLoading: false,
|
||||||
deleteAccountLoading: false,
|
deleteAccountLoading: false,
|
||||||
deleteAccountShowConfirm: false,
|
deleteAccountShowConfirm: false,
|
||||||
deleteAccountForm: new DeleteAccount({
|
deleteAccountForm: {},
|
||||||
password: undefined,
|
|
||||||
auth: undefined,
|
|
||||||
}),
|
|
||||||
personBlocks: [],
|
personBlocks: [],
|
||||||
blockPerson: None,
|
|
||||||
communityBlocks: [],
|
communityBlocks: [],
|
||||||
blockCommunityId: 0,
|
blockCommunityId: 0,
|
||||||
currentTab: "settings",
|
currentTab: "settings",
|
||||||
|
@ -138,7 +133,6 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
this.handleSortTypeChange = this.handleSortTypeChange.bind(this);
|
this.handleSortTypeChange = this.handleSortTypeChange.bind(this);
|
||||||
this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
|
this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
|
||||||
this.handleBioChange = this.handleBioChange.bind(this);
|
this.handleBioChange = this.handleBioChange.bind(this);
|
||||||
|
@ -154,8 +148,8 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
if (UserService.Instance.myUserInfo.isSome()) {
|
let mui = UserService.Instance.myUserInfo;
|
||||||
let mui = UserService.Instance.myUserInfo.unwrap();
|
if (mui) {
|
||||||
let luv = mui.local_user_view;
|
let luv = mui.local_user_view;
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
|
@ -163,26 +157,25 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
communityBlocks: mui.community_blocks,
|
communityBlocks: mui.community_blocks,
|
||||||
saveUserSettingsForm: {
|
saveUserSettingsForm: {
|
||||||
...this.state.saveUserSettingsForm,
|
...this.state.saveUserSettingsForm,
|
||||||
show_nsfw: Some(luv.local_user.show_nsfw),
|
show_nsfw: luv.local_user.show_nsfw,
|
||||||
theme: Some(luv.local_user.theme ? luv.local_user.theme : "browser"),
|
theme: luv.local_user.theme ? luv.local_user.theme : "browser",
|
||||||
default_sort_type: Some(luv.local_user.default_sort_type),
|
default_sort_type: luv.local_user.default_sort_type,
|
||||||
default_listing_type: Some(luv.local_user.default_listing_type),
|
default_listing_type: luv.local_user.default_listing_type,
|
||||||
interface_language: Some(luv.local_user.interface_language),
|
interface_language: luv.local_user.interface_language,
|
||||||
discussion_languages: Some(mui.discussion_languages),
|
discussion_languages: mui.discussion_languages,
|
||||||
avatar: luv.person.avatar,
|
avatar: luv.person.avatar,
|
||||||
banner: luv.person.banner,
|
banner: luv.person.banner,
|
||||||
display_name: luv.person.display_name,
|
display_name: luv.person.display_name,
|
||||||
show_avatars: Some(luv.local_user.show_avatars),
|
show_avatars: luv.local_user.show_avatars,
|
||||||
bot_account: Some(luv.person.bot_account),
|
bot_account: luv.person.bot_account,
|
||||||
show_bot_accounts: Some(luv.local_user.show_bot_accounts),
|
show_bot_accounts: luv.local_user.show_bot_accounts,
|
||||||
show_scores: Some(luv.local_user.show_scores),
|
show_scores: luv.local_user.show_scores,
|
||||||
show_read_posts: Some(luv.local_user.show_read_posts),
|
show_read_posts: luv.local_user.show_read_posts,
|
||||||
show_new_post_notifs: Some(luv.local_user.show_new_post_notifs),
|
show_new_post_notifs: luv.local_user.show_new_post_notifs,
|
||||||
email: luv.local_user.email,
|
email: luv.local_user.email,
|
||||||
bio: luv.person.bio,
|
bio: luv.person.bio,
|
||||||
send_notifications_to_email: Some(
|
send_notifications_to_email:
|
||||||
luv.local_user.send_notifications_to_email
|
luv.local_user.send_notifications_to_email,
|
||||||
),
|
|
||||||
matrix_user_id: luv.person.matrix_user_id,
|
matrix_user_id: luv.person.matrix_user_id,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -195,7 +188,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
|
@ -209,7 +202,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={Some(this.documentTitle)}
|
description={this.documentTitle}
|
||||||
image={this.state.saveUserSettingsForm.avatar}
|
image={this.state.saveUserSettingsForm.avatar}
|
||||||
/>
|
/>
|
||||||
<ul className="nav nav-tabs mb-2">
|
<ul className="nav nav-tabs mb-2">
|
||||||
|
@ -391,6 +384,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
blockUserForm() {
|
blockUserForm() {
|
||||||
|
let blockPerson = this.state.blockPerson;
|
||||||
return (
|
return (
|
||||||
<div className="form-group row">
|
<div className="form-group row">
|
||||||
<label
|
<label
|
||||||
|
@ -403,17 +397,14 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
<select
|
<select
|
||||||
className="form-control"
|
className="form-control"
|
||||||
id="block-person-filter"
|
id="block-person-filter"
|
||||||
value={this.state.blockPerson.map(p => p.person.id).unwrapOr(0)}
|
value={blockPerson?.person.id ?? 0}
|
||||||
>
|
>
|
||||||
<option value="0">—</option>
|
<option value="0">—</option>
|
||||||
{this.state.blockPerson.match({
|
{blockPerson && (
|
||||||
some: personView => (
|
<option value={blockPerson.person.id}>
|
||||||
<option value={personView.person.id}>
|
{personSelectName(blockPerson)}
|
||||||
{personSelectName(personView)}
|
</option>
|
||||||
</option>
|
)}
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -500,9 +491,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
placeholder={i18n.t("optional")}
|
placeholder={i18n.t("optional")}
|
||||||
value={toUndefined(
|
value={this.state.saveUserSettingsForm.display_name}
|
||||||
this.state.saveUserSettingsForm.display_name
|
|
||||||
)}
|
|
||||||
onInput={linkEvent(this, this.handleDisplayNameChange)}
|
onInput={linkEvent(this, this.handleDisplayNameChange)}
|
||||||
pattern="^(?!@)(.+)$"
|
pattern="^(?!@)(.+)$"
|
||||||
minLength={3}
|
minLength={3}
|
||||||
|
@ -516,11 +505,8 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
<div className="col-sm-9">
|
<div className="col-sm-9">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.saveUserSettingsForm.bio}
|
initialContent={this.state.saveUserSettingsForm.bio}
|
||||||
initialLanguageId={None}
|
|
||||||
onContentChange={this.handleBioChange}
|
onContentChange={this.handleBioChange}
|
||||||
maxLength={Some(300)}
|
maxLength={300}
|
||||||
placeholder={None}
|
|
||||||
buttonTitle={None}
|
|
||||||
hideNavigationWarnings
|
hideNavigationWarnings
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
siteLanguages={this.state.siteRes.discussion_languages}
|
||||||
|
@ -537,7 +523,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
id="user-email"
|
id="user-email"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
placeholder={i18n.t("optional")}
|
placeholder={i18n.t("optional")}
|
||||||
value={toUndefined(this.state.saveUserSettingsForm.email)}
|
value={this.state.saveUserSettingsForm.email}
|
||||||
onInput={linkEvent(this, this.handleEmailChange)}
|
onInput={linkEvent(this, this.handleEmailChange)}
|
||||||
minLength={3}
|
minLength={3}
|
||||||
/>
|
/>
|
||||||
|
@ -555,9 +541,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
placeholder="@user:example.com"
|
placeholder="@user:example.com"
|
||||||
value={toUndefined(
|
value={this.state.saveUserSettingsForm.matrix_user_id}
|
||||||
this.state.saveUserSettingsForm.matrix_user_id
|
|
||||||
)}
|
|
||||||
onInput={linkEvent(this, this.handleMatrixUserIdChange)}
|
onInput={linkEvent(this, this.handleMatrixUserIdChange)}
|
||||||
pattern="^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"
|
pattern="^@[A-Za-z0-9._=-]+:[A-Za-z0-9.-]+\.[A-Za-z]{2,}$"
|
||||||
/>
|
/>
|
||||||
|
@ -593,9 +577,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
<div className="col-sm-9">
|
<div className="col-sm-9">
|
||||||
<select
|
<select
|
||||||
id="user-language"
|
id="user-language"
|
||||||
value={toUndefined(
|
value={this.state.saveUserSettingsForm.interface_language}
|
||||||
this.state.saveUserSettingsForm.interface_language
|
|
||||||
)}
|
|
||||||
onChange={linkEvent(this, this.handleInterfaceLangChange)}
|
onChange={linkEvent(this, this.handleInterfaceLangChange)}
|
||||||
className="custom-select w-auto"
|
className="custom-select w-auto"
|
||||||
>
|
>
|
||||||
|
@ -631,7 +613,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
<div className="col-sm-9">
|
<div className="col-sm-9">
|
||||||
<select
|
<select
|
||||||
id="user-theme"
|
id="user-theme"
|
||||||
value={toUndefined(this.state.saveUserSettingsForm.theme)}
|
value={this.state.saveUserSettingsForm.theme}
|
||||||
onChange={linkEvent(this, this.handleThemeChange)}
|
onChange={linkEvent(this, this.handleThemeChange)}
|
||||||
className="custom-select w-auto"
|
className="custom-select w-auto"
|
||||||
>
|
>
|
||||||
|
@ -653,9 +635,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
<ListingTypeSelect
|
<ListingTypeSelect
|
||||||
type_={
|
type_={
|
||||||
Object.values(ListingType)[
|
Object.values(ListingType)[
|
||||||
this.state.saveUserSettingsForm.default_listing_type.unwrapOr(
|
this.state.saveUserSettingsForm.default_listing_type ?? 1
|
||||||
1
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
showLocal={showLocal(this.isoData)}
|
showLocal={showLocal(this.isoData)}
|
||||||
|
@ -670,9 +650,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
<SortSelect
|
<SortSelect
|
||||||
sort={
|
sort={
|
||||||
Object.values(SortType)[
|
Object.values(SortType)[
|
||||||
this.state.saveUserSettingsForm.default_sort_type.unwrapOr(
|
this.state.saveUserSettingsForm.default_sort_type ?? 0
|
||||||
0
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
onChange={this.handleSortTypeChange}
|
onChange={this.handleSortTypeChange}
|
||||||
|
@ -686,9 +664,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
className="form-check-input"
|
className="form-check-input"
|
||||||
id="user-show-nsfw"
|
id="user-show-nsfw"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={this.state.saveUserSettingsForm.show_nsfw}
|
||||||
this.state.saveUserSettingsForm.show_nsfw
|
|
||||||
)}
|
|
||||||
onChange={linkEvent(this, this.handleShowNsfwChange)}
|
onChange={linkEvent(this, this.handleShowNsfwChange)}
|
||||||
/>
|
/>
|
||||||
<label className="form-check-label" htmlFor="user-show-nsfw">
|
<label className="form-check-label" htmlFor="user-show-nsfw">
|
||||||
|
@ -703,9 +679,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
className="form-check-input"
|
className="form-check-input"
|
||||||
id="user-show-scores"
|
id="user-show-scores"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={this.state.saveUserSettingsForm.show_scores}
|
||||||
this.state.saveUserSettingsForm.show_scores
|
|
||||||
)}
|
|
||||||
onChange={linkEvent(this, this.handleShowScoresChange)}
|
onChange={linkEvent(this, this.handleShowScoresChange)}
|
||||||
/>
|
/>
|
||||||
<label className="form-check-label" htmlFor="user-show-scores">
|
<label className="form-check-label" htmlFor="user-show-scores">
|
||||||
|
@ -719,9 +693,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
className="form-check-input"
|
className="form-check-input"
|
||||||
id="user-show-avatars"
|
id="user-show-avatars"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={this.state.saveUserSettingsForm.show_avatars}
|
||||||
this.state.saveUserSettingsForm.show_avatars
|
|
||||||
)}
|
|
||||||
onChange={linkEvent(this, this.handleShowAvatarsChange)}
|
onChange={linkEvent(this, this.handleShowAvatarsChange)}
|
||||||
/>
|
/>
|
||||||
<label className="form-check-label" htmlFor="user-show-avatars">
|
<label className="form-check-label" htmlFor="user-show-avatars">
|
||||||
|
@ -735,9 +707,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
className="form-check-input"
|
className="form-check-input"
|
||||||
id="user-bot-account"
|
id="user-bot-account"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={this.state.saveUserSettingsForm.bot_account}
|
||||||
this.state.saveUserSettingsForm.bot_account
|
|
||||||
)}
|
|
||||||
onChange={linkEvent(this, this.handleBotAccount)}
|
onChange={linkEvent(this, this.handleBotAccount)}
|
||||||
/>
|
/>
|
||||||
<label className="form-check-label" htmlFor="user-bot-account">
|
<label className="form-check-label" htmlFor="user-bot-account">
|
||||||
|
@ -751,9 +721,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
className="form-check-input"
|
className="form-check-input"
|
||||||
id="user-show-bot-accounts"
|
id="user-show-bot-accounts"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={this.state.saveUserSettingsForm.show_bot_accounts}
|
||||||
this.state.saveUserSettingsForm.show_bot_accounts
|
|
||||||
)}
|
|
||||||
onChange={linkEvent(this, this.handleShowBotAccounts)}
|
onChange={linkEvent(this, this.handleShowBotAccounts)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
|
@ -770,9 +738,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
className="form-check-input"
|
className="form-check-input"
|
||||||
id="user-show-read-posts"
|
id="user-show-read-posts"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={this.state.saveUserSettingsForm.show_read_posts}
|
||||||
this.state.saveUserSettingsForm.show_read_posts
|
|
||||||
)}
|
|
||||||
onChange={linkEvent(this, this.handleReadPosts)}
|
onChange={linkEvent(this, this.handleReadPosts)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
|
@ -789,9 +755,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
className="form-check-input"
|
className="form-check-input"
|
||||||
id="user-show-new-post-notifs"
|
id="user-show-new-post-notifs"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(
|
checked={this.state.saveUserSettingsForm.show_new_post_notifs}
|
||||||
this.state.saveUserSettingsForm.show_new_post_notifs
|
|
||||||
)}
|
|
||||||
onChange={linkEvent(this, this.handleShowNewPostNotifs)}
|
onChange={linkEvent(this, this.handleShowNewPostNotifs)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
|
@ -809,9 +773,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
id="user-send-notifications-to-email"
|
id="user-send-notifications-to-email"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
disabled={!this.state.saveUserSettingsForm.email}
|
disabled={!this.state.saveUserSettingsForm.email}
|
||||||
checked={toUndefined(
|
checked={
|
||||||
this.state.saveUserSettingsForm.send_notifications_to_email
|
this.state.saveUserSettingsForm.send_notifications_to_email
|
||||||
)}
|
}
|
||||||
onChange={linkEvent(
|
onChange={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleSendNotificationsToEmailChange
|
this.handleSendNotificationsToEmailChange
|
||||||
|
@ -959,32 +923,37 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBlockPerson(personId: number) {
|
handleBlockPerson(personId: number) {
|
||||||
if (personId != 0) {
|
let auth = myAuth();
|
||||||
let blockUserForm = new BlockPerson({
|
if (auth && personId != 0) {
|
||||||
|
let blockUserForm: BlockPerson = {
|
||||||
person_id: personId,
|
person_id: personId,
|
||||||
block: true,
|
block: true,
|
||||||
auth: auth().unwrap(),
|
auth,
|
||||||
});
|
};
|
||||||
WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
|
WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnblockPerson(i: { ctx: Settings; recipientId: number }) {
|
handleUnblockPerson(i: { ctx: Settings; recipientId: number }) {
|
||||||
let blockUserForm = new BlockPerson({
|
let auth = myAuth();
|
||||||
person_id: i.recipientId,
|
if (auth) {
|
||||||
block: false,
|
let blockUserForm: BlockPerson = {
|
||||||
auth: auth().unwrap(),
|
person_id: i.recipientId,
|
||||||
});
|
block: false,
|
||||||
WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBlockCommunity(community_id: number) {
|
handleBlockCommunity(community_id: number) {
|
||||||
if (community_id != 0) {
|
let auth = myAuth();
|
||||||
let blockCommunityForm = new BlockCommunity({
|
if (auth && community_id != 0) {
|
||||||
|
let blockCommunityForm: BlockCommunity = {
|
||||||
community_id,
|
community_id,
|
||||||
block: true,
|
block: true,
|
||||||
auth: auth().unwrap(),
|
auth,
|
||||||
});
|
};
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.blockCommunity(blockCommunityForm)
|
wsClient.blockCommunity(blockCommunityForm)
|
||||||
);
|
);
|
||||||
|
@ -992,94 +961,93 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnblockCommunity(i: { ctx: Settings; communityId: number }) {
|
handleUnblockCommunity(i: { ctx: Settings; communityId: number }) {
|
||||||
let blockCommunityForm = new BlockCommunity({
|
let auth = myAuth();
|
||||||
community_id: i.communityId,
|
if (auth) {
|
||||||
block: false,
|
let blockCommunityForm: BlockCommunity = {
|
||||||
auth: auth().unwrap(),
|
community_id: i.communityId,
|
||||||
});
|
block: false,
|
||||||
WebSocketService.Instance.send(wsClient.blockCommunity(blockCommunityForm));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.blockCommunity(blockCommunityForm)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowNsfwChange(i: Settings, event: any) {
|
handleShowNsfwChange(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.show_nsfw = Some(event.target.checked);
|
i.state.saveUserSettingsForm.show_nsfw = event.target.checked;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowAvatarsChange(i: Settings, event: any) {
|
handleShowAvatarsChange(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.show_avatars = Some(event.target.checked);
|
i.state.saveUserSettingsForm.show_avatars = event.target.checked;
|
||||||
UserService.Instance.myUserInfo.match({
|
let mui = UserService.Instance.myUserInfo;
|
||||||
some: mui =>
|
if (mui) {
|
||||||
(mui.local_user_view.local_user.show_avatars = event.target.checked),
|
mui.local_user_view.local_user.show_avatars = event.target.checked;
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBotAccount(i: Settings, event: any) {
|
handleBotAccount(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.bot_account = Some(event.target.checked);
|
i.state.saveUserSettingsForm.bot_account = event.target.checked;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowBotAccounts(i: Settings, event: any) {
|
handleShowBotAccounts(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.show_bot_accounts = Some(event.target.checked);
|
i.state.saveUserSettingsForm.show_bot_accounts = event.target.checked;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReadPosts(i: Settings, event: any) {
|
handleReadPosts(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.show_read_posts = Some(event.target.checked);
|
i.state.saveUserSettingsForm.show_read_posts = event.target.checked;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowNewPostNotifs(i: Settings, event: any) {
|
handleShowNewPostNotifs(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.show_new_post_notifs = Some(
|
i.state.saveUserSettingsForm.show_new_post_notifs = event.target.checked;
|
||||||
event.target.checked
|
|
||||||
);
|
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowScoresChange(i: Settings, event: any) {
|
handleShowScoresChange(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.show_scores = Some(event.target.checked);
|
i.state.saveUserSettingsForm.show_scores = event.target.checked;
|
||||||
UserService.Instance.myUserInfo.match({
|
let mui = UserService.Instance.myUserInfo;
|
||||||
some: mui =>
|
if (mui) {
|
||||||
(mui.local_user_view.local_user.show_scores = event.target.checked),
|
mui.local_user_view.local_user.show_scores = event.target.checked;
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSendNotificationsToEmailChange(i: Settings, event: any) {
|
handleSendNotificationsToEmailChange(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.send_notifications_to_email = Some(
|
i.state.saveUserSettingsForm.send_notifications_to_email =
|
||||||
event.target.checked
|
event.target.checked;
|
||||||
);
|
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleThemeChange(i: Settings, event: any) {
|
handleThemeChange(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.theme = Some(event.target.value);
|
i.state.saveUserSettingsForm.theme = event.target.value;
|
||||||
setTheme(event.target.value, true);
|
setTheme(event.target.value, true);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleInterfaceLangChange(i: Settings, event: any) {
|
handleInterfaceLangChange(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.interface_language = Some(event.target.value);
|
i.state.saveUserSettingsForm.interface_language = event.target.value;
|
||||||
i18n.changeLanguage(
|
i18n.changeLanguage(
|
||||||
getLanguages(i.state.saveUserSettingsForm.interface_language.unwrap())[0]
|
getLanguages(i.state.saveUserSettingsForm.interface_language).at(0)
|
||||||
);
|
);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDiscussionLanguageChange(val: number[]) {
|
handleDiscussionLanguageChange(val: number[]) {
|
||||||
this.setState(
|
this.setState(
|
||||||
s => ((s.saveUserSettingsForm.discussion_languages = Some(val)), s)
|
s => ((s.saveUserSettingsForm.discussion_languages = val), s)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSortTypeChange(val: SortType) {
|
handleSortTypeChange(val: SortType) {
|
||||||
this.setState(
|
this.setState(
|
||||||
s => (
|
s => (
|
||||||
(s.saveUserSettingsForm.default_sort_type = Some(
|
(s.saveUserSettingsForm.default_sort_type =
|
||||||
Object.keys(SortType).indexOf(val)
|
Object.keys(SortType).indexOf(val)),
|
||||||
)),
|
|
||||||
s
|
s
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -1088,46 +1056,45 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
handleListingTypeChange(val: ListingType) {
|
handleListingTypeChange(val: ListingType) {
|
||||||
this.setState(
|
this.setState(
|
||||||
s => (
|
s => (
|
||||||
(s.saveUserSettingsForm.default_listing_type = Some(
|
(s.saveUserSettingsForm.default_listing_type =
|
||||||
Object.keys(ListingType).indexOf(val)
|
Object.keys(ListingType).indexOf(val)),
|
||||||
)),
|
|
||||||
s
|
s
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEmailChange(i: Settings, event: any) {
|
handleEmailChange(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.email = Some(event.target.value);
|
i.state.saveUserSettingsForm.email = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBioChange(val: string) {
|
handleBioChange(val: string) {
|
||||||
this.setState(s => ((s.saveUserSettingsForm.bio = Some(val)), s));
|
this.setState(s => ((s.saveUserSettingsForm.bio = val), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAvatarUpload(url: string) {
|
handleAvatarUpload(url: string) {
|
||||||
this.setState(s => ((s.saveUserSettingsForm.avatar = Some(url)), s));
|
this.setState(s => ((s.saveUserSettingsForm.avatar = url), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAvatarRemove() {
|
handleAvatarRemove() {
|
||||||
this.setState(s => ((s.saveUserSettingsForm.avatar = Some("")), s));
|
this.setState(s => ((s.saveUserSettingsForm.avatar = ""), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerUpload(url: string) {
|
handleBannerUpload(url: string) {
|
||||||
this.setState(s => ((s.saveUserSettingsForm.banner = Some(url)), s));
|
this.setState(s => ((s.saveUserSettingsForm.banner = url), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerRemove() {
|
handleBannerRemove() {
|
||||||
this.setState(s => ((s.saveUserSettingsForm.banner = Some("")), s));
|
this.setState(s => ((s.saveUserSettingsForm.banner = ""), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDisplayNameChange(i: Settings, event: any) {
|
handleDisplayNameChange(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.display_name = Some(event.target.value);
|
i.state.saveUserSettingsForm.display_name = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMatrixUserIdChange(i: Settings, event: any) {
|
handleMatrixUserIdChange(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.matrix_user_id = Some(event.target.value);
|
i.state.saveUserSettingsForm.matrix_user_id = event.target.value;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1158,20 +1125,31 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
handleSaveSettingsSubmit(i: Settings, event: any) {
|
handleSaveSettingsSubmit(i: Settings, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.setState({ saveUserSettingsLoading: true });
|
i.setState({ saveUserSettingsLoading: true });
|
||||||
i.setState(s => ((s.saveUserSettingsForm.auth = auth().unwrap()), s));
|
let auth = myAuth();
|
||||||
|
if (auth) {
|
||||||
let form = new SaveUserSettings({ ...i.state.saveUserSettingsForm });
|
let form: SaveUserSettings = { ...i.state.saveUserSettingsForm, auth };
|
||||||
WebSocketService.Instance.send(wsClient.saveUserSettings(form));
|
WebSocketService.Instance.send(wsClient.saveUserSettings(form));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChangePasswordSubmit(i: Settings, event: any) {
|
handleChangePasswordSubmit(i: Settings, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.setState({ changePasswordLoading: true });
|
i.setState({ changePasswordLoading: true });
|
||||||
i.setState(s => ((s.changePasswordForm.auth = auth().unwrap()), s));
|
let auth = myAuth();
|
||||||
|
let pForm = i.state.changePasswordForm;
|
||||||
|
let new_password = pForm.new_password;
|
||||||
|
let new_password_verify = pForm.new_password_verify;
|
||||||
|
let old_password = pForm.old_password;
|
||||||
|
if (auth && new_password && old_password && new_password_verify) {
|
||||||
|
let form: ChangePassword = {
|
||||||
|
new_password,
|
||||||
|
new_password_verify,
|
||||||
|
old_password,
|
||||||
|
auth,
|
||||||
|
};
|
||||||
|
|
||||||
let form = new ChangePassword({ ...i.state.changePasswordForm });
|
WebSocketService.Instance.send(wsClient.changePassword(form));
|
||||||
|
}
|
||||||
WebSocketService.Instance.send(wsClient.changePassword(form));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteAccountShowConfirmToggle(i: Settings, event: any) {
|
handleDeleteAccountShowConfirmToggle(i: Settings, event: any) {
|
||||||
|
@ -1187,11 +1165,15 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
handleDeleteAccount(i: Settings, event: any) {
|
handleDeleteAccount(i: Settings, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.setState({ deleteAccountLoading: true });
|
i.setState({ deleteAccountLoading: true });
|
||||||
i.setState(s => ((s.deleteAccountForm.auth = auth().unwrap()), s));
|
let auth = myAuth();
|
||||||
|
let password = i.state.deleteAccountForm.password;
|
||||||
let form = new DeleteAccount({ ...i.state.deleteAccountForm });
|
if (auth && password) {
|
||||||
|
let form: DeleteAccount = {
|
||||||
WebSocketService.Instance.send(wsClient.deleteAccount(form));
|
password,
|
||||||
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.deleteAccount(form));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSwitchTab(i: { ctx: Settings; tab: string }) {
|
handleSwitchTab(i: { ctx: Settings; tab: string }) {
|
||||||
|
@ -1215,14 +1197,14 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.SaveUserSettings) {
|
} else if (op == UserOperation.SaveUserSettings) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
let data = wsJsonToRes<LoginResponse>(msg);
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
location.reload();
|
location.reload();
|
||||||
this.setState({ saveUserSettingsLoading: false });
|
this.setState({ saveUserSettingsLoading: false });
|
||||||
toast(i18n.t("saved"));
|
toast(i18n.t("saved"));
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
} else if (op == UserOperation.ChangePassword) {
|
} else if (op == UserOperation.ChangePassword) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
let data = wsJsonToRes<LoginResponse>(msg);
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
this.setState({ changePasswordLoading: false });
|
this.setState({ changePasswordLoading: false });
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
|
@ -1235,20 +1217,19 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
UserService.Instance.logout();
|
UserService.Instance.logout();
|
||||||
window.location.href = "/";
|
window.location.href = "/";
|
||||||
} else if (op == UserOperation.BlockPerson) {
|
} else if (op == UserOperation.BlockPerson) {
|
||||||
let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
|
let data = wsJsonToRes<BlockPersonResponse>(msg);
|
||||||
updatePersonBlock(data).match({
|
updatePersonBlock(data);
|
||||||
some: blocks => this.setState({ personBlocks: blocks }),
|
let mui = UserService.Instance.myUserInfo;
|
||||||
none: void 0,
|
if (mui) {
|
||||||
});
|
this.setState({ personBlocks: mui.person_blocks });
|
||||||
|
}
|
||||||
} else if (op == UserOperation.BlockCommunity) {
|
} else if (op == UserOperation.BlockCommunity) {
|
||||||
let data = wsJsonToRes<BlockCommunityResponse>(
|
let data = wsJsonToRes<BlockCommunityResponse>(msg);
|
||||||
msg,
|
updateCommunityBlock(data);
|
||||||
BlockCommunityResponse
|
let mui = UserService.Instance.myUserInfo;
|
||||||
);
|
if (mui) {
|
||||||
updateCommunityBlock(data).match({
|
this.setState({ communityBlocks: mui.community_blocks });
|
||||||
some: blocks => this.setState({ communityBlocks: blocks }),
|
}
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None } from "@sniptt/monads/build";
|
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import {
|
import {
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
|
@ -27,20 +26,18 @@ interface State {
|
||||||
|
|
||||||
export class VerifyEmail extends Component<any, State> {
|
export class VerifyEmail extends Component<any, State> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(this.context);
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
|
|
||||||
emptyState: State = {
|
state: State = {
|
||||||
verifyEmailForm: new VerifyEmailForm({
|
verifyEmailForm: {
|
||||||
token: this.props.match.params.token,
|
token: this.props.match.params.token,
|
||||||
}),
|
},
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
}
|
}
|
||||||
|
@ -53,7 +50,7 @@ export class VerifyEmail extends Component<any, State> {
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,8 +66,6 @@ export class VerifyEmail extends Component<any, State> {
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-12 col-lg-6 offset-lg-3 mb-4">
|
<div className="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||||
|
@ -90,10 +85,9 @@ export class VerifyEmail extends Component<any, State> {
|
||||||
this.props.history.push("/");
|
this.props.history.push("/");
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.VerifyEmail) {
|
} else if (op == UserOperation.VerifyEmail) {
|
||||||
let data = wsJsonToRes<VerifyEmailResponse>(msg, VerifyEmailResponse);
|
let data = wsJsonToRes<VerifyEmailResponse>(msg);
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("email_verified"));
|
toast(i18n.t("email_verified"));
|
||||||
this.setState(this.emptyState);
|
|
||||||
this.props.history.push("/login");
|
this.props.history.push("/login");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Either, Left, None, Option, Right, Some } from "@sniptt/monads";
|
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import {
|
import {
|
||||||
GetCommunity,
|
GetCommunity,
|
||||||
|
@ -9,7 +8,6 @@ import {
|
||||||
ListingType,
|
ListingType,
|
||||||
PostView,
|
PostView,
|
||||||
SortType,
|
SortType,
|
||||||
toOption,
|
|
||||||
UserOperation,
|
UserOperation,
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
wsUserOp,
|
wsUserOp,
|
||||||
|
@ -19,11 +17,11 @@ import { InitialFetchRequest, PostFormParams } from "shared/interfaces";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
|
||||||
enableDownvotes,
|
enableDownvotes,
|
||||||
enableNsfw,
|
enableNsfw,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
|
myAuth,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
toast,
|
toast,
|
||||||
wsClient,
|
wsClient,
|
||||||
|
@ -34,30 +32,28 @@ import { Spinner } from "../common/icon";
|
||||||
import { PostForm } from "./post-form";
|
import { PostForm } from "./post-form";
|
||||||
|
|
||||||
interface CreatePostState {
|
interface CreatePostState {
|
||||||
listCommunitiesResponse: Option<ListCommunitiesResponse>;
|
listCommunitiesResponse?: ListCommunitiesResponse;
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CreatePost extends Component<any, CreatePostState> {
|
export class CreatePost extends Component<any, CreatePostState> {
|
||||||
private isoData = setIsoData(this.context, ListCommunitiesResponse);
|
private isoData = setIsoData(this.context);
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
private emptyState: CreatePostState = {
|
state: CreatePostState = {
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
listCommunitiesResponse: None,
|
|
||||||
loading: true,
|
loading: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
|
||||||
|
|
||||||
this.handlePostCreate = this.handlePostCreate.bind(this);
|
this.handlePostCreate = this.handlePostCreate.bind(this);
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
if (UserService.Instance.myUserInfo.isNone() && isBrowser()) {
|
if (!UserService.Instance.myUserInfo && isBrowser()) {
|
||||||
toast(i18n.t("not_logged_in"), "danger");
|
toast(i18n.t("not_logged_in"), "danger");
|
||||||
this.context.router.history.push(`/login`);
|
this.context.router.history.push(`/login`);
|
||||||
}
|
}
|
||||||
|
@ -66,9 +62,8 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
if (this.isoData.path == this.context.router.route.match.url) {
|
if (this.isoData.path == this.context.router.route.match.url) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
listCommunitiesResponse: Some(
|
listCommunitiesResponse: this.isoData
|
||||||
this.isoData.routeData[0] as ListCommunitiesResponse
|
.routeData[0] as ListCommunitiesResponse,
|
||||||
),
|
|
||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
@ -77,44 +72,38 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
refetch() {
|
refetch() {
|
||||||
this.params.nameOrId.match({
|
let nameOrId = this.params.nameOrId;
|
||||||
some: opt =>
|
let auth = myAuth(false);
|
||||||
opt.match({
|
if (nameOrId) {
|
||||||
left: name => {
|
if (typeof nameOrId === "string") {
|
||||||
let form = new GetCommunity({
|
let form: GetCommunity = {
|
||||||
name: Some(name),
|
name: nameOrId,
|
||||||
id: None,
|
auth,
|
||||||
auth: auth(false).ok(),
|
};
|
||||||
});
|
WebSocketService.Instance.send(wsClient.getCommunity(form));
|
||||||
WebSocketService.Instance.send(wsClient.getCommunity(form));
|
} else {
|
||||||
},
|
let form: GetCommunity = {
|
||||||
right: id => {
|
id: nameOrId,
|
||||||
let form = new GetCommunity({
|
auth,
|
||||||
id: Some(id),
|
};
|
||||||
name: None,
|
WebSocketService.Instance.send(wsClient.getCommunity(form));
|
||||||
auth: auth(false).ok(),
|
}
|
||||||
});
|
} else {
|
||||||
WebSocketService.Instance.send(wsClient.getCommunity(form));
|
let listCommunitiesForm: ListCommunities = {
|
||||||
},
|
type_: ListingType.All,
|
||||||
}),
|
sort: SortType.TopAll,
|
||||||
none: () => {
|
limit: fetchLimit,
|
||||||
let listCommunitiesForm = new ListCommunities({
|
auth,
|
||||||
type_: Some(ListingType.All),
|
};
|
||||||
sort: Some(SortType.TopAll),
|
WebSocketService.Instance.send(
|
||||||
limit: Some(fetchLimit),
|
wsClient.listCommunities(listCommunitiesForm)
|
||||||
page: None,
|
);
|
||||||
auth: auth(false).ok(),
|
}
|
||||||
});
|
|
||||||
WebSocketService.Instance.send(
|
|
||||||
wsClient.listCommunities(listCommunitiesForm)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,39 +114,34 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let res = this.state.listCommunitiesResponse;
|
||||||
return (
|
return (
|
||||||
<div className="container-lg">
|
<div className="container-lg">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<h5>
|
<h5>
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
</h5>
|
</h5>
|
||||||
) : (
|
) : (
|
||||||
this.state.listCommunitiesResponse.match({
|
res && (
|
||||||
some: res => (
|
<div className="row">
|
||||||
<div className="row">
|
<div className="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||||
<div className="col-12 col-lg-6 offset-lg-3 mb-4">
|
<h5>{i18n.t("create_post")}</h5>
|
||||||
<h5>{i18n.t("create_post")}</h5>
|
<PostForm
|
||||||
<PostForm
|
communities={res.communities}
|
||||||
post_view={None}
|
onCreate={this.handlePostCreate}
|
||||||
communities={Some(res.communities)}
|
params={this.params}
|
||||||
onCreate={this.handlePostCreate}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
params={Some(this.params)}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
siteLanguages={this.state.siteRes.discussion_languages}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
/>
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
),
|
</div>
|
||||||
none: <></>,
|
)
|
||||||
})
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -165,48 +149,42 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
|
|
||||||
get params(): PostFormParams {
|
get params(): PostFormParams {
|
||||||
let urlParams = new URLSearchParams(this.props.location.search);
|
let urlParams = new URLSearchParams(this.props.location.search);
|
||||||
let name = toOption(urlParams.get("community_name")).or(
|
let name = urlParams.get("community_name") ?? this.prevCommunityName;
|
||||||
this.prevCommunityName
|
let communityIdParam = urlParams.get("community_id");
|
||||||
);
|
let id = communityIdParam ? Number(communityIdParam) : this.prevCommunityId;
|
||||||
let id = toOption(urlParams.get("community_id"))
|
let nameOrId: string | number | undefined;
|
||||||
.map(Number)
|
if (name) {
|
||||||
.or(this.prevCommunityId);
|
nameOrId = name;
|
||||||
let nameOrId: Option<Either<string, number>>;
|
} else if (id) {
|
||||||
if (name.isSome()) {
|
nameOrId = id;
|
||||||
nameOrId = Some(Left(name.unwrap()));
|
|
||||||
} else if (id.isSome()) {
|
|
||||||
nameOrId = Some(Right(id.unwrap()));
|
|
||||||
} else {
|
|
||||||
nameOrId = None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let params: PostFormParams = {
|
let params: PostFormParams = {
|
||||||
name: toOption(urlParams.get("title")),
|
name: urlParams.get("title") ?? undefined,
|
||||||
nameOrId,
|
nameOrId,
|
||||||
body: toOption(urlParams.get("body")),
|
body: urlParams.get("body") ?? undefined,
|
||||||
url: toOption(urlParams.get("url")),
|
url: urlParams.get("url") ?? undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
get prevCommunityName(): Option<string> {
|
get prevCommunityName(): string | undefined {
|
||||||
if (this.props.match.params.name) {
|
if (this.props.match.params.name) {
|
||||||
return toOption(this.props.match.params.name);
|
return this.props.match.params.name;
|
||||||
} else if (this.props.location.state) {
|
} else if (this.props.location.state) {
|
||||||
let lastLocation = this.props.location.state.prevPath;
|
let lastLocation = this.props.location.state.prevPath;
|
||||||
if (lastLocation.includes("/c/")) {
|
if (lastLocation.includes("/c/")) {
|
||||||
return toOption(lastLocation.split("/c/")[1]);
|
return lastLocation.split("/c/").at(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return None;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
get prevCommunityId(): Option<number> {
|
get prevCommunityId(): number | undefined {
|
||||||
if (this.props.match.params.id) {
|
// TODO is this actually a number? Whats the real return type
|
||||||
return toOption(this.props.match.params.id);
|
let id = this.props.match.params.id;
|
||||||
}
|
return id ?? undefined;
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostCreate(post_view: PostView) {
|
handlePostCreate(post_view: PostView) {
|
||||||
|
@ -214,13 +192,12 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
||||||
let listCommunitiesForm = new ListCommunities({
|
let listCommunitiesForm: ListCommunities = {
|
||||||
type_: Some(ListingType.All),
|
type_: ListingType.All,
|
||||||
sort: Some(SortType.TopAll),
|
sort: SortType.TopAll,
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
page: None,
|
|
||||||
auth: req.auth,
|
auth: req.auth,
|
||||||
});
|
};
|
||||||
return [req.client.listCommunities(listCommunitiesForm)];
|
return [req.client.listCommunities(listCommunitiesForm)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,17 +208,14 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.ListCommunities) {
|
} else if (op == UserOperation.ListCommunities) {
|
||||||
let data = wsJsonToRes<ListCommunitiesResponse>(
|
let data = wsJsonToRes<ListCommunitiesResponse>(msg);
|
||||||
msg,
|
this.setState({ listCommunitiesResponse: data, loading: false });
|
||||||
ListCommunitiesResponse
|
|
||||||
);
|
|
||||||
this.setState({ listCommunitiesResponse: Some(data), loading: false });
|
|
||||||
} else if (op == UserOperation.GetCommunity) {
|
} else if (op == UserOperation.GetCommunity) {
|
||||||
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
let data = wsJsonToRes<GetCommunityResponse>(msg);
|
||||||
this.setState({
|
this.setState({
|
||||||
listCommunitiesResponse: Some({
|
listCommunitiesResponse: {
|
||||||
communities: [data.community_view],
|
communities: [data.community_view],
|
||||||
}),
|
},
|
||||||
loading: false,
|
loading: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,84 +17,66 @@ export class MetadataCard extends Component<
|
||||||
MetadataCardProps,
|
MetadataCardProps,
|
||||||
MetadataCardState
|
MetadataCardState
|
||||||
> {
|
> {
|
||||||
private emptyState: MetadataCardState = {
|
state: MetadataCardState = {
|
||||||
expanded: false,
|
expanded: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let post = this.props.post;
|
let post = this.props.post;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!this.state.expanded &&
|
{!this.state.expanded && post.embed_title && post.url && (
|
||||||
post.embed_title.match({
|
<div className="card border-secondary mt-3 mb-2">
|
||||||
some: embedTitle =>
|
<div className="row">
|
||||||
post.url.match({
|
<div className="col-12">
|
||||||
some: url => (
|
<div className="card-body">
|
||||||
<div className="card border-secondary mt-3 mb-2">
|
{post.name !== post.embed_title && (
|
||||||
<div className="row">
|
<>
|
||||||
<div className="col-12">
|
<h5 className="card-title d-inline">
|
||||||
<div className="card-body">
|
<a className="text-body" href={post.url} rel={relTags}>
|
||||||
{post.name !== embedTitle && (
|
{post.embed_title}
|
||||||
<>
|
</a>
|
||||||
<h5 className="card-title d-inline">
|
</h5>
|
||||||
<a
|
<span className="d-inline-block ml-2 mb-2 small text-muted">
|
||||||
className="text-body"
|
<a
|
||||||
href={url}
|
className="text-muted font-italic"
|
||||||
rel={relTags}
|
href={post.url}
|
||||||
>
|
rel={relTags}
|
||||||
{embedTitle}
|
>
|
||||||
</a>
|
{new URL(post.url).hostname}
|
||||||
</h5>
|
<Icon icon="external-link" classes="ml-1" />
|
||||||
<span className="d-inline-block ml-2 mb-2 small text-muted">
|
</a>
|
||||||
<a
|
</span>
|
||||||
className="text-muted font-italic"
|
</>
|
||||||
href={url}
|
)}
|
||||||
rel={relTags}
|
{post.embed_description && (
|
||||||
>
|
<div
|
||||||
{new URL(url).hostname}
|
className="card-text small text-muted md-div"
|
||||||
<Icon icon="external-link" classes="ml-1" />
|
dangerouslySetInnerHTML={{
|
||||||
</a>
|
__html: sanitizeHtml(post.embed_description),
|
||||||
</span>
|
}}
|
||||||
</>
|
/>
|
||||||
)}
|
)}
|
||||||
{post.embed_description.match({
|
{post.embed_video_url && (
|
||||||
some: desc => (
|
<button
|
||||||
<div
|
className="mt-2 btn btn-secondary text-monospace"
|
||||||
className="card-text small text-muted md-div"
|
onClick={linkEvent(this, this.handleIframeExpand)}
|
||||||
dangerouslySetInnerHTML={{
|
>
|
||||||
__html: sanitizeHtml(desc),
|
{i18n.t("expand_here")}
|
||||||
}}
|
</button>
|
||||||
/>
|
)}
|
||||||
),
|
</div>
|
||||||
none: <></>,
|
</div>
|
||||||
})}
|
</div>
|
||||||
{post.embed_video_url.isSome() && (
|
</div>
|
||||||
<button
|
)}
|
||||||
className="mt-2 btn btn-secondary text-monospace"
|
{this.state.expanded && post.embed_video_url && (
|
||||||
onClick={linkEvent(this, this.handleIframeExpand)}
|
<iframe src={post.embed_video_url}></iframe>
|
||||||
>
|
)}
|
||||||
{i18n.t("expand_here")}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
}),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
{this.state.expanded &&
|
|
||||||
post.embed_video_url.match({
|
|
||||||
some: video_url => <iframe src={video_url}></iframe>,
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import autosize from "autosize";
|
import autosize from "autosize";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { Prompt } from "inferno-router";
|
import { Prompt } from "inferno-router";
|
||||||
|
@ -14,7 +13,6 @@ import {
|
||||||
SearchResponse,
|
SearchResponse,
|
||||||
SearchType,
|
SearchType,
|
||||||
SortType,
|
SortType,
|
||||||
toUndefined,
|
|
||||||
UserOperation,
|
UserOperation,
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
wsUserOp,
|
wsUserOp,
|
||||||
|
@ -26,7 +24,6 @@ import { PostFormParams } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
archiveTodayUrl,
|
archiveTodayUrl,
|
||||||
auth,
|
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
choicesConfig,
|
choicesConfig,
|
||||||
communitySelectName,
|
communitySelectName,
|
||||||
|
@ -37,6 +34,7 @@ import {
|
||||||
ghostArchiveUrl,
|
ghostArchiveUrl,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
isImage,
|
isImage,
|
||||||
|
myAuth,
|
||||||
myFirstDiscussionLanguageId,
|
myFirstDiscussionLanguageId,
|
||||||
pictrsDeleteToast,
|
pictrsDeleteToast,
|
||||||
relTags,
|
relTags,
|
||||||
|
@ -62,11 +60,11 @@ if (isBrowser()) {
|
||||||
const MAX_POST_TITLE_LENGTH = 200;
|
const MAX_POST_TITLE_LENGTH = 200;
|
||||||
|
|
||||||
interface PostFormProps {
|
interface PostFormProps {
|
||||||
post_view: Option<PostView>; // If a post is given, that means this is an edit
|
post_view?: PostView; // If a post is given, that means this is an edit
|
||||||
allLanguages: Language[];
|
allLanguages: Language[];
|
||||||
siteLanguages: number[];
|
siteLanguages: number[];
|
||||||
communities: Option<CommunityView[]>;
|
communities?: CommunityView[];
|
||||||
params: Option<PostFormParams>;
|
params?: PostFormParams;
|
||||||
onCancel?(): any;
|
onCancel?(): any;
|
||||||
onCreate?(post: PostView): any;
|
onCreate?(post: PostView): any;
|
||||||
onEdit?(post: PostView): any;
|
onEdit?(post: PostView): any;
|
||||||
|
@ -75,10 +73,18 @@ interface PostFormProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PostFormState {
|
interface PostFormState {
|
||||||
postForm: CreatePost;
|
form: {
|
||||||
suggestedTitle: Option<string>;
|
name?: string;
|
||||||
suggestedPosts: Option<PostView[]>;
|
url?: string;
|
||||||
crossPosts: Option<PostView[]>;
|
body?: string;
|
||||||
|
nsfw?: boolean;
|
||||||
|
language_id?: number;
|
||||||
|
community_id?: number;
|
||||||
|
honeypot?: string;
|
||||||
|
};
|
||||||
|
suggestedTitle?: string;
|
||||||
|
suggestedPosts?: PostView[];
|
||||||
|
crossPosts?: PostView[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
imageLoading: boolean;
|
imageLoading: boolean;
|
||||||
communitySearchLoading: boolean;
|
communitySearchLoading: boolean;
|
||||||
|
@ -86,26 +92,14 @@ interface PostFormState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PostForm extends Component<PostFormProps, PostFormState> {
|
export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
private choices: any;
|
private choices: any;
|
||||||
private emptyState: PostFormState = {
|
state: PostFormState = {
|
||||||
postForm: new CreatePost({
|
form: {},
|
||||||
community_id: undefined,
|
|
||||||
name: undefined,
|
|
||||||
nsfw: Some(false),
|
|
||||||
url: None,
|
|
||||||
body: None,
|
|
||||||
honeypot: None,
|
|
||||||
language_id: None,
|
|
||||||
auth: undefined,
|
|
||||||
}),
|
|
||||||
loading: false,
|
loading: false,
|
||||||
imageLoading: false,
|
imageLoading: false,
|
||||||
communitySearchLoading: false,
|
communitySearchLoading: false,
|
||||||
previewMode: false,
|
previewMode: false,
|
||||||
suggestedTitle: None,
|
|
||||||
suggestedPosts: None,
|
|
||||||
crossPosts: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -115,37 +109,32 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
this.handlePostBodyChange = this.handlePostBodyChange.bind(this);
|
this.handlePostBodyChange = this.handlePostBodyChange.bind(this);
|
||||||
this.handleLanguageChange = this.handleLanguageChange.bind(this);
|
this.handleLanguageChange = this.handleLanguageChange.bind(this);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
// Means its an edit
|
// Means its an edit
|
||||||
if (this.props.post_view.isSome()) {
|
let pv = this.props.post_view;
|
||||||
let pv = this.props.post_view.unwrap();
|
if (pv) {
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
postForm: new CreatePost({
|
form: {
|
||||||
body: pv.post.body,
|
body: pv.post.body,
|
||||||
name: pv.post.name,
|
name: pv.post.name,
|
||||||
community_id: pv.community.id,
|
community_id: pv.community.id,
|
||||||
url: pv.post.url,
|
url: pv.post.url,
|
||||||
nsfw: Some(pv.post.nsfw),
|
nsfw: pv.post.nsfw,
|
||||||
honeypot: None,
|
language_id: pv.post.language_id,
|
||||||
language_id: Some(pv.post.language_id),
|
},
|
||||||
auth: auth().unwrap(),
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.params.isSome()) {
|
let params = this.props.params;
|
||||||
let params = this.props.params.unwrap();
|
if (params) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
postForm: {
|
form: {
|
||||||
...this.state.postForm,
|
...this.state.form,
|
||||||
name: toUndefined(params.name),
|
name: params.name,
|
||||||
url: params.url,
|
url: params.url,
|
||||||
body: params.body,
|
body: params.body,
|
||||||
},
|
},
|
||||||
|
@ -165,41 +154,39 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if (
|
if (
|
||||||
!this.state.loading &&
|
!this.state.loading &&
|
||||||
(this.state.postForm.name ||
|
(this.state.form.name || this.state.form.url || this.state.form.body)
|
||||||
this.state.postForm.url.isSome() ||
|
|
||||||
this.state.postForm.body.isSome())
|
|
||||||
) {
|
) {
|
||||||
window.onbeforeunload = () => true;
|
window.onbeforeunload = () => true;
|
||||||
} else {
|
} else {
|
||||||
window.onbeforeunload = undefined;
|
window.onbeforeunload = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
/* this.choices && this.choices.destroy(); */
|
/* this.choices && this.choices.destroy(); */
|
||||||
window.onbeforeunload = null;
|
window.onbeforeunload = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let selectedLangs = this.state.postForm.language_id
|
let firstLang =
|
||||||
.or(
|
this.state.form.language_id ??
|
||||||
myFirstDiscussionLanguageId(
|
myFirstDiscussionLanguageId(
|
||||||
this.props.allLanguages,
|
this.props.allLanguages,
|
||||||
this.props.siteLanguages,
|
this.props.siteLanguages,
|
||||||
UserService.Instance.myUserInfo
|
UserService.Instance.myUserInfo
|
||||||
)
|
);
|
||||||
)
|
let selectedLangs = firstLang ? Array.of(firstLang) : undefined;
|
||||||
.map(Array.of);
|
|
||||||
|
|
||||||
|
let url = this.state.form.url;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Prompt
|
<Prompt
|
||||||
when={
|
when={
|
||||||
!this.state.loading &&
|
!this.state.loading &&
|
||||||
(this.state.postForm.name ||
|
(this.state.form.name ||
|
||||||
this.state.postForm.url.isSome() ||
|
this.state.form.url ||
|
||||||
this.state.postForm.body.isSome())
|
this.state.form.body)
|
||||||
}
|
}
|
||||||
message={i18n.t("block_leaving")}
|
message={i18n.t("block_leaving")}
|
||||||
/>
|
/>
|
||||||
|
@ -213,27 +200,25 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
type="url"
|
type="url"
|
||||||
id="post-url"
|
id="post-url"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
value={toUndefined(this.state.postForm.url)}
|
value={this.state.form.url}
|
||||||
onInput={linkEvent(this, this.handlePostUrlChange)}
|
onInput={linkEvent(this, this.handlePostUrlChange)}
|
||||||
onPaste={linkEvent(this, this.handleImageUploadPaste)}
|
onPaste={linkEvent(this, this.handleImageUploadPaste)}
|
||||||
/>
|
/>
|
||||||
{this.state.suggestedTitle.match({
|
{this.state.suggestedTitle && (
|
||||||
some: title => (
|
<div
|
||||||
<div
|
className="mt-1 text-muted small font-weight-bold pointer"
|
||||||
className="mt-1 text-muted small font-weight-bold pointer"
|
role="button"
|
||||||
role="button"
|
onClick={linkEvent(this, this.copySuggestedTitle)}
|
||||||
onClick={linkEvent(this, this.copySuggestedTitle)}
|
>
|
||||||
>
|
{i18n.t("copy_suggested_title", { title: "" })}{" "}
|
||||||
{i18n.t("copy_suggested_title", { title: "" })} {title}
|
{this.state.suggestedTitle}
|
||||||
</div>
|
</div>
|
||||||
),
|
)}
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
<form>
|
<form>
|
||||||
<label
|
<label
|
||||||
htmlFor="file-upload"
|
htmlFor="file-upload"
|
||||||
className={`${
|
className={`${
|
||||||
UserService.Instance.myUserInfo.isSome() && "pointer"
|
UserService.Instance.myUserInfo && "pointer"
|
||||||
} d-inline-block float-right text-muted font-weight-bold`}
|
} d-inline-block float-right text-muted font-weight-bold`}
|
||||||
data-tippy-content={i18n.t("upload_image")}
|
data-tippy-content={i18n.t("upload_image")}
|
||||||
>
|
>
|
||||||
|
@ -245,72 +230,58 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
accept="image/*,video/*"
|
accept="image/*,video/*"
|
||||||
name="file"
|
name="file"
|
||||||
className="d-none"
|
className="d-none"
|
||||||
disabled={UserService.Instance.myUserInfo.isNone()}
|
disabled={!UserService.Instance.myUserInfo}
|
||||||
onChange={linkEvent(this, this.handleImageUpload)}
|
onChange={linkEvent(this, this.handleImageUpload)}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
{this.state.postForm.url.match({
|
{url && validURL(url) && (
|
||||||
some: url =>
|
<div>
|
||||||
validURL(url) && (
|
<a
|
||||||
<div>
|
href={`${webArchiveUrl}/save/${encodeURIComponent(url)}`}
|
||||||
<a
|
className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
||||||
href={`${webArchiveUrl}/save/${encodeURIComponent(
|
rel={relTags}
|
||||||
url
|
>
|
||||||
)}`}
|
archive.org {i18n.t("archive_link")}
|
||||||
className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
</a>
|
||||||
rel={relTags}
|
<a
|
||||||
>
|
href={`${ghostArchiveUrl}/search?term=${encodeURIComponent(
|
||||||
archive.org {i18n.t("archive_link")}
|
url
|
||||||
</a>
|
)}`}
|
||||||
<a
|
className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
||||||
href={`${ghostArchiveUrl}/search?term=${encodeURIComponent(
|
rel={relTags}
|
||||||
url
|
>
|
||||||
)}`}
|
ghostarchive.org {i18n.t("archive_link")}
|
||||||
className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
</a>
|
||||||
rel={relTags}
|
<a
|
||||||
>
|
href={`${archiveTodayUrl}/?run=1&url=${encodeURIComponent(
|
||||||
ghostarchive.org {i18n.t("archive_link")}
|
url
|
||||||
</a>
|
)}`}
|
||||||
<a
|
className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
||||||
href={`${archiveTodayUrl}/?run=1&url=${encodeURIComponent(
|
rel={relTags}
|
||||||
url
|
>
|
||||||
)}`}
|
archive.today {i18n.t("archive_link")}
|
||||||
className="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
</a>
|
||||||
rel={relTags}
|
</div>
|
||||||
>
|
)}
|
||||||
archive.today {i18n.t("archive_link")}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
{this.state.imageLoading && <Spinner />}
|
{this.state.imageLoading && <Spinner />}
|
||||||
{this.state.postForm.url.match({
|
{url && isImage(url) && (
|
||||||
some: url =>
|
<img src={url} className="img-fluid" alt="" />
|
||||||
isImage(url) && (
|
)}
|
||||||
<img src={url} className="img-fluid" alt="" />
|
{this.state.crossPosts && this.state.crossPosts.length > 0 && (
|
||||||
),
|
<>
|
||||||
none: <></>,
|
<div className="my-1 text-muted small font-weight-bold">
|
||||||
})}
|
{i18n.t("cross_posts")}
|
||||||
{this.state.crossPosts.match({
|
</div>
|
||||||
some: xPosts =>
|
<PostListings
|
||||||
xPosts.length > 0 && (
|
showCommunity
|
||||||
<>
|
posts={this.state.crossPosts}
|
||||||
<div className="my-1 text-muted small font-weight-bold">
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
{i18n.t("cross_posts")}
|
enableNsfw={this.props.enableNsfw}
|
||||||
</div>
|
allLanguages={this.props.allLanguages}
|
||||||
<PostListings
|
siteLanguages={this.props.siteLanguages}
|
||||||
showCommunity
|
/>
|
||||||
posts={xPosts}
|
</>
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
)}
|
||||||
enableNsfw={this.props.enableNsfw}
|
|
||||||
allLanguages={this.props.allLanguages}
|
|
||||||
siteLanguages={this.props.siteLanguages}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="form-group row">
|
<div className="form-group row">
|
||||||
|
@ -319,41 +290,38 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
</label>
|
</label>
|
||||||
<div className="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<textarea
|
<textarea
|
||||||
value={this.state.postForm.name}
|
value={this.state.form.name}
|
||||||
id="post-title"
|
id="post-title"
|
||||||
onInput={linkEvent(this, this.handlePostNameChange)}
|
onInput={linkEvent(this, this.handlePostNameChange)}
|
||||||
className={`form-control ${
|
className={`form-control ${
|
||||||
!validTitle(this.state.postForm.name) && "is-invalid"
|
!validTitle(this.state.form.name) && "is-invalid"
|
||||||
}`}
|
}`}
|
||||||
required
|
required
|
||||||
rows={1}
|
rows={1}
|
||||||
minLength={3}
|
minLength={3}
|
||||||
maxLength={MAX_POST_TITLE_LENGTH}
|
maxLength={MAX_POST_TITLE_LENGTH}
|
||||||
/>
|
/>
|
||||||
{!validTitle(this.state.postForm.name) && (
|
{!validTitle(this.state.form.name) && (
|
||||||
<div className="invalid-feedback">
|
<div className="invalid-feedback">
|
||||||
{i18n.t("invalid_post_title")}
|
{i18n.t("invalid_post_title")}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{this.state.suggestedPosts.match({
|
{this.state.suggestedPosts &&
|
||||||
some: sPosts =>
|
this.state.suggestedPosts.length > 0 && (
|
||||||
sPosts.length > 0 && (
|
<>
|
||||||
<>
|
<div className="my-1 text-muted small font-weight-bold">
|
||||||
<div className="my-1 text-muted small font-weight-bold">
|
{i18n.t("related_posts")}
|
||||||
{i18n.t("related_posts")}
|
</div>
|
||||||
</div>
|
<PostListings
|
||||||
<PostListings
|
showCommunity
|
||||||
showCommunity
|
posts={this.state.suggestedPosts}
|
||||||
posts={sPosts}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableNsfw={this.props.enableNsfw}
|
||||||
enableNsfw={this.props.enableNsfw}
|
allLanguages={this.props.allLanguages}
|
||||||
allLanguages={this.props.allLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
siteLanguages={this.props.siteLanguages}
|
/>
|
||||||
/>
|
</>
|
||||||
</>
|
)}
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -361,18 +329,14 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
<label className="col-sm-2 col-form-label">{i18n.t("body")}</label>
|
<label className="col-sm-2 col-form-label">{i18n.t("body")}</label>
|
||||||
<div className="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.postForm.body}
|
initialContent={this.state.form.body}
|
||||||
initialLanguageId={None}
|
|
||||||
onContentChange={this.handlePostBodyChange}
|
onContentChange={this.handlePostBodyChange}
|
||||||
placeholder={None}
|
|
||||||
buttonTitle={None}
|
|
||||||
maxLength={None}
|
|
||||||
allLanguages={this.props.allLanguages}
|
allLanguages={this.props.allLanguages}
|
||||||
siteLanguages={this.props.siteLanguages}
|
siteLanguages={this.props.siteLanguages}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{this.props.post_view.isNone() && (
|
{!this.props.post_view && (
|
||||||
<div className="form-group row">
|
<div className="form-group row">
|
||||||
<label
|
<label
|
||||||
className="col-sm-2 col-form-label"
|
className="col-sm-2 col-form-label"
|
||||||
|
@ -388,11 +352,11 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
<select
|
<select
|
||||||
className="form-control"
|
className="form-control"
|
||||||
id="post-community"
|
id="post-community"
|
||||||
value={this.state.postForm.community_id}
|
value={this.state.form.community_id}
|
||||||
onInput={linkEvent(this, this.handlePostCommunityChange)}
|
onInput={linkEvent(this, this.handlePostCommunityChange)}
|
||||||
>
|
>
|
||||||
<option>{i18n.t("select_a_community")}</option>
|
<option>{i18n.t("select_a_community")}</option>
|
||||||
{this.props.communities.unwrapOr([]).map(cv => (
|
{this.props.communities?.map(cv => (
|
||||||
<option key={cv.community.id} value={cv.community.id}>
|
<option key={cv.community.id} value={cv.community.id}>
|
||||||
{communitySelectName(cv)}
|
{communitySelectName(cv)}
|
||||||
</option>
|
</option>
|
||||||
|
@ -412,7 +376,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
className="form-check-input position-static"
|
className="form-check-input position-static"
|
||||||
id="post-nsfw"
|
id="post-nsfw"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={toUndefined(this.state.postForm.nsfw)}
|
checked={this.state.form.nsfw}
|
||||||
onChange={linkEvent(this, this.handlePostNsfwChange)}
|
onChange={linkEvent(this, this.handlePostNsfwChange)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -433,27 +397,25 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control honeypot"
|
className="form-control honeypot"
|
||||||
id="register-honey"
|
id="register-honey"
|
||||||
value={toUndefined(this.state.postForm.honeypot)}
|
value={this.state.form.honeypot}
|
||||||
onInput={linkEvent(this, this.handleHoneyPotChange)}
|
onInput={linkEvent(this, this.handleHoneyPotChange)}
|
||||||
/>
|
/>
|
||||||
<div className="form-group row">
|
<div className="form-group row">
|
||||||
<div className="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<button
|
<button
|
||||||
disabled={
|
disabled={!this.state.form.community_id || this.state.loading}
|
||||||
!this.state.postForm.community_id || this.state.loading
|
|
||||||
}
|
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn btn-secondary mr-2"
|
className="btn btn-secondary mr-2"
|
||||||
>
|
>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : this.props.post_view.isSome() ? (
|
) : this.props.post_view ? (
|
||||||
capitalizeFirstLetter(i18n.t("save"))
|
capitalizeFirstLetter(i18n.t("save"))
|
||||||
) : (
|
) : (
|
||||||
capitalizeFirstLetter(i18n.t("create"))
|
capitalizeFirstLetter(i18n.t("create"))
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
{this.props.post_view.isSome() && (
|
{this.props.post_view && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
|
@ -475,139 +437,133 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
i.setState({ loading: true });
|
i.setState({ loading: true });
|
||||||
|
|
||||||
// Coerce empty url string to undefined
|
// Coerce empty url string to undefined
|
||||||
if (
|
if ((i.state.form.url ?? "blank") === "") {
|
||||||
i.state.postForm.url.isSome() &&
|
i.setState(s => ((s.form.url = undefined), s));
|
||||||
i.state.postForm.url.unwrapOr("blank") === ""
|
|
||||||
) {
|
|
||||||
i.setState(s => ((s.postForm.url = None), s));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let pForm = i.state.postForm;
|
let pForm = i.state.form;
|
||||||
i.props.post_view.match({
|
let pv = i.props.post_view;
|
||||||
some: pv => {
|
let auth = myAuth();
|
||||||
let form = new EditPost({
|
if (auth) {
|
||||||
name: Some(pForm.name),
|
if (pv) {
|
||||||
|
let form: EditPost = {
|
||||||
|
name: pForm.name,
|
||||||
url: pForm.url,
|
url: pForm.url,
|
||||||
body: pForm.body,
|
body: pForm.body,
|
||||||
nsfw: pForm.nsfw,
|
nsfw: pForm.nsfw,
|
||||||
post_id: pv.post.id,
|
post_id: pv.post.id,
|
||||||
language_id: Some(pv.post.language_id),
|
language_id: pv.post.language_id,
|
||||||
auth: auth().unwrap(),
|
auth,
|
||||||
});
|
};
|
||||||
WebSocketService.Instance.send(wsClient.editPost(form));
|
WebSocketService.Instance.send(wsClient.editPost(form));
|
||||||
},
|
} else {
|
||||||
none: () => {
|
if (pForm.name && pForm.community_id) {
|
||||||
i.setState(s => ((s.postForm.auth = auth().unwrap()), s));
|
let form: CreatePost = {
|
||||||
let form = new CreatePost({ ...i.state.postForm });
|
name: pForm.name,
|
||||||
WebSocketService.Instance.send(wsClient.createPost(form));
|
community_id: pForm.community_id,
|
||||||
},
|
url: pForm.url,
|
||||||
});
|
body: pForm.body,
|
||||||
|
nsfw: pForm.nsfw,
|
||||||
|
language_id: pForm.language_id,
|
||||||
|
honeypot: pForm.honeypot,
|
||||||
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.createPost(form));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
copySuggestedTitle(i: PostForm) {
|
copySuggestedTitle(i: PostForm) {
|
||||||
i.state.suggestedTitle.match({
|
let sTitle = i.state.suggestedTitle;
|
||||||
some: sTitle => {
|
if (sTitle) {
|
||||||
i.setState(
|
i.setState(
|
||||||
s => (
|
s => ((s.form.name = sTitle?.substring(0, MAX_POST_TITLE_LENGTH)), s)
|
||||||
(s.postForm.name = sTitle.substring(0, MAX_POST_TITLE_LENGTH)), s
|
);
|
||||||
)
|
i.setState({ suggestedTitle: undefined });
|
||||||
);
|
setTimeout(() => {
|
||||||
i.setState({ suggestedTitle: None });
|
let textarea: any = document.getElementById("post-title");
|
||||||
setTimeout(() => {
|
autosize.update(textarea);
|
||||||
let textarea: any = document.getElementById("post-title");
|
}, 10);
|
||||||
autosize.update(textarea);
|
}
|
||||||
}, 10);
|
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostUrlChange(i: PostForm, event: any) {
|
handlePostUrlChange(i: PostForm, event: any) {
|
||||||
i.setState(s => ((s.postForm.url = Some(event.target.value)), s));
|
i.setState(s => ((s.form.url = event.target.value), s));
|
||||||
i.fetchPageTitle();
|
i.fetchPageTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchPageTitle() {
|
fetchPageTitle() {
|
||||||
this.state.postForm.url.match({
|
let url = this.state.form.url;
|
||||||
some: url => {
|
if (url && validURL(url)) {
|
||||||
if (validURL(url)) {
|
let form: Search = {
|
||||||
let form = new Search({
|
q: url,
|
||||||
q: url,
|
type_: SearchType.Url,
|
||||||
community_id: None,
|
sort: SortType.TopAll,
|
||||||
community_name: None,
|
listing_type: ListingType.All,
|
||||||
creator_id: None,
|
page: 1,
|
||||||
type_: Some(SearchType.Url),
|
limit: trendingFetchLimit,
|
||||||
sort: Some(SortType.TopAll),
|
auth: myAuth(false),
|
||||||
listing_type: Some(ListingType.All),
|
};
|
||||||
page: Some(1),
|
|
||||||
limit: Some(trendingFetchLimit),
|
|
||||||
auth: auth(false).ok(),
|
|
||||||
});
|
|
||||||
|
|
||||||
WebSocketService.Instance.send(wsClient.search(form));
|
WebSocketService.Instance.send(wsClient.search(form));
|
||||||
|
|
||||||
// Fetch the page title
|
// Fetch the page title
|
||||||
getSiteMetadata(url).then(d => {
|
getSiteMetadata(url).then(d => {
|
||||||
this.setState({ suggestedTitle: d.metadata.title });
|
this.setState({ suggestedTitle: d.metadata.title });
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.setState({ suggestedTitle: None, crossPosts: None });
|
this.setState({ suggestedTitle: undefined, crossPosts: undefined });
|
||||||
}
|
}
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostNameChange(i: PostForm, event: any) {
|
handlePostNameChange(i: PostForm, event: any) {
|
||||||
i.setState(s => ((s.postForm.name = event.target.value), s));
|
i.setState(s => ((s.form.name = event.target.value), s));
|
||||||
i.fetchSimilarPosts();
|
i.fetchSimilarPosts();
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchSimilarPosts() {
|
fetchSimilarPosts() {
|
||||||
let form = new Search({
|
let q = this.state.form.name;
|
||||||
q: this.state.postForm.name,
|
if (q && q !== "") {
|
||||||
type_: Some(SearchType.Posts),
|
let form: Search = {
|
||||||
sort: Some(SortType.TopAll),
|
q,
|
||||||
listing_type: Some(ListingType.All),
|
type_: SearchType.Posts,
|
||||||
community_id: Some(this.state.postForm.community_id),
|
sort: SortType.TopAll,
|
||||||
community_name: None,
|
listing_type: ListingType.All,
|
||||||
creator_id: None,
|
community_id: this.state.form.community_id,
|
||||||
page: Some(1),
|
page: 1,
|
||||||
limit: Some(trendingFetchLimit),
|
limit: trendingFetchLimit,
|
||||||
auth: auth(false).ok(),
|
auth: myAuth(false),
|
||||||
});
|
};
|
||||||
|
|
||||||
if (this.state.postForm.name !== "") {
|
|
||||||
WebSocketService.Instance.send(wsClient.search(form));
|
WebSocketService.Instance.send(wsClient.search(form));
|
||||||
} else {
|
} else {
|
||||||
this.setState({ suggestedPosts: None });
|
this.setState({ suggestedPosts: undefined });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostBodyChange(val: string) {
|
handlePostBodyChange(val: string) {
|
||||||
this.setState(s => ((s.postForm.body = Some(val)), s));
|
this.setState(s => ((s.form.body = val), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostCommunityChange(i: PostForm, event: any) {
|
handlePostCommunityChange(i: PostForm, event: any) {
|
||||||
i.setState(
|
i.setState(s => ((s.form.community_id = Number(event.target.value)), s));
|
||||||
s => ((s.postForm.community_id = Number(event.target.value)), s)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostNsfwChange(i: PostForm, event: any) {
|
handlePostNsfwChange(i: PostForm, event: any) {
|
||||||
i.setState(s => ((s.postForm.nsfw = Some(event.target.checked)), s));
|
i.setState(s => ((s.form.nsfw = event.target.checked), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLanguageChange(val: number[]) {
|
handleLanguageChange(val: number[]) {
|
||||||
this.setState(s => ((s.postForm.language_id = Some(val[0])), s));
|
this.setState(s => ((s.form.language_id = val.at(0)), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleHoneyPotChange(i: PostForm, event: any) {
|
handleHoneyPotChange(i: PostForm, event: any) {
|
||||||
i.setState(s => ((s.postForm.honeypot = Some(event.target.value)), s));
|
i.setState(s => ((s.form.honeypot = event.target.value), s));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancel(i: PostForm) {
|
handleCancel(i: PostForm) {
|
||||||
i.props.onCancel();
|
i.props.onCancel?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePreviewToggle(i: PostForm, event: any) {
|
handlePreviewToggle(i: PostForm, event: any) {
|
||||||
|
@ -649,7 +605,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
let url = `${pictrsUri}/${hash}`;
|
let url = `${pictrsUri}/${hash}`;
|
||||||
let deleteToken = res.files[0].delete_token;
|
let deleteToken = res.files[0].delete_token;
|
||||||
let deleteUrl = `${pictrsUri}/delete/${deleteToken}/${hash}`;
|
let deleteUrl = `${pictrsUri}/delete/${deleteToken}/${hash}`;
|
||||||
i.state.postForm.url = Some(url);
|
i.state.form.url = url;
|
||||||
i.setState({ imageLoading: false });
|
i.setState({ imageLoading: false });
|
||||||
pictrsDeleteToast(
|
pictrsDeleteToast(
|
||||||
`${i18n.t("click_to_delete_picture")}: ${file.name}`,
|
`${i18n.t("click_to_delete_picture")}: ${file.name}`,
|
||||||
|
@ -679,9 +635,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
"choice",
|
"choice",
|
||||||
(e: any) => {
|
(e: any) => {
|
||||||
this.setState(
|
this.setState(
|
||||||
s => (
|
s => ((s.form.community_id = Number(e.detail.choice.value)), s)
|
||||||
(s.postForm.community_id = Number(e.detail.choice.value)), s
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
|
@ -711,41 +665,31 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.post_view.match({
|
let pv = this.props.post_view;
|
||||||
some: pv =>
|
this.setState(s => ((s.form.community_id = pv?.community.id), s));
|
||||||
this.setState(s => ((s.postForm.community_id = pv.community.id), s)),
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
this.props.params.match({
|
|
||||||
some: params =>
|
|
||||||
params.nameOrId.match({
|
|
||||||
some: nameOrId =>
|
|
||||||
nameOrId.match({
|
|
||||||
left: name => {
|
|
||||||
let foundCommunityId = this.props.communities
|
|
||||||
.unwrapOr([])
|
|
||||||
.find(r => r.community.name == name).community.id;
|
|
||||||
this.setState(
|
|
||||||
s => ((s.postForm.community_id = foundCommunityId), s)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
right: id =>
|
|
||||||
this.setState(s => ((s.postForm.community_id = id), s)),
|
|
||||||
}),
|
|
||||||
none: void 0,
|
|
||||||
}),
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isBrowser() && this.state.postForm.community_id) {
|
let nameOrId = this.props.params?.nameOrId;
|
||||||
this.choices.setChoiceByValue(
|
if (nameOrId) {
|
||||||
this.state.postForm.community_id.toString()
|
if (typeof nameOrId === "string") {
|
||||||
);
|
let name_ = nameOrId;
|
||||||
|
let foundCommunityId = this.props.communities?.find(
|
||||||
|
r => r.community.name == name_
|
||||||
|
)?.community.id;
|
||||||
|
this.setState(s => ((s.form.community_id = foundCommunityId), s));
|
||||||
|
} else {
|
||||||
|
let id = nameOrId;
|
||||||
|
this.setState(s => ((s.form.community_id = id), s));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBrowser() && this.state.form.community_id) {
|
||||||
|
this.choices.setChoiceByValue(this.state.form.community_id.toString());
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
let mui = UserService.Instance.myUserInfo;
|
||||||
let op = wsUserOp(msg);
|
let op = wsUserOp(msg);
|
||||||
console.log(msg);
|
console.log(msg);
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
|
@ -754,33 +698,23 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.CreatePost) {
|
} else if (op == UserOperation.CreatePost) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
let data = wsJsonToRes<PostResponse>(msg);
|
||||||
UserService.Instance.myUserInfo.match({
|
if (data.post_view.creator.id == mui?.local_user_view.person.id) {
|
||||||
some: mui => {
|
this.props.onCreate?.(data.post_view);
|
||||||
if (data.post_view.creator.id == mui.local_user_view.person.id) {
|
}
|
||||||
this.props.onCreate(data.post_view);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
} else if (op == UserOperation.EditPost) {
|
} else if (op == UserOperation.EditPost) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
let data = wsJsonToRes<PostResponse>(msg);
|
||||||
UserService.Instance.myUserInfo.match({
|
if (data.post_view.creator.id == mui?.local_user_view.person.id) {
|
||||||
some: mui => {
|
this.setState({ loading: false });
|
||||||
if (data.post_view.creator.id == mui.local_user_view.person.id) {
|
this.props.onEdit?.(data.post_view);
|
||||||
this.setState({ loading: false });
|
}
|
||||||
this.props.onEdit(data.post_view);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
} else if (op == UserOperation.Search) {
|
} else if (op == UserOperation.Search) {
|
||||||
let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
|
let data = wsJsonToRes<SearchResponse>(msg);
|
||||||
|
|
||||||
if (data.type_ == SearchType[SearchType.Posts]) {
|
if (data.type_ == SearchType[SearchType.Posts]) {
|
||||||
this.setState({ suggestedPosts: Some(data.posts) });
|
this.setState({ suggestedPosts: data.posts });
|
||||||
} else if (data.type_ == SearchType[SearchType.Url]) {
|
} else if (data.type_ == SearchType[SearchType.Url]) {
|
||||||
this.setState({ crossPosts: Some(data.posts) });
|
this.setState({ crossPosts: data.posts });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,3 @@
|
||||||
import { None, Some } from "@sniptt/monads";
|
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import { T } from "inferno-i18next-dess";
|
import { T } from "inferno-i18next-dess";
|
||||||
import { Link } from "inferno-router";
|
import { Link } from "inferno-router";
|
||||||
|
@ -12,8 +11,8 @@ interface PostListingsProps {
|
||||||
siteLanguages: number[];
|
siteLanguages: number[];
|
||||||
showCommunity?: boolean;
|
showCommunity?: boolean;
|
||||||
removeDuplicates?: boolean;
|
removeDuplicates?: boolean;
|
||||||
enableDownvotes: boolean;
|
enableDownvotes?: boolean;
|
||||||
enableNsfw: boolean;
|
enableNsfw?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PostListings extends Component<PostListingsProps, any> {
|
export class PostListings extends Component<PostListingsProps, any> {
|
||||||
|
@ -37,9 +36,7 @@ export class PostListings extends Component<PostListingsProps, any> {
|
||||||
<>
|
<>
|
||||||
<PostListing
|
<PostListing
|
||||||
post_view={post_view}
|
post_view={post_view}
|
||||||
duplicates={Some(this.duplicatesMap.get(post_view.post.id))}
|
duplicates={this.duplicatesMap.get(post_view.post.id)}
|
||||||
moderators={None}
|
|
||||||
admins={None}
|
|
||||||
showCommunity={this.props.showCommunity}
|
showCommunity={this.props.showCommunity}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
|
@ -72,20 +69,20 @@ export class PostListings extends Component<PostListingsProps, any> {
|
||||||
|
|
||||||
// Loop over the posts, find ones with same urls
|
// Loop over the posts, find ones with same urls
|
||||||
for (let pv of posts) {
|
for (let pv of posts) {
|
||||||
!pv.post.deleted &&
|
let url = pv.post.url;
|
||||||
|
if (
|
||||||
|
!pv.post.deleted &&
|
||||||
!pv.post.removed &&
|
!pv.post.removed &&
|
||||||
!pv.community.deleted &&
|
!pv.community.deleted &&
|
||||||
!pv.community.removed &&
|
!pv.community.removed &&
|
||||||
pv.post.url.match({
|
url
|
||||||
some: url => {
|
) {
|
||||||
if (!urlMap.get(url)) {
|
if (!urlMap.get(url)) {
|
||||||
urlMap.set(url, [pv]);
|
urlMap.set(url, [pv]);
|
||||||
} else {
|
} else {
|
||||||
urlMap.get(url).push(pv);
|
urlMap.get(url)?.push(pv);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by oldest
|
// Sort by oldest
|
||||||
|
@ -100,22 +97,20 @@ export class PostListings extends Component<PostListingsProps, any> {
|
||||||
|
|
||||||
for (let i = 0; i < posts.length; i++) {
|
for (let i = 0; i < posts.length; i++) {
|
||||||
let pv = posts[i];
|
let pv = posts[i];
|
||||||
pv.post.url.match({
|
let url = pv.post.url;
|
||||||
some: url => {
|
if (url) {
|
||||||
let found = urlMap.get(url);
|
let found = urlMap.get(url);
|
||||||
if (found) {
|
if (found) {
|
||||||
// If its the oldest, add
|
// If its the oldest, add
|
||||||
if (pv.post.id == found[0].post.id) {
|
if (pv.post.id == found[0].post.id) {
|
||||||
this.duplicatesMap.set(pv.post.id, found.slice(1));
|
this.duplicatesMap.set(pv.post.id, found.slice(1));
|
||||||
}
|
|
||||||
// Otherwise, delete it
|
|
||||||
else {
|
|
||||||
posts.splice(i--, 1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
// Otherwise, delete it
|
||||||
none: void 0,
|
else {
|
||||||
});
|
posts.splice(i--, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return posts;
|
return posts;
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { T } from "inferno-i18next-dess";
|
import { T } from "inferno-i18next-dess";
|
||||||
import {
|
import {
|
||||||
|
@ -9,7 +8,7 @@ import {
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { WebSocketService } from "../../services";
|
import { WebSocketService } from "../../services";
|
||||||
import { auth, wsClient } from "../../utils";
|
import { myAuth, wsClient } from "../../utils";
|
||||||
import { Icon } from "../common/icon";
|
import { Icon } from "../common/icon";
|
||||||
import { PersonListing } from "../person/person-listing";
|
import { PersonListing } from "../person/person-listing";
|
||||||
import { PostListing } from "./post-listing";
|
import { PostListing } from "./post-listing";
|
||||||
|
@ -25,6 +24,7 @@ export class PostReport extends Component<PostReportProps, any> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let r = this.props.report;
|
let r = this.props.report;
|
||||||
|
let resolver = r.resolver;
|
||||||
let post = r.post;
|
let post = r.post;
|
||||||
let tippyContent = i18n.t(
|
let tippyContent = i18n.t(
|
||||||
r.post_report.resolved ? "unresolve_report" : "resolve_report"
|
r.post_report.resolved ? "unresolve_report" : "resolve_report"
|
||||||
|
@ -52,9 +52,6 @@ export class PostReport extends Component<PostReportProps, any> {
|
||||||
<div>
|
<div>
|
||||||
<PostListing
|
<PostListing
|
||||||
post_view={pv}
|
post_view={pv}
|
||||||
duplicates={None}
|
|
||||||
moderators={None}
|
|
||||||
admins={None}
|
|
||||||
showCommunity={true}
|
showCommunity={true}
|
||||||
enableDownvotes={true}
|
enableDownvotes={true}
|
||||||
enableNsfw={true}
|
enableNsfw={true}
|
||||||
|
@ -69,24 +66,21 @@ export class PostReport extends Component<PostReportProps, any> {
|
||||||
<div>
|
<div>
|
||||||
{i18n.t("reason")}: {r.post_report.reason}
|
{i18n.t("reason")}: {r.post_report.reason}
|
||||||
</div>
|
</div>
|
||||||
{r.resolver.match({
|
{resolver && (
|
||||||
some: resolver => (
|
<div>
|
||||||
<div>
|
{r.post_report.resolved ? (
|
||||||
{r.post_report.resolved ? (
|
<T i18nKey="resolved_by">
|
||||||
<T i18nKey="resolved_by">
|
#
|
||||||
#
|
<PersonListing person={resolver} />
|
||||||
<PersonListing person={resolver} />
|
</T>
|
||||||
</T>
|
) : (
|
||||||
) : (
|
<T i18nKey="unresolved_by">
|
||||||
<T i18nKey="unresolved_by">
|
#
|
||||||
#
|
<PersonListing person={resolver} />
|
||||||
<PersonListing person={resolver} />
|
</T>
|
||||||
</T>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
<button
|
<button
|
||||||
className="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleResolveReport)}
|
onClick={linkEvent(this, this.handleResolveReport)}
|
||||||
|
@ -105,11 +99,14 @@ export class PostReport extends Component<PostReportProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleResolveReport(i: PostReport) {
|
handleResolveReport(i: PostReport) {
|
||||||
let form = new ResolvePostReport({
|
let auth = myAuth();
|
||||||
report_id: i.props.report.post_report.id,
|
if (auth) {
|
||||||
resolved: !i.props.report.post_report.resolved,
|
let form: ResolvePostReport = {
|
||||||
auth: auth().unwrap(),
|
report_id: i.props.report.post_report.id,
|
||||||
});
|
resolved: !i.props.report.post_report.resolved,
|
||||||
WebSocketService.Instance.send(wsClient.resolvePostReport(form));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.resolvePostReport(form));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Right, Some } from "@sniptt/monads";
|
|
||||||
import autosize from "autosize";
|
import autosize from "autosize";
|
||||||
import { Component, createRef, linkEvent, RefObject } from "inferno";
|
import { Component, createRef, linkEvent, RefObject } from "inferno";
|
||||||
import {
|
import {
|
||||||
|
@ -27,7 +26,6 @@ import {
|
||||||
SearchResponse,
|
SearchResponse,
|
||||||
SearchType,
|
SearchType,
|
||||||
SortType,
|
SortType,
|
||||||
toOption,
|
|
||||||
UserOperation,
|
UserOperation,
|
||||||
wsJsonToRes,
|
wsJsonToRes,
|
||||||
wsUserOp,
|
wsUserOp,
|
||||||
|
@ -37,7 +35,6 @@ import { i18n } from "../../i18next";
|
||||||
import { CommentViewType, InitialFetchRequest } from "../../interfaces";
|
import { CommentViewType, InitialFetchRequest } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
|
||||||
buildCommentsTree,
|
buildCommentsTree,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
commentTreeMaxDepth,
|
commentTreeMaxDepth,
|
||||||
|
@ -54,6 +51,7 @@ import {
|
||||||
insertCommentIntoTree,
|
insertCommentIntoTree,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
isImage,
|
isImage,
|
||||||
|
myAuth,
|
||||||
restoreScrollPosition,
|
restoreScrollPosition,
|
||||||
saveCommentRes,
|
saveCommentRes,
|
||||||
saveScrollPosition,
|
saveScrollPosition,
|
||||||
|
@ -75,16 +73,16 @@ import { PostListing } from "./post-listing";
|
||||||
const commentsShownInterval = 15;
|
const commentsShownInterval = 15;
|
||||||
|
|
||||||
interface PostState {
|
interface PostState {
|
||||||
postId: Option<number>;
|
postId?: number;
|
||||||
commentId: Option<number>;
|
commentId?: number;
|
||||||
postRes: Option<GetPostResponse>;
|
postRes?: GetPostResponse;
|
||||||
commentsRes: Option<GetCommentsResponse>;
|
commentsRes?: GetCommentsResponse;
|
||||||
commentTree: CommentNodeI[];
|
commentTree: CommentNodeI[];
|
||||||
commentSort: CommentSortType;
|
commentSort: CommentSortType;
|
||||||
commentViewType: CommentViewType;
|
commentViewType: CommentViewType;
|
||||||
scrolled?: boolean;
|
scrolled?: boolean;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
crossPosts: Option<PostView[]>;
|
crossPosts?: PostView[];
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
commentSectionRef?: RefObject<HTMLDivElement>;
|
commentSectionRef?: RefObject<HTMLDivElement>;
|
||||||
showSidebarMobile: boolean;
|
showSidebarMobile: boolean;
|
||||||
|
@ -92,16 +90,10 @@ interface PostState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Post extends Component<any, PostState> {
|
export class Post extends Component<any, PostState> {
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
private isoData = setIsoData(
|
private isoData = setIsoData(this.context);
|
||||||
this.context,
|
|
||||||
GetPostResponse,
|
|
||||||
GetCommentsResponse
|
|
||||||
);
|
|
||||||
private commentScrollDebounced: () => void;
|
private commentScrollDebounced: () => void;
|
||||||
private emptyState: PostState = {
|
state: PostState = {
|
||||||
postRes: None,
|
|
||||||
commentsRes: None,
|
|
||||||
postId: getIdFromProps(this.props),
|
postId: getIdFromProps(this.props),
|
||||||
commentId: getCommentIdFromProps(this.props),
|
commentId: getCommentIdFromProps(this.props),
|
||||||
commentTree: [],
|
commentTree: [],
|
||||||
|
@ -109,9 +101,7 @@ export class Post extends Component<any, PostState> {
|
||||||
commentViewType: CommentViewType.Tree,
|
commentViewType: CommentViewType.Tree,
|
||||||
scrolled: false,
|
scrolled: false,
|
||||||
loading: true,
|
loading: true,
|
||||||
crossPosts: None,
|
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
commentSectionRef: null,
|
|
||||||
showSidebarMobile: false,
|
showSidebarMobile: false,
|
||||||
maxCommentsShown: commentsShownInterval,
|
maxCommentsShown: commentsShownInterval,
|
||||||
};
|
};
|
||||||
|
@ -119,8 +109,6 @@ export class Post extends Component<any, PostState> {
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
|
@ -130,16 +118,16 @@ export class Post extends Component<any, PostState> {
|
||||||
if (this.isoData.path == this.context.router.route.match.url) {
|
if (this.isoData.path == this.context.router.route.match.url) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
postRes: Some(this.isoData.routeData[0] as GetPostResponse),
|
postRes: this.isoData.routeData[0] as GetPostResponse,
|
||||||
commentsRes: Some(this.isoData.routeData[1] as GetCommentsResponse),
|
commentsRes: this.isoData.routeData[1] as GetCommentsResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.state.commentsRes.isSome()) {
|
if (this.state.commentsRes) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
commentTree: buildCommentsTree(
|
commentTree: buildCommentsTree(
|
||||||
this.state.commentsRes.unwrap().comments,
|
this.state.commentsRes.comments,
|
||||||
this.state.commentId.isSome()
|
!!this.state.commentId
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -147,18 +135,19 @@ export class Post extends Component<any, PostState> {
|
||||||
this.state = { ...this.state, loading: false };
|
this.state = { ...this.state, loading: false };
|
||||||
|
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
WebSocketService.Instance.send(
|
if (this.state.postRes) {
|
||||||
wsClient.communityJoin({
|
WebSocketService.Instance.send(
|
||||||
community_id:
|
wsClient.communityJoin({
|
||||||
this.state.postRes.unwrap().community_view.community.id,
|
community_id: this.state.postRes.community_view.community.id,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
this.state.postId.match({
|
if (this.state.postId) {
|
||||||
some: post_id =>
|
WebSocketService.Instance.send(
|
||||||
WebSocketService.Instance.send(wsClient.postJoin({ post_id })),
|
wsClient.postJoin({ post_id: this.state.postId })
|
||||||
none: void 0,
|
);
|
||||||
});
|
}
|
||||||
|
|
||||||
this.fetchCrossPosts();
|
this.fetchCrossPosts();
|
||||||
|
|
||||||
|
@ -172,87 +161,70 @@ export class Post extends Component<any, PostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchPost() {
|
fetchPost() {
|
||||||
this.setState({ commentsRes: None });
|
this.setState({ commentsRes: undefined, postRes: undefined });
|
||||||
let postForm = new GetPost({
|
let auth = myAuth(false);
|
||||||
|
let postForm: GetPost = {
|
||||||
id: this.state.postId,
|
id: this.state.postId,
|
||||||
comment_id: this.state.commentId,
|
comment_id: this.state.commentId,
|
||||||
auth: auth(false).ok(),
|
auth,
|
||||||
});
|
};
|
||||||
WebSocketService.Instance.send(wsClient.getPost(postForm));
|
WebSocketService.Instance.send(wsClient.getPost(postForm));
|
||||||
|
|
||||||
let commentsForm = new GetComments({
|
let commentsForm: GetComments = {
|
||||||
post_id: this.state.postId,
|
post_id: this.state.postId,
|
||||||
parent_id: this.state.commentId,
|
parent_id: this.state.commentId,
|
||||||
max_depth: Some(commentTreeMaxDepth),
|
max_depth: commentTreeMaxDepth,
|
||||||
page: None,
|
sort: this.state.commentSort,
|
||||||
limit: None,
|
type_: ListingType.All,
|
||||||
sort: Some(this.state.commentSort),
|
saved_only: false,
|
||||||
type_: Some(ListingType.All),
|
auth,
|
||||||
community_name: None,
|
};
|
||||||
community_id: None,
|
|
||||||
saved_only: Some(false),
|
|
||||||
auth: auth(false).ok(),
|
|
||||||
});
|
|
||||||
WebSocketService.Instance.send(wsClient.getComments(commentsForm));
|
WebSocketService.Instance.send(wsClient.getComments(commentsForm));
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchCrossPosts() {
|
fetchCrossPosts() {
|
||||||
this.state.postRes
|
let q = this.state.postRes?.post_view.post.url;
|
||||||
.andThen(r => r.post_view.post.url)
|
if (q) {
|
||||||
.match({
|
let form: Search = {
|
||||||
some: url => {
|
q,
|
||||||
let form = new Search({
|
type_: SearchType.Url,
|
||||||
q: url,
|
sort: SortType.TopAll,
|
||||||
type_: Some(SearchType.Url),
|
listing_type: ListingType.All,
|
||||||
sort: Some(SortType.TopAll),
|
page: 1,
|
||||||
listing_type: Some(ListingType.All),
|
limit: trendingFetchLimit,
|
||||||
page: Some(1),
|
auth: myAuth(false),
|
||||||
limit: Some(trendingFetchLimit),
|
};
|
||||||
community_id: None,
|
WebSocketService.Instance.send(wsClient.search(form));
|
||||||
community_name: None,
|
}
|
||||||
creator_id: None,
|
|
||||||
auth: auth(false).ok(),
|
|
||||||
});
|
|
||||||
WebSocketService.Instance.send(wsClient.search(form));
|
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
||||||
let pathSplit = req.path.split("/");
|
let pathSplit = req.path.split("/");
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
let pathType = pathSplit[1];
|
let pathType = pathSplit.at(1);
|
||||||
let id = Number(pathSplit[2]);
|
let id = pathSplit.at(2) ? Number(pathSplit.at(2)) : undefined;
|
||||||
|
let auth = req.auth;
|
||||||
|
|
||||||
let postForm = new GetPost({
|
let postForm: GetPost = {
|
||||||
id: None,
|
auth,
|
||||||
comment_id: None,
|
};
|
||||||
auth: req.auth,
|
|
||||||
});
|
|
||||||
|
|
||||||
let commentsForm = new GetComments({
|
let commentsForm: GetComments = {
|
||||||
post_id: None,
|
max_depth: commentTreeMaxDepth,
|
||||||
parent_id: None,
|
sort: CommentSortType.Hot,
|
||||||
max_depth: Some(commentTreeMaxDepth),
|
type_: ListingType.All,
|
||||||
page: None,
|
saved_only: false,
|
||||||
limit: None,
|
auth,
|
||||||
sort: Some(CommentSortType.Hot),
|
};
|
||||||
type_: Some(ListingType.All),
|
|
||||||
community_name: None,
|
|
||||||
community_id: None,
|
|
||||||
saved_only: Some(false),
|
|
||||||
auth: req.auth,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set the correct id based on the path type
|
// Set the correct id based on the path type
|
||||||
if (pathType == "post") {
|
if (pathType == "post") {
|
||||||
postForm.id = Some(id);
|
postForm.id = id;
|
||||||
commentsForm.post_id = Some(id);
|
commentsForm.post_id = id;
|
||||||
} else {
|
} else {
|
||||||
postForm.comment_id = Some(id);
|
postForm.comment_id = id;
|
||||||
commentsForm.parent_id = Some(id);
|
commentsForm.parent_id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
promises.push(req.client.getPost(postForm));
|
promises.push(req.client.getPost(postForm));
|
||||||
|
@ -262,7 +234,7 @@ export class Post extends Component<any, PostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
document.removeEventListener("scroll", this.commentScrollDebounced);
|
document.removeEventListener("scroll", this.commentScrollDebounced);
|
||||||
|
|
||||||
saveScrollPosition(this.context);
|
saveScrollPosition(this.context);
|
||||||
|
@ -295,7 +267,7 @@ export class Post extends Component<any, PostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollIntoCommentSection() {
|
scrollIntoCommentSection() {
|
||||||
this.state.commentSectionRef.current?.scrollIntoView();
|
this.state.commentSectionRef?.current?.scrollIntoView();
|
||||||
}
|
}
|
||||||
|
|
||||||
isBottom(el: Element): boolean {
|
isBottom(el: Element): boolean {
|
||||||
|
@ -315,31 +287,21 @@ export class Post extends Component<any, PostState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return this.state.postRes.match({
|
let name_ = this.state.postRes?.post_view.post.name;
|
||||||
some: res =>
|
let siteName = this.state.siteRes.site_view.site.name;
|
||||||
`${res.post_view.post.name} - ${this.state.siteRes.site_view.site.name}`,
|
return name_ ? `${name_} - ${siteName}` : "";
|
||||||
none: "",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get imageTag(): Option<string> {
|
get imageTag(): string | undefined {
|
||||||
return this.state.postRes.match({
|
let post = this.state.postRes?.post_view.post;
|
||||||
some: res =>
|
let thumbnail = post?.thumbnail_url;
|
||||||
res.post_view.post.thumbnail_url.or(
|
let url = post?.url;
|
||||||
res.post_view.post.url.match({
|
return thumbnail || (url && isImage(url) ? url : undefined);
|
||||||
some: url => (isImage(url) ? Some(url) : None),
|
|
||||||
none: None,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
none: None,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get descriptionTag(): Option<string> {
|
|
||||||
return this.state.postRes.andThen(r => r.post_view.post.body);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let res = this.state.postRes;
|
||||||
|
let description = res?.post_view.post.body;
|
||||||
return (
|
return (
|
||||||
<div className="container-lg">
|
<div className="container-lg">
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
|
@ -347,65 +309,60 @@ export class Post extends Component<any, PostState> {
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
</h5>
|
</h5>
|
||||||
) : (
|
) : (
|
||||||
this.state.postRes.match({
|
res && (
|
||||||
some: res => (
|
<div className="row">
|
||||||
<div className="row">
|
<div className="col-12 col-md-8 mb-3">
|
||||||
<div className="col-12 col-md-8 mb-3">
|
<HtmlTags
|
||||||
<HtmlTags
|
title={this.documentTitle}
|
||||||
title={this.documentTitle}
|
path={this.context.router.route.match.url}
|
||||||
path={this.context.router.route.match.url}
|
image={this.imageTag}
|
||||||
image={this.imageTag}
|
description={description}
|
||||||
description={this.descriptionTag}
|
/>
|
||||||
/>
|
<PostListing
|
||||||
<PostListing
|
post_view={res.post_view}
|
||||||
post_view={res.post_view}
|
duplicates={this.state.crossPosts}
|
||||||
duplicates={this.state.crossPosts}
|
showBody
|
||||||
showBody
|
showCommunity
|
||||||
showCommunity
|
moderators={res.moderators}
|
||||||
moderators={Some(res.moderators)}
|
admins={this.state.siteRes.admins}
|
||||||
admins={Some(this.state.siteRes.admins)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
siteLanguages={this.state.siteRes.discussion_languages}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
/>
|
||||||
/>
|
<div ref={this.state.commentSectionRef} className="mb-2" />
|
||||||
<div ref={this.state.commentSectionRef} className="mb-2" />
|
<CommentForm
|
||||||
<CommentForm
|
node={res.post_view.post.id}
|
||||||
node={Right(res.post_view.post.id)}
|
disabled={res.post_view.post.locked}
|
||||||
disabled={res.post_view.post.locked}
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
siteLanguages={this.state.siteRes.discussion_languages}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
/>
|
||||||
/>
|
<div className="d-block d-md-none">
|
||||||
<div className="d-block d-md-none">
|
<button
|
||||||
<button
|
className="btn btn-secondary d-inline-block mb-2 mr-3"
|
||||||
className="btn btn-secondary d-inline-block mb-2 mr-3"
|
onClick={linkEvent(this, this.handleShowSidebarMobile)}
|
||||||
onClick={linkEvent(this, this.handleShowSidebarMobile)}
|
>
|
||||||
>
|
{i18n.t("sidebar")}{" "}
|
||||||
{i18n.t("sidebar")}{" "}
|
<Icon
|
||||||
<Icon
|
icon={
|
||||||
icon={
|
this.state.showSidebarMobile
|
||||||
this.state.showSidebarMobile
|
? `minus-square`
|
||||||
? `minus-square`
|
: `plus-square`
|
||||||
: `plus-square`
|
}
|
||||||
}
|
classes="icon-inline"
|
||||||
classes="icon-inline"
|
/>
|
||||||
/>
|
</button>
|
||||||
</button>
|
{this.state.showSidebarMobile && this.sidebar()}
|
||||||
{this.state.showSidebarMobile && this.sidebar()}
|
|
||||||
</div>
|
|
||||||
{this.sortRadios()}
|
|
||||||
{this.state.commentViewType == CommentViewType.Tree &&
|
|
||||||
this.commentsTree()}
|
|
||||||
{this.state.commentViewType == CommentViewType.Flat &&
|
|
||||||
this.commentsFlat()}
|
|
||||||
</div>
|
|
||||||
<div className="d-none d-md-block col-md-4">
|
|
||||||
{this.sidebar()}
|
|
||||||
</div>
|
</div>
|
||||||
|
{this.sortRadios()}
|
||||||
|
{this.state.commentViewType == CommentViewType.Tree &&
|
||||||
|
this.commentsTree()}
|
||||||
|
{this.state.commentViewType == CommentViewType.Flat &&
|
||||||
|
this.commentsFlat()}
|
||||||
</div>
|
</div>
|
||||||
),
|
<div className="d-none d-md-block col-md-4">{this.sidebar()}</div>
|
||||||
none: <></>,
|
</div>
|
||||||
})
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -493,35 +450,34 @@ export class Post extends Component<any, PostState> {
|
||||||
|
|
||||||
commentsFlat() {
|
commentsFlat() {
|
||||||
// These are already sorted by new
|
// These are already sorted by new
|
||||||
return this.state.commentsRes.match({
|
let commentsRes = this.state.commentsRes;
|
||||||
some: commentsRes =>
|
let postRes = this.state.postRes;
|
||||||
this.state.postRes.match({
|
return (
|
||||||
some: postRes => (
|
commentsRes &&
|
||||||
<div>
|
postRes && (
|
||||||
<CommentNodes
|
<div>
|
||||||
nodes={commentsToFlatNodes(commentsRes.comments)}
|
<CommentNodes
|
||||||
viewType={this.state.commentViewType}
|
nodes={commentsToFlatNodes(commentsRes.comments)}
|
||||||
maxCommentsShown={Some(this.state.maxCommentsShown)}
|
viewType={this.state.commentViewType}
|
||||||
noIndent
|
maxCommentsShown={this.state.maxCommentsShown}
|
||||||
locked={postRes.post_view.post.locked}
|
noIndent
|
||||||
moderators={Some(postRes.moderators)}
|
locked={postRes.post_view.post.locked}
|
||||||
admins={Some(this.state.siteRes.admins)}
|
moderators={postRes.moderators}
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
admins={this.state.siteRes.admins}
|
||||||
showContext
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
showContext
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
/>
|
siteLanguages={this.state.siteRes.discussion_languages}
|
||||||
</div>
|
/>
|
||||||
),
|
</div>
|
||||||
none: <></>,
|
)
|
||||||
}),
|
);
|
||||||
none: <></>,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sidebar() {
|
sidebar() {
|
||||||
return this.state.postRes.match({
|
let res = this.state.postRes;
|
||||||
some: res => (
|
return (
|
||||||
|
res && (
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<Sidebar
|
<Sidebar
|
||||||
community_view={res.community_view}
|
community_view={res.community_view}
|
||||||
|
@ -532,12 +488,10 @@ export class Post extends Component<any, PostState> {
|
||||||
showIcon
|
showIcon
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
siteLanguages={this.state.siteRes.discussion_languages}
|
||||||
communityLanguages={None}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
),
|
)
|
||||||
none: <></>,
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommentSortChange(i: Post, event: any) {
|
handleCommentSortChange(i: Post, event: any) {
|
||||||
|
@ -549,14 +503,14 @@ export class Post extends Component<any, PostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommentViewTypeChange(i: Post, event: any) {
|
handleCommentViewTypeChange(i: Post, event: any) {
|
||||||
i.setState({
|
let comments = i.state.commentsRes?.comments;
|
||||||
commentViewType: Number(event.target.value),
|
if (comments) {
|
||||||
commentSort: CommentSortType.New,
|
i.setState({
|
||||||
commentTree: buildCommentsTree(
|
commentViewType: Number(event.target.value),
|
||||||
i.state.commentsRes.map(r => r.comments).unwrapOr([]),
|
commentSort: CommentSortType.New,
|
||||||
i.state.commentId.isSome()
|
commentTree: buildCommentsTree(comments, !!i.state.commentId),
|
||||||
),
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowSidebarMobile(i: Post) {
|
handleShowSidebarMobile(i: Post) {
|
||||||
|
@ -564,33 +518,31 @@ export class Post extends Component<any, PostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleViewPost(i: Post) {
|
handleViewPost(i: Post) {
|
||||||
i.state.postRes.match({
|
let id = i.state.postRes?.post_view.post.id;
|
||||||
some: res =>
|
if (id) {
|
||||||
i.context.router.history.push(`/post/${res.post_view.post.id}`),
|
i.context.router.history.push(`/post/${id}`);
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleViewContext(i: Post) {
|
handleViewContext(i: Post) {
|
||||||
i.state.commentsRes.match({
|
let parentId = getCommentParentId(
|
||||||
some: res =>
|
i.state.commentsRes?.comments?.at(0)?.comment
|
||||||
i.context.router.history.push(
|
);
|
||||||
`/comment/${getCommentParentId(res.comments[0].comment).unwrap()}`
|
if (parentId) {
|
||||||
),
|
i.context.router.history.push(`/comment/${parentId}`);
|
||||||
none: void 0,
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commentsTree() {
|
commentsTree() {
|
||||||
let showContextButton = toOption(this.state.commentTree[0]).match({
|
let res = this.state.postRes;
|
||||||
some: comment => getDepthFromComment(comment.comment_view.comment) > 0,
|
let firstComment = this.state.commentTree.at(0)?.comment_view.comment;
|
||||||
none: false,
|
let depth = getDepthFromComment(firstComment);
|
||||||
});
|
let showContextButton = depth ? depth > 0 : false;
|
||||||
|
|
||||||
return this.state.postRes.match({
|
return (
|
||||||
some: res => (
|
res && (
|
||||||
<div>
|
<div>
|
||||||
{this.state.commentId.isSome() && (
|
{!!this.state.commentId && (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
className="pl-0 d-block btn btn-link text-muted"
|
className="pl-0 d-block btn btn-link text-muted"
|
||||||
|
@ -611,18 +563,17 @@ export class Post extends Component<any, PostState> {
|
||||||
<CommentNodes
|
<CommentNodes
|
||||||
nodes={this.state.commentTree}
|
nodes={this.state.commentTree}
|
||||||
viewType={this.state.commentViewType}
|
viewType={this.state.commentViewType}
|
||||||
maxCommentsShown={Some(this.state.maxCommentsShown)}
|
maxCommentsShown={this.state.maxCommentsShown}
|
||||||
locked={res.post_view.post.locked}
|
locked={res.post_view.post.locked}
|
||||||
moderators={Some(res.moderators)}
|
moderators={res.moderators}
|
||||||
admins={Some(this.state.siteRes.admins)}
|
admins={this.state.siteRes.admins}
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
siteLanguages={this.state.siteRes.discussion_languages}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
),
|
)
|
||||||
none: <></>,
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -632,25 +583,19 @@ export class Post extends Component<any, PostState> {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
return;
|
return;
|
||||||
} else if (msg.reconnect) {
|
} else if (msg.reconnect) {
|
||||||
this.state.postRes.match({
|
let post_id = this.state.postRes?.post_view.post.id;
|
||||||
some: res => {
|
if (post_id) {
|
||||||
let postId = res.post_view.post.id;
|
WebSocketService.Instance.send(wsClient.postJoin({ post_id }));
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.postJoin({ post_id: postId })
|
wsClient.getPost({
|
||||||
);
|
id: post_id,
|
||||||
WebSocketService.Instance.send(
|
auth: myAuth(false),
|
||||||
wsClient.getPost({
|
})
|
||||||
id: Some(postId),
|
);
|
||||||
comment_id: None,
|
}
|
||||||
auth: auth(false).ok(),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
} else if (op == UserOperation.GetPost) {
|
} else if (op == UserOperation.GetPost) {
|
||||||
let data = wsJsonToRes<GetPostResponse>(msg, GetPostResponse);
|
let data = wsJsonToRes<GetPostResponse>(msg);
|
||||||
this.setState({ postRes: Some(data) });
|
this.setState({ postRes: data });
|
||||||
|
|
||||||
// join the rooms
|
// join the rooms
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
|
@ -666,60 +611,55 @@ export class Post extends Component<any, PostState> {
|
||||||
// TODO move this into initial fetch and refetch
|
// TODO move this into initial fetch and refetch
|
||||||
this.fetchCrossPosts();
|
this.fetchCrossPosts();
|
||||||
setupTippy();
|
setupTippy();
|
||||||
if (this.state.commentId.isNone()) restoreScrollPosition(this.context);
|
if (!this.state.commentId) restoreScrollPosition(this.context);
|
||||||
|
|
||||||
if (this.checkScrollIntoCommentsParam) {
|
if (this.checkScrollIntoCommentsParam) {
|
||||||
this.scrollIntoCommentSection();
|
this.scrollIntoCommentSection();
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.GetComments) {
|
} else if (op == UserOperation.GetComments) {
|
||||||
let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
|
let data = wsJsonToRes<GetCommentsResponse>(msg);
|
||||||
// You might need to append here, since this could be building more comments from a tree fetch
|
// This section sets the comments res
|
||||||
this.state.commentsRes.match({
|
let comments = this.state.commentsRes?.comments;
|
||||||
some: res => {
|
if (comments) {
|
||||||
// Remove the first comment, since it is the parent
|
// You might need to append here, since this could be building more comments from a tree fetch
|
||||||
let newComments = data.comments;
|
// Remove the first comment, since it is the parent
|
||||||
newComments.shift();
|
let newComments = data.comments;
|
||||||
res.comments.push(...newComments);
|
newComments.shift();
|
||||||
},
|
comments.push(...newComments);
|
||||||
none: () => this.setState({ commentsRes: Some(data) }),
|
} else {
|
||||||
});
|
this.setState({ commentsRes: data });
|
||||||
// this.state.commentsRes = Some(data);
|
}
|
||||||
|
|
||||||
|
let cComments = this.state.commentsRes?.comments ?? [];
|
||||||
this.setState({
|
this.setState({
|
||||||
commentTree: buildCommentsTree(
|
commentTree: buildCommentsTree(cComments, !!this.state.commentId),
|
||||||
this.state.commentsRes.map(r => r.comments).unwrapOr([]),
|
|
||||||
this.state.commentId.isSome()
|
|
||||||
),
|
|
||||||
loading: false,
|
loading: false,
|
||||||
});
|
});
|
||||||
this.setState(this.state);
|
|
||||||
} else if (op == UserOperation.CreateComment) {
|
} else if (op == UserOperation.CreateComment) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
|
|
||||||
// Don't get comments from the post room, if the creator is blocked
|
// Don't get comments from the post room, if the creator is blocked
|
||||||
let creatorBlocked = UserService.Instance.myUserInfo
|
let creatorBlocked = UserService.Instance.myUserInfo?.person_blocks
|
||||||
.map(m => m.person_blocks)
|
|
||||||
.unwrapOr([])
|
|
||||||
.map(pb => pb.target.id)
|
.map(pb => pb.target.id)
|
||||||
.includes(data.comment_view.creator.id);
|
.includes(data.comment_view.creator.id);
|
||||||
|
|
||||||
// Necessary since it might be a user reply, which has the recipients, to avoid double
|
// Necessary since it might be a user reply, which has the recipients, to avoid double
|
||||||
if (data.recipient_ids.length == 0 && !creatorBlocked) {
|
let postRes = this.state.postRes;
|
||||||
this.state.postRes.match({
|
let commentsRes = this.state.commentsRes;
|
||||||
some: postRes =>
|
if (
|
||||||
this.state.commentsRes.match({
|
data.recipient_ids.length == 0 &&
|
||||||
some: commentsRes => {
|
!creatorBlocked &&
|
||||||
commentsRes.comments.unshift(data.comment_view);
|
postRes &&
|
||||||
insertCommentIntoTree(
|
commentsRes
|
||||||
this.state.commentTree,
|
) {
|
||||||
data.comment_view,
|
commentsRes.comments.unshift(data.comment_view);
|
||||||
this.state.commentId.isSome()
|
insertCommentIntoTree(
|
||||||
);
|
this.state.commentTree,
|
||||||
postRes.post_view.counts.comments++;
|
data.comment_view,
|
||||||
},
|
!!this.state.commentId
|
||||||
none: void 0,
|
);
|
||||||
}),
|
postRes.post_view.counts.comments++;
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
}
|
}
|
||||||
|
@ -728,34 +668,22 @@ export class Post extends Component<any, PostState> {
|
||||||
op == UserOperation.DeleteComment ||
|
op == UserOperation.DeleteComment ||
|
||||||
op == UserOperation.RemoveComment
|
op == UserOperation.RemoveComment
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
editCommentRes(
|
editCommentRes(data.comment_view, this.state.commentsRes?.comments);
|
||||||
data.comment_view,
|
|
||||||
this.state.commentsRes.map(r => r.comments).unwrapOr([])
|
|
||||||
);
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.SaveComment) {
|
} else if (op == UserOperation.SaveComment) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
saveCommentRes(
|
saveCommentRes(data.comment_view, this.state.commentsRes?.comments);
|
||||||
data.comment_view,
|
|
||||||
this.state.commentsRes.map(r => r.comments).unwrapOr([])
|
|
||||||
);
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.CreateCommentLike) {
|
} else if (op == UserOperation.CreateCommentLike) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
createCommentLikeRes(
|
createCommentLikeRes(data.comment_view, this.state.commentsRes?.comments);
|
||||||
data.comment_view,
|
|
||||||
this.state.commentsRes.map(r => r.comments).unwrapOr([])
|
|
||||||
);
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.CreatePostLike) {
|
} else if (op == UserOperation.CreatePostLike) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
let data = wsJsonToRes<PostResponse>(msg);
|
||||||
this.state.postRes.match({
|
createPostLikeRes(data.post_view, this.state.postRes?.post_view);
|
||||||
some: res => createPostLikeRes(data.post_view, res.post_view),
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (
|
} else if (
|
||||||
op == UserOperation.EditPost ||
|
op == UserOperation.EditPost ||
|
||||||
|
@ -765,112 +693,91 @@ export class Post extends Component<any, PostState> {
|
||||||
op == UserOperation.FeaturePost ||
|
op == UserOperation.FeaturePost ||
|
||||||
op == UserOperation.SavePost
|
op == UserOperation.SavePost
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
let data = wsJsonToRes<PostResponse>(msg);
|
||||||
this.state.postRes.match({
|
let pv = this.state.postRes?.post_view;
|
||||||
some: res => (res.post_view = data.post_view),
|
if (pv) {
|
||||||
none: void 0,
|
pv = data.post_view;
|
||||||
});
|
this.setState(this.state);
|
||||||
this.setState(this.state);
|
setupTippy();
|
||||||
setupTippy();
|
}
|
||||||
} else if (
|
} else if (
|
||||||
op == UserOperation.EditCommunity ||
|
op == UserOperation.EditCommunity ||
|
||||||
op == UserOperation.DeleteCommunity ||
|
op == UserOperation.DeleteCommunity ||
|
||||||
op == UserOperation.RemoveCommunity ||
|
op == UserOperation.RemoveCommunity ||
|
||||||
op == UserOperation.FollowCommunity
|
op == UserOperation.FollowCommunity
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
let data = wsJsonToRes<CommunityResponse>(msg);
|
||||||
this.state.postRes.match({
|
let res = this.state.postRes;
|
||||||
some: res => {
|
if (res) {
|
||||||
res.community_view = data.community_view;
|
res.community_view = data.community_view;
|
||||||
res.post_view.community = data.community_view.community;
|
res.post_view.community = data.community_view.community;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
},
|
}
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
} else if (op == UserOperation.BanFromCommunity) {
|
} else if (op == UserOperation.BanFromCommunity) {
|
||||||
let data = wsJsonToRes<BanFromCommunityResponse>(
|
let data = wsJsonToRes<BanFromCommunityResponse>(msg);
|
||||||
msg,
|
|
||||||
BanFromCommunityResponse
|
let postRes = this.state.postRes;
|
||||||
);
|
if (postRes) {
|
||||||
this.state.postRes.match({
|
if (postRes.post_view.creator.id == data.person_view.person.id) {
|
||||||
some: postRes =>
|
postRes.post_view.creator_banned_from_community = data.banned;
|
||||||
this.state.commentsRes.match({
|
}
|
||||||
some: commentsRes => {
|
}
|
||||||
commentsRes.comments
|
|
||||||
.filter(c => c.creator.id == data.person_view.person.id)
|
this.state.commentsRes?.comments
|
||||||
.forEach(c => (c.creator_banned_from_community = data.banned));
|
.filter(c => c.creator.id == data.person_view.person.id)
|
||||||
if (postRes.post_view.creator.id == data.person_view.person.id) {
|
.forEach(c => (c.creator_banned_from_community = data.banned));
|
||||||
postRes.post_view.creator_banned_from_community = data.banned;
|
this.setState(this.state);
|
||||||
}
|
|
||||||
this.setState(this.state);
|
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
}),
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
} else if (op == UserOperation.AddModToCommunity) {
|
} else if (op == UserOperation.AddModToCommunity) {
|
||||||
let data = wsJsonToRes<AddModToCommunityResponse>(
|
let data = wsJsonToRes<AddModToCommunityResponse>(msg);
|
||||||
msg,
|
let postRes = this.state.postRes;
|
||||||
AddModToCommunityResponse
|
if (postRes) {
|
||||||
);
|
postRes.moderators = data.moderators;
|
||||||
this.state.postRes.match({
|
this.setState(this.state);
|
||||||
some: res => {
|
}
|
||||||
res.moderators = data.moderators;
|
|
||||||
this.setState(this.state);
|
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
} else if (op == UserOperation.BanPerson) {
|
} else if (op == UserOperation.BanPerson) {
|
||||||
let data = wsJsonToRes<BanPersonResponse>(msg, BanPersonResponse);
|
let data = wsJsonToRes<BanPersonResponse>(msg);
|
||||||
this.state.postRes.match({
|
this.state.commentsRes?.comments
|
||||||
some: postRes =>
|
.filter(c => c.creator.id == data.person_view.person.id)
|
||||||
this.state.commentsRes.match({
|
.forEach(c => (c.creator.banned = data.banned));
|
||||||
some: commentsRes => {
|
|
||||||
commentsRes.comments
|
let postRes = this.state.postRes;
|
||||||
.filter(c => c.creator.id == data.person_view.person.id)
|
if (postRes) {
|
||||||
.forEach(c => (c.creator.banned = data.banned));
|
if (postRes.post_view.creator.id == data.person_view.person.id) {
|
||||||
if (postRes.post_view.creator.id == data.person_view.person.id) {
|
postRes.post_view.creator.banned = data.banned;
|
||||||
postRes.post_view.creator.banned = data.banned;
|
}
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
}),
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
} else if (op == UserOperation.AddAdmin) {
|
} else if (op == UserOperation.AddAdmin) {
|
||||||
let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
|
let data = wsJsonToRes<AddAdminResponse>(msg);
|
||||||
this.setState(s => ((s.siteRes.admins = data.admins), s));
|
this.setState(s => ((s.siteRes.admins = data.admins), s));
|
||||||
} else if (op == UserOperation.Search) {
|
} else if (op == UserOperation.Search) {
|
||||||
let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
|
let data = wsJsonToRes<SearchResponse>(msg);
|
||||||
let xPosts = data.posts.filter(
|
let xPosts = data.posts.filter(
|
||||||
p => p.post.id != Number(this.props.match.params.id)
|
p => p.post.id != Number(this.props.match.params.id)
|
||||||
);
|
);
|
||||||
this.setState({ crossPosts: xPosts.length > 0 ? Some(xPosts) : None });
|
this.setState({ crossPosts: xPosts.length > 0 ? xPosts : undefined });
|
||||||
} else if (op == UserOperation.LeaveAdmin) {
|
} else if (op == UserOperation.LeaveAdmin) {
|
||||||
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
let data = wsJsonToRes<GetSiteResponse>(msg);
|
||||||
this.setState({ siteRes: data });
|
this.setState({ siteRes: data });
|
||||||
} else if (op == UserOperation.TransferCommunity) {
|
} else if (op == UserOperation.TransferCommunity) {
|
||||||
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
let data = wsJsonToRes<GetCommunityResponse>(msg);
|
||||||
this.state.postRes.match({
|
let postRes = this.state.postRes;
|
||||||
some: res => {
|
if (postRes) {
|
||||||
res.community_view = data.community_view;
|
postRes.community_view = data.community_view;
|
||||||
res.post_view.community = data.community_view.community;
|
postRes.post_view.community = data.community_view.community;
|
||||||
res.moderators = data.moderators;
|
postRes.moderators = data.moderators;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
},
|
}
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
} else if (op == UserOperation.BlockPerson) {
|
} else if (op == UserOperation.BlockPerson) {
|
||||||
let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
|
let data = wsJsonToRes<BlockPersonResponse>(msg);
|
||||||
updatePersonBlock(data);
|
updatePersonBlock(data);
|
||||||
} else if (op == UserOperation.CreatePostReport) {
|
} else if (op == UserOperation.CreatePostReport) {
|
||||||
let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
|
let data = wsJsonToRes<PostReportResponse>(msg);
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
toast(i18n.t("report_created"));
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.CreateCommentReport) {
|
} else if (op == UserOperation.CreateCommentReport) {
|
||||||
let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
|
let data = wsJsonToRes<CommentReportResponse>(msg);
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
toast(i18n.t("report_created"));
|
||||||
}
|
}
|
||||||
|
@ -880,7 +787,7 @@ export class Post extends Component<any, PostState> {
|
||||||
op == UserOperation.PurgeComment ||
|
op == UserOperation.PurgeComment ||
|
||||||
op == UserOperation.PurgeCommunity
|
op == UserOperation.PurgeCommunity
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<PurgeItemResponse>(msg, PurgeItemResponse);
|
let data = wsJsonToRes<PurgeItemResponse>(msg);
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
toast(i18n.t("purge_success"));
|
toast(i18n.t("purge_success"));
|
||||||
this.context.router.history.push(`/`);
|
this.context.router.history.push(`/`);
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import {
|
import {
|
||||||
GetPersonDetails,
|
GetPersonDetails,
|
||||||
|
@ -14,9 +13,9 @@ import { i18n } from "../../i18next";
|
||||||
import { InitialFetchRequest } from "../../interfaces";
|
import { InitialFetchRequest } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
|
||||||
getRecipientIdFromProps,
|
getRecipientIdFromProps,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
|
myAuth,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
toast,
|
toast,
|
||||||
wsClient,
|
wsClient,
|
||||||
|
@ -28,7 +27,7 @@ import { PrivateMessageForm } from "./private-message-form";
|
||||||
|
|
||||||
interface CreatePrivateMessageState {
|
interface CreatePrivateMessageState {
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
recipientDetailsRes: Option<GetPersonDetailsResponse>;
|
recipientDetailsRes?: GetPersonDetailsResponse;
|
||||||
recipient_id: number;
|
recipient_id: number;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
@ -37,25 +36,23 @@ export class CreatePrivateMessage extends Component<
|
||||||
any,
|
any,
|
||||||
CreatePrivateMessageState
|
CreatePrivateMessageState
|
||||||
> {
|
> {
|
||||||
private isoData = setIsoData(this.context, GetPersonDetailsResponse);
|
private isoData = setIsoData(this.context);
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
private emptyState: CreatePrivateMessageState = {
|
state: CreatePrivateMessageState = {
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
recipientDetailsRes: None,
|
|
||||||
recipient_id: getRecipientIdFromProps(this.props),
|
recipient_id: getRecipientIdFromProps(this.props),
|
||||||
loading: true,
|
loading: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
|
||||||
this.handlePrivateMessageCreate =
|
this.handlePrivateMessageCreate =
|
||||||
this.handlePrivateMessageCreate.bind(this);
|
this.handlePrivateMessageCreate.bind(this);
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
if (UserService.Instance.myUserInfo.isNone() && isBrowser()) {
|
if (!UserService.Instance.myUserInfo && isBrowser()) {
|
||||||
toast(i18n.t("not_logged_in"), "danger");
|
toast(i18n.t("not_logged_in"), "danger");
|
||||||
this.context.router.history.push(`/login`);
|
this.context.router.history.push(`/login`);
|
||||||
}
|
}
|
||||||
|
@ -64,9 +61,8 @@ export class CreatePrivateMessage extends Component<
|
||||||
if (this.isoData.path == this.context.router.route.match.url) {
|
if (this.isoData.path == this.context.router.route.match.url) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
recipientDetailsRes: Some(
|
recipientDetailsRes: this.isoData
|
||||||
this.isoData.routeData[0] as GetPersonDetailsResponse
|
.routeData[0] as GetPersonDetailsResponse,
|
||||||
),
|
|
||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
@ -75,77 +71,61 @@ export class CreatePrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchPersonDetails() {
|
fetchPersonDetails() {
|
||||||
let form = new GetPersonDetails({
|
let form: GetPersonDetails = {
|
||||||
person_id: Some(this.state.recipient_id),
|
person_id: this.state.recipient_id,
|
||||||
sort: Some(SortType.New),
|
sort: SortType.New,
|
||||||
saved_only: Some(false),
|
saved_only: false,
|
||||||
username: None,
|
auth: myAuth(false),
|
||||||
page: None,
|
};
|
||||||
limit: None,
|
|
||||||
community_id: None,
|
|
||||||
auth: auth(false).ok(),
|
|
||||||
});
|
|
||||||
WebSocketService.Instance.send(wsClient.getPersonDetails(form));
|
WebSocketService.Instance.send(wsClient.getPersonDetails(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
||||||
let person_id = Some(Number(req.path.split("/").pop()));
|
let person_id = Number(req.path.split("/").pop());
|
||||||
let form = new GetPersonDetails({
|
let form: GetPersonDetails = {
|
||||||
person_id,
|
person_id,
|
||||||
sort: Some(SortType.New),
|
sort: SortType.New,
|
||||||
saved_only: Some(false),
|
saved_only: false,
|
||||||
username: None,
|
|
||||||
page: None,
|
|
||||||
limit: None,
|
|
||||||
community_id: None,
|
|
||||||
auth: req.auth,
|
auth: req.auth,
|
||||||
});
|
};
|
||||||
return [req.client.getPersonDetails(form)];
|
return [req.client.getPersonDetails(form)];
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return this.state.recipientDetailsRes.match({
|
let name_ = this.state.recipientDetailsRes?.person_view.person.name;
|
||||||
some: res =>
|
return name_ ? `${i18n.t("create_private_message")} - ${name_}` : "";
|
||||||
`${i18n.t("create_private_message")} - ${res.person_view.person.name}`,
|
|
||||||
none: "",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let res = this.state.recipientDetailsRes;
|
||||||
return (
|
return (
|
||||||
<div className="container-lg">
|
<div className="container-lg">
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<h5>
|
<h5>
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
</h5>
|
</h5>
|
||||||
) : (
|
) : (
|
||||||
this.state.recipientDetailsRes.match({
|
res && (
|
||||||
some: res => (
|
<div className="row">
|
||||||
<div className="row">
|
<div className="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||||
<div className="col-12 col-lg-6 offset-lg-3 mb-4">
|
<h5>{i18n.t("create_private_message")}</h5>
|
||||||
<h5>{i18n.t("create_private_message")}</h5>
|
<PrivateMessageForm
|
||||||
<PrivateMessageForm
|
onCreate={this.handlePrivateMessageCreate}
|
||||||
privateMessageView={None}
|
recipient={res.person_view.person}
|
||||||
onCreate={this.handlePrivateMessageCreate}
|
/>
|
||||||
recipient={res.person_view.person}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
),
|
</div>
|
||||||
none: <></>,
|
)
|
||||||
})
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -166,11 +146,8 @@ export class CreatePrivateMessage extends Component<
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.GetPersonDetails) {
|
} else if (op == UserOperation.GetPersonDetails) {
|
||||||
let data = wsJsonToRes<GetPersonDetailsResponse>(
|
let data = wsJsonToRes<GetPersonDetailsResponse>(msg);
|
||||||
msg,
|
this.setState({ recipientDetailsRes: data, loading: false });
|
||||||
GetPersonDetailsResponse
|
|
||||||
);
|
|
||||||
this.setState({ recipientDetailsRes: Some(data), loading: false });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import { T } from "inferno-i18next-dess";
|
import { T } from "inferno-i18next-dess";
|
||||||
import { Prompt } from "inferno-router";
|
import { Prompt } from "inferno-router";
|
||||||
|
@ -16,9 +15,9 @@ import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { WebSocketService } from "../../services";
|
import { WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
|
myAuth,
|
||||||
relTags,
|
relTags,
|
||||||
setupTippy,
|
setupTippy,
|
||||||
toast,
|
toast,
|
||||||
|
@ -31,14 +30,14 @@ import { PersonListing } from "../person/person-listing";
|
||||||
|
|
||||||
interface PrivateMessageFormProps {
|
interface PrivateMessageFormProps {
|
||||||
recipient: PersonSafe;
|
recipient: PersonSafe;
|
||||||
privateMessageView: Option<PrivateMessageView>; // If a pm is given, that means this is an edit
|
privateMessageView?: PrivateMessageView; // If a pm is given, that means this is an edit
|
||||||
onCancel?(): any;
|
onCancel?(): any;
|
||||||
onCreate?(message: PrivateMessageView): any;
|
onCreate?(message: PrivateMessageView): any;
|
||||||
onEdit?(message: PrivateMessageView): any;
|
onEdit?(message: PrivateMessageView): any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PrivateMessageFormState {
|
interface PrivateMessageFormState {
|
||||||
privateMessageForm: CreatePrivateMessage;
|
content?: string;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
previewMode: boolean;
|
previewMode: boolean;
|
||||||
showDisclaimer: boolean;
|
showDisclaimer: boolean;
|
||||||
|
@ -48,13 +47,8 @@ export class PrivateMessageForm extends Component<
|
||||||
PrivateMessageFormProps,
|
PrivateMessageFormProps,
|
||||||
PrivateMessageFormState
|
PrivateMessageFormState
|
||||||
> {
|
> {
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
private emptyState: PrivateMessageFormState = {
|
state: PrivateMessageFormState = {
|
||||||
privateMessageForm: new CreatePrivateMessage({
|
|
||||||
content: null,
|
|
||||||
recipient_id: this.props.recipient.id,
|
|
||||||
auth: auth().unwrap(),
|
|
||||||
}),
|
|
||||||
loading: false,
|
loading: false,
|
||||||
previewMode: false,
|
previewMode: false,
|
||||||
showDisclaimer: false,
|
showDisclaimer: false,
|
||||||
|
@ -63,17 +57,15 @@ export class PrivateMessageForm extends Component<
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
|
|
||||||
this.handleContentChange = this.handleContentChange.bind(this);
|
this.handleContentChange = this.handleContentChange.bind(this);
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
// Its an edit
|
// Its an edit
|
||||||
if (this.props.privateMessageView.isSome()) {
|
if (this.props.privateMessageView) {
|
||||||
this.state.privateMessageForm.content =
|
this.state.content =
|
||||||
this.props.privateMessageView.unwrap().private_message.content;
|
this.props.privateMessageView.private_message.content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,16 +74,16 @@ export class PrivateMessageForm extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if (!this.state.loading && this.state.privateMessageForm.content) {
|
if (!this.state.loading && this.state.content) {
|
||||||
window.onbeforeunload = () => true;
|
window.onbeforeunload = () => true;
|
||||||
} else {
|
} else {
|
||||||
window.onbeforeunload = undefined;
|
window.onbeforeunload = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
window.onbeforeunload = null;
|
window.onbeforeunload = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,11 +92,11 @@ export class PrivateMessageForm extends Component<
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Prompt
|
<Prompt
|
||||||
when={!this.state.loading && this.state.privateMessageForm.content}
|
when={!this.state.loading && this.state.content}
|
||||||
message={i18n.t("block_leaving")}
|
message={i18n.t("block_leaving")}
|
||||||
/>
|
/>
|
||||||
<form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
|
<form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
|
||||||
{this.props.privateMessageView.isNone() && (
|
{!this.props.privateMessageView && (
|
||||||
<div className="form-group row">
|
<div className="form-group row">
|
||||||
<label className="col-sm-2 col-form-label">
|
<label className="col-sm-2 col-form-label">
|
||||||
{capitalizeFirstLetter(i18n.t("to"))}
|
{capitalizeFirstLetter(i18n.t("to"))}
|
||||||
|
@ -129,11 +121,7 @@ export class PrivateMessageForm extends Component<
|
||||||
</label>
|
</label>
|
||||||
<div className="col-sm-10">
|
<div className="col-sm-10">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={Some(this.state.privateMessageForm.content)}
|
initialContent={this.state.content}
|
||||||
initialLanguageId={None}
|
|
||||||
placeholder={None}
|
|
||||||
buttonTitle={None}
|
|
||||||
maxLength={None}
|
|
||||||
onContentChange={this.handleContentChange}
|
onContentChange={this.handleContentChange}
|
||||||
allLanguages={[]}
|
allLanguages={[]}
|
||||||
siteLanguages={[]}
|
siteLanguages={[]}
|
||||||
|
@ -168,13 +156,13 @@ export class PrivateMessageForm extends Component<
|
||||||
>
|
>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : this.props.privateMessageView.isSome() ? (
|
) : this.props.privateMessageView ? (
|
||||||
capitalizeFirstLetter(i18n.t("save"))
|
capitalizeFirstLetter(i18n.t("save"))
|
||||||
) : (
|
) : (
|
||||||
capitalizeFirstLetter(i18n.t("send_message"))
|
capitalizeFirstLetter(i18n.t("send_message"))
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
{this.props.privateMessageView.isSome() && (
|
{this.props.privateMessageView && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
|
@ -195,28 +183,35 @@ export class PrivateMessageForm extends Component<
|
||||||
|
|
||||||
handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) {
|
handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.props.privateMessageView.match({
|
let pm = i.props.privateMessageView;
|
||||||
some: pm => {
|
let auth = myAuth();
|
||||||
let form = new EditPrivateMessage({
|
let content = i.state.content;
|
||||||
|
if (auth && content) {
|
||||||
|
if (pm) {
|
||||||
|
let form: EditPrivateMessage = {
|
||||||
private_message_id: pm.private_message.id,
|
private_message_id: pm.private_message.id,
|
||||||
content: i.state.privateMessageForm.content,
|
content,
|
||||||
auth: auth().unwrap(),
|
auth,
|
||||||
});
|
};
|
||||||
WebSocketService.Instance.send(wsClient.editPrivateMessage(form));
|
WebSocketService.Instance.send(wsClient.editPrivateMessage(form));
|
||||||
},
|
} else {
|
||||||
none: WebSocketService.Instance.send(
|
let form: CreatePrivateMessage = {
|
||||||
wsClient.createPrivateMessage(i.state.privateMessageForm)
|
content,
|
||||||
),
|
recipient_id: i.props.recipient.id,
|
||||||
});
|
auth,
|
||||||
i.setState({ loading: true });
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.createPrivateMessage(form));
|
||||||
|
}
|
||||||
|
i.setState({ loading: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleContentChange(val: string) {
|
handleContentChange(val: string) {
|
||||||
this.setState(s => ((s.privateMessageForm.content = val), s));
|
this.setState({ content: val });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancel(i: PrivateMessageForm) {
|
handleCancel(i: PrivateMessageForm) {
|
||||||
i.props.onCancel();
|
i.props.onCancel?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePreviewToggle(i: PrivateMessageForm, event: any) {
|
handlePreviewToggle(i: PrivateMessageForm, event: any) {
|
||||||
|
@ -240,18 +235,12 @@ export class PrivateMessageForm extends Component<
|
||||||
op == UserOperation.DeletePrivateMessage ||
|
op == UserOperation.DeletePrivateMessage ||
|
||||||
op == UserOperation.MarkPrivateMessageAsRead
|
op == UserOperation.MarkPrivateMessageAsRead
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<PrivateMessageResponse>(
|
let data = wsJsonToRes<PrivateMessageResponse>(msg);
|
||||||
msg,
|
|
||||||
PrivateMessageResponse
|
|
||||||
);
|
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
this.props.onEdit(data.private_message_view);
|
this.props.onEdit?.(data.private_message_view);
|
||||||
} else if (op == UserOperation.CreatePrivateMessage) {
|
} else if (op == UserOperation.CreatePrivateMessage) {
|
||||||
let data = wsJsonToRes<PrivateMessageResponse>(
|
let data = wsJsonToRes<PrivateMessageResponse>(msg);
|
||||||
msg,
|
this.props.onCreate?.(data.private_message_view);
|
||||||
PrivateMessageResponse
|
|
||||||
);
|
|
||||||
this.props.onCreate(data.private_message_view);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { WebSocketService } from "../../services";
|
import { WebSocketService } from "../../services";
|
||||||
import { auth, mdToHtml, wsClient } from "../../utils";
|
import { mdToHtml, myAuth, wsClient } from "../../utils";
|
||||||
import { Icon } from "../common/icon";
|
import { Icon } from "../common/icon";
|
||||||
import { PersonListing } from "../person/person-listing";
|
import { PersonListing } from "../person/person-listing";
|
||||||
|
|
||||||
|
@ -45,24 +45,21 @@ export class PrivateMessageReport extends Component<Props, any> {
|
||||||
<div>
|
<div>
|
||||||
{i18n.t("reason")}: {pmr.reason}
|
{i18n.t("reason")}: {pmr.reason}
|
||||||
</div>
|
</div>
|
||||||
{r.resolver.match({
|
{r.resolver && (
|
||||||
some: resolver => (
|
<div>
|
||||||
<div>
|
{pmr.resolved ? (
|
||||||
{pmr.resolved ? (
|
<T i18nKey="resolved_by">
|
||||||
<T i18nKey="resolved_by">
|
#
|
||||||
#
|
<PersonListing person={r.resolver} />
|
||||||
<PersonListing person={resolver} />
|
</T>
|
||||||
</T>
|
) : (
|
||||||
) : (
|
<T i18nKey="unresolved_by">
|
||||||
<T i18nKey="unresolved_by">
|
#
|
||||||
#
|
<PersonListing person={r.resolver} />
|
||||||
<PersonListing person={resolver} />
|
</T>
|
||||||
</T>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
<button
|
<button
|
||||||
className="btn btn-link btn-animate text-muted py-0"
|
className="btn btn-link btn-animate text-muted py-0"
|
||||||
onClick={linkEvent(this, this.handleResolveReport)}
|
onClick={linkEvent(this, this.handleResolveReport)}
|
||||||
|
@ -82,11 +79,16 @@ export class PrivateMessageReport extends Component<Props, any> {
|
||||||
|
|
||||||
handleResolveReport(i: PrivateMessageReport) {
|
handleResolveReport(i: PrivateMessageReport) {
|
||||||
let pmr = i.props.report.private_message_report;
|
let pmr = i.props.report.private_message_report;
|
||||||
let form = new ResolvePrivateMessageReport({
|
let auth = myAuth();
|
||||||
report_id: pmr.id,
|
if (auth) {
|
||||||
resolved: !pmr.resolved,
|
let form: ResolvePrivateMessageReport = {
|
||||||
auth: auth().unwrap(),
|
report_id: pmr.id,
|
||||||
});
|
resolved: !pmr.resolved,
|
||||||
WebSocketService.Instance.send(wsClient.resolvePrivateMessageReport(form));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.resolvePrivateMessageReport(form)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads/build";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
CreatePrivateMessageReport,
|
CreatePrivateMessageReport,
|
||||||
|
@ -6,11 +5,10 @@ import {
|
||||||
MarkPrivateMessageAsRead,
|
MarkPrivateMessageAsRead,
|
||||||
PersonSafe,
|
PersonSafe,
|
||||||
PrivateMessageView,
|
PrivateMessageView,
|
||||||
toUndefined,
|
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import { auth, mdToHtml, toast, wsClient } from "../../utils";
|
import { mdToHtml, myAuth, toast, wsClient } from "../../utils";
|
||||||
import { Icon } from "../common/icon";
|
import { Icon } from "../common/icon";
|
||||||
import { MomentTime } from "../common/moment-time";
|
import { MomentTime } from "../common/moment-time";
|
||||||
import { PersonListing } from "../person/person-listing";
|
import { PersonListing } from "../person/person-listing";
|
||||||
|
@ -22,7 +20,7 @@ interface PrivateMessageState {
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
viewSource: boolean;
|
viewSource: boolean;
|
||||||
showReportDialog: boolean;
|
showReportDialog: boolean;
|
||||||
reportReason: Option<string>;
|
reportReason?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PrivateMessageProps {
|
interface PrivateMessageProps {
|
||||||
|
@ -33,19 +31,17 @@ export class PrivateMessage extends Component<
|
||||||
PrivateMessageProps,
|
PrivateMessageProps,
|
||||||
PrivateMessageState
|
PrivateMessageState
|
||||||
> {
|
> {
|
||||||
private emptyState: PrivateMessageState = {
|
state: PrivateMessageState = {
|
||||||
showReply: false,
|
showReply: false,
|
||||||
showEdit: false,
|
showEdit: false,
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
viewSource: false,
|
viewSource: false,
|
||||||
showReportDialog: false,
|
showReportDialog: false,
|
||||||
reportReason: None,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
this.handleReplyCancel = this.handleReplyCancel.bind(this);
|
this.handleReplyCancel = this.handleReplyCancel.bind(this);
|
||||||
this.handlePrivateMessageCreate =
|
this.handlePrivateMessageCreate =
|
||||||
this.handlePrivateMessageCreate.bind(this);
|
this.handlePrivateMessageCreate.bind(this);
|
||||||
|
@ -53,13 +49,10 @@ export class PrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
get mine(): boolean {
|
get mine(): boolean {
|
||||||
return UserService.Instance.myUserInfo
|
return (
|
||||||
.map(
|
UserService.Instance.myUserInfo?.local_user_view.person.id ==
|
||||||
m =>
|
this.props.private_message_view.creator.id
|
||||||
m.local_user_view.person.id ==
|
);
|
||||||
this.props.private_message_view.creator.id
|
|
||||||
)
|
|
||||||
.unwrapOr(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -104,7 +97,7 @@ export class PrivateMessage extends Component<
|
||||||
{this.state.showEdit && (
|
{this.state.showEdit && (
|
||||||
<PrivateMessageForm
|
<PrivateMessageForm
|
||||||
recipient={otherPerson}
|
recipient={otherPerson}
|
||||||
privateMessageView={Some(message_view)}
|
privateMessageView={message_view}
|
||||||
onEdit={this.handlePrivateMessageEdit}
|
onEdit={this.handlePrivateMessageEdit}
|
||||||
onCreate={this.handlePrivateMessageCreate}
|
onCreate={this.handlePrivateMessageCreate}
|
||||||
onCancel={this.handleReplyCancel}
|
onCancel={this.handleReplyCancel}
|
||||||
|
@ -230,7 +223,7 @@ export class PrivateMessage extends Component<
|
||||||
className="form-control mr-2"
|
className="form-control mr-2"
|
||||||
placeholder={i18n.t("reason")}
|
placeholder={i18n.t("reason")}
|
||||||
required
|
required
|
||||||
value={toUndefined(this.state.reportReason)}
|
value={this.state.reportReason}
|
||||||
onInput={linkEvent(this, this.handleReportReasonChange)}
|
onInput={linkEvent(this, this.handleReportReasonChange)}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
|
@ -245,7 +238,6 @@ export class PrivateMessage extends Component<
|
||||||
{this.state.showReply && (
|
{this.state.showReply && (
|
||||||
<PrivateMessageForm
|
<PrivateMessageForm
|
||||||
recipient={otherPerson}
|
recipient={otherPerson}
|
||||||
privateMessageView={None}
|
|
||||||
onCreate={this.handlePrivateMessageCreate}
|
onCreate={this.handlePrivateMessageCreate}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -283,12 +275,15 @@ export class PrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteClick(i: PrivateMessage) {
|
handleDeleteClick(i: PrivateMessage) {
|
||||||
let form = new DeletePrivateMessage({
|
let auth = myAuth();
|
||||||
private_message_id: i.props.private_message_view.private_message.id,
|
if (auth) {
|
||||||
deleted: !i.props.private_message_view.private_message.deleted,
|
let form: DeletePrivateMessage = {
|
||||||
auth: auth().unwrap(),
|
private_message_id: i.props.private_message_view.private_message.id,
|
||||||
});
|
deleted: !i.props.private_message_view.private_message.deleted,
|
||||||
WebSocketService.Instance.send(wsClient.deletePrivateMessage(form));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.deletePrivateMessage(form));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReplyCancel() {
|
handleReplyCancel() {
|
||||||
|
@ -296,12 +291,15 @@ export class PrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMarkRead(i: PrivateMessage) {
|
handleMarkRead(i: PrivateMessage) {
|
||||||
let form = new MarkPrivateMessageAsRead({
|
let auth = myAuth();
|
||||||
private_message_id: i.props.private_message_view.private_message.id,
|
if (auth) {
|
||||||
read: !i.props.private_message_view.private_message.read,
|
let form: MarkPrivateMessageAsRead = {
|
||||||
auth: auth().unwrap(),
|
private_message_id: i.props.private_message_view.private_message.id,
|
||||||
});
|
read: !i.props.private_message_view.private_message.read,
|
||||||
WebSocketService.Instance.send(wsClient.markPrivateMessageAsRead(form));
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.markPrivateMessageAsRead(form));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMessageCollapse(i: PrivateMessage) {
|
handleMessageCollapse(i: PrivateMessage) {
|
||||||
|
@ -317,19 +315,23 @@ export class PrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReportReasonChange(i: PrivateMessage, event: any) {
|
handleReportReasonChange(i: PrivateMessage, event: any) {
|
||||||
i.setState({ reportReason: Some(event.target.value) });
|
i.setState({ reportReason: event.target.value });
|
||||||
}
|
}
|
||||||
|
|
||||||
handleReportSubmit(i: PrivateMessage, event: any) {
|
handleReportSubmit(i: PrivateMessage, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let form = new CreatePrivateMessageReport({
|
let auth = myAuth();
|
||||||
private_message_id: i.props.private_message_view.private_message.id,
|
let reason = i.state.reportReason;
|
||||||
reason: toUndefined(i.state.reportReason),
|
if (auth && reason) {
|
||||||
auth: auth().unwrap(),
|
let form: CreatePrivateMessageReport = {
|
||||||
});
|
private_message_id: i.props.private_message_view.private_message.id,
|
||||||
WebSocketService.Instance.send(wsClient.createPrivateMessageReport(form));
|
reason,
|
||||||
|
auth,
|
||||||
|
};
|
||||||
|
WebSocketService.Instance.send(wsClient.createPrivateMessageReport(form));
|
||||||
|
|
||||||
i.setState({ showReportDialog: false });
|
i.setState({ showReportDialog: false });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePrivateMessageEdit() {
|
handlePrivateMessageEdit() {
|
||||||
|
@ -337,14 +339,12 @@ export class PrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePrivateMessageCreate(message: PrivateMessageView) {
|
handlePrivateMessageCreate(message: PrivateMessageView) {
|
||||||
UserService.Instance.myUserInfo.match({
|
if (
|
||||||
some: mui => {
|
message.creator.id ==
|
||||||
if (message.creator.id == mui.local_user_view.person.id) {
|
UserService.Instance.myUserInfo?.local_user_view.person.id
|
||||||
this.setState({ showReply: false });
|
) {
|
||||||
toast(i18n.t("message_sent"));
|
this.setState({ showReply: false });
|
||||||
}
|
toast(i18n.t("message_sent"));
|
||||||
},
|
}
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { None, Option, Some } from "@sniptt/monads";
|
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommentResponse,
|
CommentResponse,
|
||||||
|
@ -30,7 +29,6 @@ import { i18n } from "../i18next";
|
||||||
import { CommentViewType, InitialFetchRequest } from "../interfaces";
|
import { CommentViewType, InitialFetchRequest } from "../interfaces";
|
||||||
import { WebSocketService } from "../services";
|
import { WebSocketService } from "../services";
|
||||||
import {
|
import {
|
||||||
auth,
|
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
choicesConfig,
|
choicesConfig,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
|
@ -45,6 +43,7 @@ import {
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
fetchUsers,
|
fetchUsers,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
|
myAuth,
|
||||||
numToSI,
|
numToSI,
|
||||||
personSelectName,
|
personSelectName,
|
||||||
personToChoice,
|
personToChoice,
|
||||||
|
@ -93,13 +92,13 @@ interface SearchState {
|
||||||
communityId: number;
|
communityId: number;
|
||||||
creatorId: number;
|
creatorId: number;
|
||||||
page: number;
|
page: number;
|
||||||
searchResponse: Option<SearchResponse>;
|
searchResponse?: SearchResponse;
|
||||||
communities: CommunityView[];
|
communities: CommunityView[];
|
||||||
creatorDetails: Option<GetPersonDetailsResponse>;
|
creatorDetails?: GetPersonDetailsResponse;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
searchText: string;
|
searchText: string;
|
||||||
resolveObjectResponse: Option<ResolveObjectResponse>;
|
resolveObjectResponse?: ResolveObjectResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UrlParams {
|
interface UrlParams {
|
||||||
|
@ -119,18 +118,11 @@ interface Combined {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Search extends Component<any, SearchState> {
|
export class Search extends Component<any, SearchState> {
|
||||||
private isoData = setIsoData(
|
private isoData = setIsoData(this.context);
|
||||||
this.context,
|
|
||||||
GetCommunityResponse,
|
|
||||||
ListCommunitiesResponse,
|
|
||||||
GetPersonDetailsResponse,
|
|
||||||
SearchResponse,
|
|
||||||
ResolveObjectResponse
|
|
||||||
);
|
|
||||||
private communityChoices: any;
|
private communityChoices: any;
|
||||||
private creatorChoices: any;
|
private creatorChoices: any;
|
||||||
private subscription: Subscription;
|
private subscription?: Subscription;
|
||||||
private emptyState: SearchState = {
|
state: SearchState = {
|
||||||
q: Search.getSearchQueryFromProps(this.props.match.params.q),
|
q: Search.getSearchQueryFromProps(this.props.match.params.q),
|
||||||
type_: Search.getSearchTypeFromProps(this.props.match.params.type),
|
type_: Search.getSearchTypeFromProps(this.props.match.params.type),
|
||||||
sort: Search.getSortTypeFromProps(this.props.match.params.sort),
|
sort: Search.getSortTypeFromProps(this.props.match.params.sort),
|
||||||
|
@ -143,9 +135,6 @@ export class Search extends Component<any, SearchState> {
|
||||||
this.props.match.params.community_id
|
this.props.match.params.community_id
|
||||||
),
|
),
|
||||||
creatorId: Search.getCreatorIdFromProps(this.props.match.params.creator_id),
|
creatorId: Search.getCreatorIdFromProps(this.props.match.params.creator_id),
|
||||||
searchResponse: None,
|
|
||||||
resolveObjectResponse: None,
|
|
||||||
creatorDetails: None,
|
|
||||||
loading: true,
|
loading: true,
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
communities: [],
|
communities: [],
|
||||||
|
@ -182,7 +171,6 @@ export class Search extends Component<any, SearchState> {
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = this.emptyState;
|
|
||||||
this.handleSortChange = this.handleSortChange.bind(this);
|
this.handleSortChange = this.handleSortChange.bind(this);
|
||||||
this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
|
this.handleListingTypeChange = this.handleListingTypeChange.bind(this);
|
||||||
this.handlePageChange = this.handlePageChange.bind(this);
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
|
@ -192,42 +180,38 @@ export class Search extends Component<any, SearchState> {
|
||||||
|
|
||||||
// Only fetch the data if coming from another route
|
// Only fetch the data if coming from another route
|
||||||
if (this.isoData.path == this.context.router.route.match.url) {
|
if (this.isoData.path == this.context.router.route.match.url) {
|
||||||
let communityRes = Some(
|
let communityRes = this.isoData.routeData[0] as
|
||||||
this.isoData.routeData[0] as GetCommunityResponse
|
| GetCommunityResponse
|
||||||
);
|
| undefined;
|
||||||
let communitiesRes = Some(
|
let communitiesRes = this.isoData.routeData[1] as
|
||||||
this.isoData.routeData[1] as ListCommunitiesResponse
|
| ListCommunitiesResponse
|
||||||
);
|
| undefined;
|
||||||
|
|
||||||
// This can be single or multiple communities given
|
// This can be single or multiple communities given
|
||||||
if (communitiesRes.isSome()) {
|
if (communitiesRes) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
communities: communitiesRes.unwrap().communities,
|
communities: communitiesRes.communities,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (communityRes.isSome()) {
|
if (communityRes) {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
communities: [communityRes.unwrap().community_view],
|
communities: [communityRes.community_view],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
creatorDetails: Some(
|
creatorDetails: this.isoData.routeData[2] as GetPersonDetailsResponse,
|
||||||
this.isoData.routeData[2] as GetPersonDetailsResponse
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.state.q != "") {
|
if (this.state.q != "") {
|
||||||
this.state = {
|
this.state = {
|
||||||
...this.state,
|
...this.state,
|
||||||
searchResponse: Some(this.isoData.routeData[3] as SearchResponse),
|
searchResponse: this.isoData.routeData[3] as SearchResponse,
|
||||||
resolveObjectResponse: Some(
|
resolveObjectResponse: this.isoData
|
||||||
this.isoData.routeData[4] as ResolveObjectResponse
|
.routeData[4] as ResolveObjectResponse,
|
||||||
),
|
|
||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
|
@ -240,7 +224,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.subscription.unsubscribe();
|
this.subscription?.unsubscribe();
|
||||||
saveScrollPosition(this.context);
|
saveScrollPosition(this.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,13 +250,12 @@ export class Search extends Component<any, SearchState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchCommunities() {
|
fetchCommunities() {
|
||||||
let listCommunitiesForm = new ListCommunities({
|
let listCommunitiesForm: ListCommunities = {
|
||||||
type_: Some(ListingType.All),
|
type_: ListingType.All,
|
||||||
sort: Some(SortType.TopAll),
|
sort: SortType.TopAll,
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
page: None,
|
auth: myAuth(false),
|
||||||
auth: auth(false).ok(),
|
};
|
||||||
});
|
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.listCommunities(listCommunitiesForm)
|
wsClient.listCommunities(listCommunitiesForm)
|
||||||
);
|
);
|
||||||
|
@ -281,71 +264,56 @@ export class Search extends Component<any, SearchState> {
|
||||||
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
||||||
let pathSplit = req.path.split("/");
|
let pathSplit = req.path.split("/");
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
let auth = req.auth;
|
||||||
|
|
||||||
let communityId = this.getCommunityIdFromProps(pathSplit[11]);
|
let communityId = this.getCommunityIdFromProps(pathSplit[11]);
|
||||||
let community_id: Option<number> =
|
let community_id = communityId == 0 ? undefined : communityId;
|
||||||
communityId == 0 ? None : Some(communityId);
|
if (community_id) {
|
||||||
community_id.match({
|
let getCommunityForm: GetCommunity = {
|
||||||
some: id => {
|
id: community_id,
|
||||||
let getCommunityForm = new GetCommunity({
|
auth,
|
||||||
id: Some(id),
|
};
|
||||||
name: None,
|
promises.push(req.client.getCommunity(getCommunityForm));
|
||||||
auth: req.auth,
|
promises.push(Promise.resolve());
|
||||||
});
|
} else {
|
||||||
promises.push(req.client.getCommunity(getCommunityForm));
|
let listCommunitiesForm: ListCommunities = {
|
||||||
promises.push(Promise.resolve());
|
type_: ListingType.All,
|
||||||
},
|
sort: SortType.TopAll,
|
||||||
none: () => {
|
limit: fetchLimit,
|
||||||
let listCommunitiesForm = new ListCommunities({
|
auth: req.auth,
|
||||||
type_: Some(ListingType.All),
|
};
|
||||||
sort: Some(SortType.TopAll),
|
promises.push(Promise.resolve());
|
||||||
limit: Some(fetchLimit),
|
promises.push(req.client.listCommunities(listCommunitiesForm));
|
||||||
page: None,
|
}
|
||||||
auth: req.auth,
|
|
||||||
});
|
|
||||||
promises.push(Promise.resolve());
|
|
||||||
promises.push(req.client.listCommunities(listCommunitiesForm));
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
let creatorId = this.getCreatorIdFromProps(pathSplit[13]);
|
let creatorId = this.getCreatorIdFromProps(pathSplit[13]);
|
||||||
let creator_id: Option<number> = creatorId == 0 ? None : Some(creatorId);
|
let creator_id = creatorId == 0 ? undefined : creatorId;
|
||||||
creator_id.match({
|
if (creator_id) {
|
||||||
some: id => {
|
let getCreatorForm: GetPersonDetails = {
|
||||||
let getCreatorForm = new GetPersonDetails({
|
person_id: creator_id,
|
||||||
person_id: Some(id),
|
auth: req.auth,
|
||||||
username: None,
|
};
|
||||||
sort: None,
|
promises.push(req.client.getPersonDetails(getCreatorForm));
|
||||||
page: None,
|
} else {
|
||||||
limit: None,
|
promises.push(Promise.resolve());
|
||||||
community_id: None,
|
}
|
||||||
saved_only: None,
|
|
||||||
auth: req.auth,
|
|
||||||
});
|
|
||||||
promises.push(req.client.getPersonDetails(getCreatorForm));
|
|
||||||
},
|
|
||||||
none: () => {
|
|
||||||
promises.push(Promise.resolve());
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
let form = new SearchForm({
|
let form: SearchForm = {
|
||||||
q: this.getSearchQueryFromProps(pathSplit[3]),
|
q: this.getSearchQueryFromProps(pathSplit[3]),
|
||||||
community_id,
|
community_id,
|
||||||
community_name: None,
|
|
||||||
creator_id,
|
creator_id,
|
||||||
type_: Some(this.getSearchTypeFromProps(pathSplit[5])),
|
type_: this.getSearchTypeFromProps(pathSplit[5]),
|
||||||
sort: Some(this.getSortTypeFromProps(pathSplit[7])),
|
sort: this.getSortTypeFromProps(pathSplit[7]),
|
||||||
listing_type: Some(this.getListingTypeFromProps(pathSplit[9])),
|
listing_type: this.getListingTypeFromProps(pathSplit[9]),
|
||||||
page: Some(this.getPageFromProps(pathSplit[15])),
|
page: this.getPageFromProps(pathSplit[15]),
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
auth: req.auth,
|
auth: req.auth,
|
||||||
});
|
};
|
||||||
|
|
||||||
let resolveObjectForm = new ResolveObject({
|
let resolveObjectForm: ResolveObject = {
|
||||||
q: this.getSearchQueryFromProps(pathSplit[3]),
|
q: this.getSearchQueryFromProps(pathSplit[3]),
|
||||||
auth: req.auth,
|
auth: req.auth,
|
||||||
});
|
};
|
||||||
|
|
||||||
if (form.q != "") {
|
if (form.q != "") {
|
||||||
promises.push(req.client.search(form));
|
promises.push(req.client.search(form));
|
||||||
|
@ -371,8 +339,6 @@ export class Search extends Component<any, SearchState> {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: true,
|
loading: true,
|
||||||
searchText: this.state.q,
|
searchText: this.state.q,
|
||||||
searchResponse: None,
|
|
||||||
resolveObjectResponse: None,
|
|
||||||
});
|
});
|
||||||
this.search();
|
this.search();
|
||||||
}
|
}
|
||||||
|
@ -391,8 +357,6 @@ export class Search extends Component<any, SearchState> {
|
||||||
<HtmlTags
|
<HtmlTags
|
||||||
title={this.documentTitle}
|
title={this.documentTitle}
|
||||||
path={this.context.router.route.match.url}
|
path={this.context.router.route.match.url}
|
||||||
description={None}
|
|
||||||
image={None}
|
|
||||||
/>
|
/>
|
||||||
<h5>{i18n.t("search")}</h5>
|
<h5>{i18n.t("search")}</h5>
|
||||||
{this.selects()}
|
{this.selects()}
|
||||||
|
@ -512,53 +476,47 @@ export class Search extends Component<any, SearchState> {
|
||||||
buildCombined(): Combined[] {
|
buildCombined(): Combined[] {
|
||||||
let combined: Combined[] = [];
|
let combined: Combined[] = [];
|
||||||
|
|
||||||
|
let resolveRes = this.state.resolveObjectResponse;
|
||||||
// Push the possible resolve / federated objects first
|
// Push the possible resolve / federated objects first
|
||||||
this.state.resolveObjectResponse.match({
|
if (resolveRes) {
|
||||||
some: res => {
|
let resolveComment = resolveRes.comment;
|
||||||
let resolveComment = res.comment;
|
if (resolveComment) {
|
||||||
if (resolveComment.isSome()) {
|
combined.push(this.commentViewToCombined(resolveComment));
|
||||||
combined.push(this.commentViewToCombined(resolveComment.unwrap()));
|
}
|
||||||
}
|
let resolvePost = resolveRes.post;
|
||||||
let resolvePost = res.post;
|
if (resolvePost) {
|
||||||
if (resolvePost.isSome()) {
|
combined.push(this.postViewToCombined(resolvePost));
|
||||||
combined.push(this.postViewToCombined(resolvePost.unwrap()));
|
}
|
||||||
}
|
let resolveCommunity = resolveRes.community;
|
||||||
let resolveCommunity = res.community;
|
if (resolveCommunity) {
|
||||||
if (resolveCommunity.isSome()) {
|
combined.push(this.communityViewToCombined(resolveCommunity));
|
||||||
combined.push(
|
}
|
||||||
this.communityViewToCombined(resolveCommunity.unwrap())
|
let resolveUser = resolveRes.person;
|
||||||
);
|
if (resolveUser) {
|
||||||
}
|
combined.push(this.personViewSafeToCombined(resolveUser));
|
||||||
let resolveUser = res.person;
|
}
|
||||||
if (resolveUser.isSome()) {
|
}
|
||||||
combined.push(this.personViewSafeToCombined(resolveUser.unwrap()));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Push the search results
|
// Push the search results
|
||||||
this.state.searchResponse.match({
|
let searchRes = this.state.searchResponse;
|
||||||
some: res => {
|
if (searchRes) {
|
||||||
pushNotNull(
|
pushNotNull(
|
||||||
combined,
|
combined,
|
||||||
res.comments?.map(e => this.commentViewToCombined(e))
|
searchRes.comments?.map(e => this.commentViewToCombined(e))
|
||||||
);
|
);
|
||||||
pushNotNull(
|
pushNotNull(
|
||||||
combined,
|
combined,
|
||||||
res.posts?.map(e => this.postViewToCombined(e))
|
searchRes.posts?.map(e => this.postViewToCombined(e))
|
||||||
);
|
);
|
||||||
pushNotNull(
|
pushNotNull(
|
||||||
combined,
|
combined,
|
||||||
res.communities?.map(e => this.communityViewToCombined(e))
|
searchRes.communities?.map(e => this.communityViewToCombined(e))
|
||||||
);
|
);
|
||||||
pushNotNull(
|
pushNotNull(
|
||||||
combined,
|
combined,
|
||||||
res.users?.map(e => this.personViewSafeToCombined(e))
|
searchRes.users?.map(e => this.personViewSafeToCombined(e))
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sort it
|
// Sort it
|
||||||
if (this.state.sort == SortType.New) {
|
if (this.state.sort == SortType.New) {
|
||||||
|
@ -588,9 +546,6 @@ export class Search extends Component<any, SearchState> {
|
||||||
<PostListing
|
<PostListing
|
||||||
key={(i.data as PostView).post.id}
|
key={(i.data as PostView).post.id}
|
||||||
post_view={i.data as PostView}
|
post_view={i.data as PostView}
|
||||||
duplicates={None}
|
|
||||||
moderators={None}
|
|
||||||
admins={None}
|
|
||||||
showCommunity
|
showCommunity
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
|
@ -611,9 +566,6 @@ export class Search extends Component<any, SearchState> {
|
||||||
]}
|
]}
|
||||||
viewType={CommentViewType.Flat}
|
viewType={CommentViewType.Flat}
|
||||||
viewOnly
|
viewOnly
|
||||||
moderators={None}
|
|
||||||
admins={None}
|
|
||||||
maxCommentsShown={None}
|
|
||||||
locked
|
locked
|
||||||
noIndent
|
noIndent
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
|
@ -636,15 +588,8 @@ export class Search extends Component<any, SearchState> {
|
||||||
|
|
||||||
comments() {
|
comments() {
|
||||||
let comments: CommentView[] = [];
|
let comments: CommentView[] = [];
|
||||||
|
pushNotNull(comments, this.state.resolveObjectResponse?.comment);
|
||||||
this.state.resolveObjectResponse.match({
|
pushNotNull(comments, this.state.searchResponse?.comments);
|
||||||
some: res => pushNotNull(comments, res.comment),
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
this.state.searchResponse.match({
|
|
||||||
some: res => pushNotNull(comments, res.comments),
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CommentNodes
|
<CommentNodes
|
||||||
|
@ -653,9 +598,6 @@ export class Search extends Component<any, SearchState> {
|
||||||
viewOnly
|
viewOnly
|
||||||
locked
|
locked
|
||||||
noIndent
|
noIndent
|
||||||
moderators={None}
|
|
||||||
admins={None}
|
|
||||||
maxCommentsShown={None}
|
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
siteLanguages={this.state.siteRes.discussion_languages}
|
siteLanguages={this.state.siteRes.discussion_languages}
|
||||||
|
@ -666,14 +608,8 @@ export class Search extends Component<any, SearchState> {
|
||||||
posts() {
|
posts() {
|
||||||
let posts: PostView[] = [];
|
let posts: PostView[] = [];
|
||||||
|
|
||||||
this.state.resolveObjectResponse.match({
|
pushNotNull(posts, this.state.resolveObjectResponse?.post);
|
||||||
some: res => pushNotNull(posts, res.post),
|
pushNotNull(posts, this.state.searchResponse?.posts);
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
this.state.searchResponse.match({
|
|
||||||
some: res => pushNotNull(posts, res.posts),
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -683,9 +619,6 @@ export class Search extends Component<any, SearchState> {
|
||||||
<PostListing
|
<PostListing
|
||||||
post_view={pv}
|
post_view={pv}
|
||||||
showCommunity
|
showCommunity
|
||||||
duplicates={None}
|
|
||||||
moderators={None}
|
|
||||||
admins={None}
|
|
||||||
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
enableNsfw={enableNsfw(this.state.siteRes)}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
allLanguages={this.state.siteRes.all_languages}
|
allLanguages={this.state.siteRes.all_languages}
|
||||||
|
@ -702,14 +635,8 @@ export class Search extends Component<any, SearchState> {
|
||||||
communities() {
|
communities() {
|
||||||
let communities: CommunityView[] = [];
|
let communities: CommunityView[] = [];
|
||||||
|
|
||||||
this.state.resolveObjectResponse.match({
|
pushNotNull(communities, this.state.resolveObjectResponse?.community);
|
||||||
some: res => pushNotNull(communities, res.community),
|
pushNotNull(communities, this.state.searchResponse?.communities);
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
this.state.searchResponse.match({
|
|
||||||
some: res => pushNotNull(communities, res.communities),
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -725,14 +652,8 @@ export class Search extends Component<any, SearchState> {
|
||||||
users() {
|
users() {
|
||||||
let users: PersonViewSafe[] = [];
|
let users: PersonViewSafe[] = [];
|
||||||
|
|
||||||
this.state.resolveObjectResponse.match({
|
pushNotNull(users, this.state.resolveObjectResponse?.person);
|
||||||
some: res => pushNotNull(users, res.person),
|
pushNotNull(users, this.state.searchResponse?.users);
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
this.state.searchResponse.match({
|
|
||||||
some: res => pushNotNull(users, res.users),
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -800,6 +721,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
creatorFilter() {
|
creatorFilter() {
|
||||||
|
let creatorPv = this.state.creatorDetails?.person_view;
|
||||||
return (
|
return (
|
||||||
<div className="form-group col-sm-6">
|
<div className="form-group col-sm-6">
|
||||||
<label className="col-form-label" htmlFor="creator-filter">
|
<label className="col-form-label" htmlFor="creator-filter">
|
||||||
|
@ -812,14 +734,11 @@ export class Search extends Component<any, SearchState> {
|
||||||
value={this.state.creatorId}
|
value={this.state.creatorId}
|
||||||
>
|
>
|
||||||
<option value="0">{i18n.t("all")}</option>
|
<option value="0">{i18n.t("all")}</option>
|
||||||
{this.state.creatorDetails.match({
|
{creatorPv && (
|
||||||
some: creator => (
|
<option value={creatorPv.person.id}>
|
||||||
<option value={creator.person_view.person.id}>
|
{personSelectName(creatorPv)}
|
||||||
{personSelectName(creator.person_view)}
|
</option>
|
||||||
</option>
|
)}
|
||||||
),
|
|
||||||
none: <></>,
|
|
||||||
})}
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -827,19 +746,24 @@ export class Search extends Component<any, SearchState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
resultsCount(): number {
|
resultsCount(): number {
|
||||||
let searchCount = this.state.searchResponse
|
let r = this.state.searchResponse;
|
||||||
.map(
|
|
||||||
r =>
|
|
||||||
r.posts?.length +
|
|
||||||
r.comments?.length +
|
|
||||||
r.communities?.length +
|
|
||||||
r.users?.length
|
|
||||||
)
|
|
||||||
.unwrapOr(0);
|
|
||||||
|
|
||||||
let resObjCount = this.state.resolveObjectResponse
|
let searchCount = r
|
||||||
.map(r => (r.post || r.person || r.community || r.comment ? 1 : 0))
|
? r.posts?.length +
|
||||||
.unwrapOr(0);
|
r.comments?.length +
|
||||||
|
r.communities?.length +
|
||||||
|
r.users?.length
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
let resolveRes = this.state.resolveObjectResponse;
|
||||||
|
let resObjCount = resolveRes
|
||||||
|
? resolveRes.post ||
|
||||||
|
resolveRes.person ||
|
||||||
|
resolveRes.community ||
|
||||||
|
resolveRes.comment
|
||||||
|
? 1
|
||||||
|
: 0
|
||||||
|
: 0;
|
||||||
|
|
||||||
return resObjCount + searchCount;
|
return resObjCount + searchCount;
|
||||||
}
|
}
|
||||||
|
@ -849,33 +773,33 @@ export class Search extends Component<any, SearchState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
search() {
|
search() {
|
||||||
let community_id: Option<number> =
|
let community_id =
|
||||||
this.state.communityId == 0 ? None : Some(this.state.communityId);
|
this.state.communityId == 0 ? undefined : this.state.communityId;
|
||||||
let creator_id: Option<number> =
|
let creator_id =
|
||||||
this.state.creatorId == 0 ? None : Some(this.state.creatorId);
|
this.state.creatorId == 0 ? undefined : this.state.creatorId;
|
||||||
|
|
||||||
let form = new SearchForm({
|
let auth = myAuth(false);
|
||||||
|
let form: SearchForm = {
|
||||||
q: this.state.q,
|
q: this.state.q,
|
||||||
community_id,
|
community_id,
|
||||||
community_name: None,
|
|
||||||
creator_id,
|
creator_id,
|
||||||
type_: Some(this.state.type_),
|
type_: this.state.type_,
|
||||||
sort: Some(this.state.sort),
|
sort: this.state.sort,
|
||||||
listing_type: Some(this.state.listingType),
|
listing_type: this.state.listingType,
|
||||||
page: Some(this.state.page),
|
page: this.state.page,
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
auth: auth(false).ok(),
|
auth,
|
||||||
});
|
};
|
||||||
|
|
||||||
let resolveObjectForm = new ResolveObject({
|
let resolveObjectForm: ResolveObject = {
|
||||||
q: this.state.q,
|
q: this.state.q,
|
||||||
auth: auth(false).ok(),
|
auth,
|
||||||
});
|
};
|
||||||
|
|
||||||
if (this.state.q != "") {
|
if (this.state.q != "") {
|
||||||
this.setState({
|
this.setState({
|
||||||
searchResponse: None,
|
searchResponse: undefined,
|
||||||
resolveObjectResponse: None,
|
resolveObjectResponse: undefined,
|
||||||
loading: true,
|
loading: true,
|
||||||
});
|
});
|
||||||
WebSocketService.Instance.send(wsClient.search(form));
|
WebSocketService.Instance.send(wsClient.search(form));
|
||||||
|
@ -1019,12 +943,7 @@ export class Search extends Component<any, SearchState> {
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
if (msg.error == "couldnt_find_object") {
|
if (msg.error == "couldnt_find_object") {
|
||||||
this.setState({
|
this.setState({
|
||||||
resolveObjectResponse: Some({
|
resolveObjectResponse: {},
|
||||||
comment: None,
|
|
||||||
post: None,
|
|
||||||
community: None,
|
|
||||||
person: None,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
this.checkFinishedLoading();
|
this.checkFinishedLoading();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1032,44 +951,35 @@ export class Search extends Component<any, SearchState> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.Search) {
|
} else if (op == UserOperation.Search) {
|
||||||
let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
|
let data = wsJsonToRes<SearchResponse>(msg);
|
||||||
this.setState({ searchResponse: Some(data) });
|
this.setState({ searchResponse: data });
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.checkFinishedLoading();
|
this.checkFinishedLoading();
|
||||||
restoreScrollPosition(this.context);
|
restoreScrollPosition(this.context);
|
||||||
} else if (op == UserOperation.CreateCommentLike) {
|
} else if (op == UserOperation.CreateCommentLike) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
let data = wsJsonToRes<CommentResponse>(msg);
|
||||||
createCommentLikeRes(
|
createCommentLikeRes(
|
||||||
data.comment_view,
|
data.comment_view,
|
||||||
this.state.searchResponse.map(r => r.comments).unwrapOr([])
|
this.state.searchResponse?.comments
|
||||||
);
|
);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.CreatePostLike) {
|
} else if (op == UserOperation.CreatePostLike) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
let data = wsJsonToRes<PostResponse>(msg);
|
||||||
createPostLikeFindRes(
|
createPostLikeFindRes(data.post_view, this.state.searchResponse?.posts);
|
||||||
data.post_view,
|
|
||||||
this.state.searchResponse.map(r => r.posts).unwrapOr([])
|
|
||||||
);
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.ListCommunities) {
|
} else if (op == UserOperation.ListCommunities) {
|
||||||
let data = wsJsonToRes<ListCommunitiesResponse>(
|
let data = wsJsonToRes<ListCommunitiesResponse>(msg);
|
||||||
msg,
|
|
||||||
ListCommunitiesResponse
|
|
||||||
);
|
|
||||||
this.setState({ communities: data.communities });
|
this.setState({ communities: data.communities });
|
||||||
this.setupCommunityFilter();
|
this.setupCommunityFilter();
|
||||||
} else if (op == UserOperation.ResolveObject) {
|
} else if (op == UserOperation.ResolveObject) {
|
||||||
let data = wsJsonToRes<ResolveObjectResponse>(msg, ResolveObjectResponse);
|
let data = wsJsonToRes<ResolveObjectResponse>(msg);
|
||||||
this.setState({ resolveObjectResponse: Some(data) });
|
this.setState({ resolveObjectResponse: data });
|
||||||
this.checkFinishedLoading();
|
this.checkFinishedLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkFinishedLoading() {
|
checkFinishedLoading() {
|
||||||
if (
|
if (this.state.searchResponse && this.state.resolveObjectResponse) {
|
||||||
this.state.searchResponse.isSome() &&
|
|
||||||
this.state.resolveObjectResponse.isSome()
|
|
||||||
) {
|
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { Either, Option } from "@sniptt/monads";
|
|
||||||
import { GetSiteResponse, LemmyHttp } from "lemmy-js-client";
|
import { GetSiteResponse, LemmyHttp } from "lemmy-js-client";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -22,16 +21,16 @@ declare global {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InitialFetchRequest {
|
export interface InitialFetchRequest {
|
||||||
auth: Option<string>;
|
auth?: string;
|
||||||
client: LemmyHttp;
|
client: LemmyHttp;
|
||||||
path: string;
|
path: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PostFormParams {
|
export interface PostFormParams {
|
||||||
name: Option<string>;
|
name?: string;
|
||||||
url: Option<string>;
|
url?: string;
|
||||||
body: Option<string>;
|
body?: string;
|
||||||
nameOrId: Option<Either<string, number>>;
|
nameOrId?: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CommentViewType {
|
export enum CommentViewType {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Inferno } from "inferno";
|
||||||
import { IRouteProps } from "inferno-router/dist/Route";
|
import { IRouteProps } from "inferno-router/dist/Route";
|
||||||
import { Communities } from "./components/community/communities";
|
import { Communities } from "./components/community/communities";
|
||||||
import { Community } from "./components/community/community";
|
import { Community } from "./components/community/community";
|
||||||
|
@ -24,6 +25,8 @@ import { Search } from "./components/search";
|
||||||
import { InitialFetchRequest } from "./interfaces";
|
import { InitialFetchRequest } from "./interfaces";
|
||||||
|
|
||||||
interface IRoutePropsWithFetch extends IRouteProps {
|
interface IRoutePropsWithFetch extends IRouteProps {
|
||||||
|
// TODO Make sure this one is good.
|
||||||
|
component: Inferno.ComponentClass;
|
||||||
fetchInitialData?(req: InitialFetchRequest): Promise<any>[];
|
fetchInitialData?(req: InitialFetchRequest): Promise<any>[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// import Cookies from 'js-cookie';
|
// import Cookies from 'js-cookie';
|
||||||
import { Err, None, Ok, Option, Result, Some } from "@sniptt/monads";
|
|
||||||
import IsomorphicCookie from "isomorphic-cookie";
|
import IsomorphicCookie from "isomorphic-cookie";
|
||||||
import jwt_decode from "jwt-decode";
|
import jwt_decode from "jwt-decode";
|
||||||
import { LoginResponse, MyUserInfo } from "lemmy-js-client";
|
import { LoginResponse, MyUserInfo } from "lemmy-js-client";
|
||||||
|
@ -21,8 +20,8 @@ interface JwtInfo {
|
||||||
|
|
||||||
export class UserService {
|
export class UserService {
|
||||||
private static _instance: UserService;
|
private static _instance: UserService;
|
||||||
public myUserInfo: Option<MyUserInfo> = None;
|
public myUserInfo?: MyUserInfo;
|
||||||
public jwtInfo: Option<JwtInfo> = None;
|
public jwtInfo?: JwtInfo;
|
||||||
public unreadInboxCountSub: BehaviorSubject<number> =
|
public unreadInboxCountSub: BehaviorSubject<number> =
|
||||||
new BehaviorSubject<number>(0);
|
new BehaviorSubject<number>(0);
|
||||||
public unreadReportCountSub: BehaviorSubject<number> =
|
public unreadReportCountSub: BehaviorSubject<number> =
|
||||||
|
@ -37,45 +36,41 @@ export class UserService {
|
||||||
public login(res: LoginResponse) {
|
public login(res: LoginResponse) {
|
||||||
let expires = new Date();
|
let expires = new Date();
|
||||||
expires.setDate(expires.getDate() + 365);
|
expires.setDate(expires.getDate() + 365);
|
||||||
res.jwt.match({
|
if (res.jwt) {
|
||||||
some: jwt => {
|
toast(i18n.t("logged_in"));
|
||||||
toast(i18n.t("logged_in"));
|
IsomorphicCookie.save("jwt", res.jwt, { expires, secure: isHttps });
|
||||||
IsomorphicCookie.save("jwt", jwt, { expires, secure: isHttps });
|
this.setJwtInfo();
|
||||||
this.setJwtInfo();
|
}
|
||||||
},
|
|
||||||
none: void 0,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public logout() {
|
public logout() {
|
||||||
this.jwtInfo = None;
|
this.jwtInfo = undefined;
|
||||||
this.myUserInfo = None;
|
this.myUserInfo = undefined;
|
||||||
IsomorphicCookie.remove("jwt"); // TODO is sometimes unreliable for some reason
|
IsomorphicCookie.remove("jwt"); // TODO is sometimes unreliable for some reason
|
||||||
document.cookie = "jwt=; Max-Age=0; path=/; domain=" + location.hostname;
|
document.cookie = "jwt=; Max-Age=0; path=/; domain=" + location.hostname;
|
||||||
location.reload();
|
location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
public auth(throwErr = true): Result<string, string> {
|
public auth(throwErr = true): string | undefined {
|
||||||
// Can't use match to convert to result for some reason
|
let jwt = this.jwtInfo?.jwt;
|
||||||
let jwt = this.jwtInfo.map(j => j.jwt);
|
if (jwt) {
|
||||||
if (jwt.isSome()) {
|
return jwt;
|
||||||
return Ok(jwt.unwrap());
|
|
||||||
} else {
|
} else {
|
||||||
let msg = "No JWT cookie found";
|
let msg = "No JWT cookie found";
|
||||||
if (throwErr && isBrowser()) {
|
if (throwErr && isBrowser()) {
|
||||||
console.log(msg);
|
console.error(msg);
|
||||||
toast(i18n.t("not_logged_in"), "danger");
|
toast(i18n.t("not_logged_in"), "danger");
|
||||||
}
|
}
|
||||||
return Err(msg);
|
return undefined;
|
||||||
|
// throw msg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private setJwtInfo() {
|
private setJwtInfo() {
|
||||||
let jwt = IsomorphicCookie.load("jwt");
|
let jwt: string | undefined = IsomorphicCookie.load("jwt");
|
||||||
|
|
||||||
if (jwt) {
|
if (jwt) {
|
||||||
let jwtInfo: JwtInfo = { jwt, claims: jwt_decode(jwt) };
|
this.jwtInfo = { jwt, claims: jwt_decode(jwt) };
|
||||||
this.jwtInfo = Some(jwtInfo);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import { Err, None, Ok, Option, Result, Some } from "@sniptt/monads";
|
|
||||||
import { ClassConstructor, deserialize, serialize } from "class-transformer";
|
|
||||||
import emojiShortName from "emoji-short-name";
|
import emojiShortName from "emoji-short-name";
|
||||||
import {
|
import {
|
||||||
BlockCommunityResponse,
|
BlockCommunityResponse,
|
||||||
|
@ -9,7 +7,6 @@ import {
|
||||||
CommentReportView,
|
CommentReportView,
|
||||||
CommentSortType,
|
CommentSortType,
|
||||||
CommentView,
|
CommentView,
|
||||||
CommunityBlockView,
|
|
||||||
CommunityModeratorView,
|
CommunityModeratorView,
|
||||||
CommunityView,
|
CommunityView,
|
||||||
GetSiteMetadata,
|
GetSiteMetadata,
|
||||||
|
@ -18,8 +15,6 @@ import {
|
||||||
LemmyHttp,
|
LemmyHttp,
|
||||||
LemmyWebsocket,
|
LemmyWebsocket,
|
||||||
ListingType,
|
ListingType,
|
||||||
MyUserInfo,
|
|
||||||
PersonBlockView,
|
|
||||||
PersonSafe,
|
PersonSafe,
|
||||||
PersonViewSafe,
|
PersonViewSafe,
|
||||||
PostReportView,
|
PostReportView,
|
||||||
|
@ -30,7 +25,6 @@ import {
|
||||||
Search,
|
Search,
|
||||||
SearchType,
|
SearchType,
|
||||||
SortType,
|
SortType,
|
||||||
toUndefined,
|
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { default as MarkdownIt } from "markdown-it";
|
import { default as MarkdownIt } from "markdown-it";
|
||||||
import markdown_it_container from "markdown-it-container";
|
import markdown_it_container from "markdown-it-container";
|
||||||
|
@ -190,11 +184,11 @@ export function mdToHtmlInline(text: string) {
|
||||||
return { __html: md.renderInline(text) };
|
return { __html: md.renderInline(text) };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUnixTime(text: string): number {
|
export function getUnixTime(text?: string): number | undefined {
|
||||||
return text ? new Date(text).getTime() / 1000 : undefined;
|
return text ? new Date(text).getTime() / 1000 : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function futureDaysToUnixTime(days: number): number {
|
export function futureDaysToUnixTime(days?: number): number | undefined {
|
||||||
return days
|
return days
|
||||||
? Math.trunc(
|
? Math.trunc(
|
||||||
new Date(Date.now() + 1000 * 60 * 60 * 24 * days).getTime() / 1000
|
new Date(Date.now() + 1000 * 60 * 60 * 24 * days).getTime() / 1000
|
||||||
|
@ -203,132 +197,89 @@ export function futureDaysToUnixTime(days: number): number {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canMod(
|
export function canMod(
|
||||||
mods: Option<CommunityModeratorView[]>,
|
|
||||||
admins: Option<PersonViewSafe[]>,
|
|
||||||
creator_id: number,
|
creator_id: number,
|
||||||
|
mods?: CommunityModeratorView[],
|
||||||
|
admins?: PersonViewSafe[],
|
||||||
myUserInfo = UserService.Instance.myUserInfo,
|
myUserInfo = UserService.Instance.myUserInfo,
|
||||||
onSelf = false
|
onSelf = false
|
||||||
): boolean {
|
): boolean {
|
||||||
// You can do moderator actions only on the mods added after you.
|
// You can do moderator actions only on the mods added after you.
|
||||||
let adminsThenMods = admins
|
let adminsThenMods =
|
||||||
.unwrapOr([])
|
admins
|
||||||
.map(a => a.person.id)
|
?.map(a => a.person.id)
|
||||||
.concat(mods.unwrapOr([]).map(m => m.moderator.id));
|
.concat(mods?.map(m => m.moderator.id) ?? []) ?? [];
|
||||||
|
|
||||||
return myUserInfo.match({
|
if (myUserInfo) {
|
||||||
some: me => {
|
let myIndex = adminsThenMods.findIndex(
|
||||||
let myIndex = adminsThenMods.findIndex(
|
id => id == myUserInfo.local_user_view.person.id
|
||||||
id => id == me.local_user_view.person.id
|
);
|
||||||
);
|
if (myIndex == -1) {
|
||||||
if (myIndex == -1) {
|
return false;
|
||||||
return false;
|
} else {
|
||||||
} else {
|
// onSelf +1 on mod actions not for yourself, IE ban, remove, etc
|
||||||
// onSelf +1 on mod actions not for yourself, IE ban, remove, etc
|
adminsThenMods = adminsThenMods.slice(0, myIndex + (onSelf ? 0 : 1));
|
||||||
adminsThenMods = adminsThenMods.slice(0, myIndex + (onSelf ? 0 : 1));
|
return !adminsThenMods.includes(creator_id);
|
||||||
return !adminsThenMods.includes(creator_id);
|
}
|
||||||
}
|
} else {
|
||||||
},
|
return false;
|
||||||
none: false,
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canAdmin(
|
export function canAdmin(
|
||||||
admins: Option<PersonViewSafe[]>,
|
creatorId: number,
|
||||||
creator_id: number,
|
admins?: PersonViewSafe[],
|
||||||
myUserInfo = UserService.Instance.myUserInfo,
|
myUserInfo = UserService.Instance.myUserInfo,
|
||||||
onSelf = false
|
onSelf = false
|
||||||
): boolean {
|
): boolean {
|
||||||
return canMod(None, admins, creator_id, myUserInfo, onSelf);
|
return canMod(creatorId, undefined, admins, myUserInfo, onSelf);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isMod(
|
export function isMod(
|
||||||
mods: Option<CommunityModeratorView[]>,
|
creatorId: number,
|
||||||
creator_id: number
|
mods?: CommunityModeratorView[]
|
||||||
): boolean {
|
): boolean {
|
||||||
return mods.match({
|
return mods?.map(m => m.moderator.id).includes(creatorId) ?? false;
|
||||||
some: mods => mods.map(m => m.moderator.id).includes(creator_id),
|
|
||||||
none: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function amMod(
|
export function amMod(
|
||||||
mods: Option<CommunityModeratorView[]>,
|
mods?: CommunityModeratorView[],
|
||||||
myUserInfo = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): boolean {
|
): boolean {
|
||||||
return myUserInfo.match({
|
return myUserInfo ? isMod(myUserInfo.local_user_view.person.id, mods) : false;
|
||||||
some: mui => isMod(mods, mui.local_user_view.person.id),
|
|
||||||
none: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isAdmin(
|
export function isAdmin(creatorId: number, admins?: PersonViewSafe[]): boolean {
|
||||||
admins: Option<PersonViewSafe[]>,
|
return admins?.map(a => a.person.id).includes(creatorId) ?? false;
|
||||||
creator_id: number
|
|
||||||
): boolean {
|
|
||||||
return admins.match({
|
|
||||||
some: admins => admins.map(a => a.person.id).includes(creator_id),
|
|
||||||
none: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function amAdmin(myUserInfo = UserService.Instance.myUserInfo): boolean {
|
export function amAdmin(myUserInfo = UserService.Instance.myUserInfo): boolean {
|
||||||
return myUserInfo
|
return myUserInfo?.local_user_view.person.admin ?? false;
|
||||||
.map(mui => mui.local_user_view.person.admin)
|
|
||||||
.unwrapOr(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function amCommunityCreator(
|
export function amCommunityCreator(
|
||||||
mods: Option<CommunityModeratorView[]>,
|
|
||||||
creator_id: number,
|
creator_id: number,
|
||||||
|
mods?: CommunityModeratorView[],
|
||||||
myUserInfo = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): boolean {
|
): boolean {
|
||||||
return mods.match({
|
let myId = myUserInfo?.local_user_view.person.id;
|
||||||
some: mods =>
|
// Don't allow mod actions on yourself
|
||||||
myUserInfo
|
return myId == mods?.at(0)?.moderator.id && myId != creator_id;
|
||||||
.map(mui => mui.local_user_view.person.id)
|
|
||||||
.match({
|
|
||||||
some: myId =>
|
|
||||||
myId == mods[0].moderator.id &&
|
|
||||||
// Don't allow mod actions on yourself
|
|
||||||
myId != creator_id,
|
|
||||||
none: false,
|
|
||||||
}),
|
|
||||||
none: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function amSiteCreator(
|
export function amSiteCreator(
|
||||||
admins: Option<PersonViewSafe[]>,
|
|
||||||
creator_id: number,
|
creator_id: number,
|
||||||
|
admins?: PersonViewSafe[],
|
||||||
myUserInfo = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): boolean {
|
): boolean {
|
||||||
return admins.match({
|
let myId = myUserInfo?.local_user_view.person.id;
|
||||||
some: admins =>
|
return myId == admins?.at(0)?.person.id && myId != creator_id;
|
||||||
myUserInfo
|
|
||||||
.map(mui => mui.local_user_view.person.id)
|
|
||||||
.match({
|
|
||||||
some: myId =>
|
|
||||||
myId == admins[0].person.id &&
|
|
||||||
// Don't allow mod actions on yourself
|
|
||||||
myId != creator_id,
|
|
||||||
none: false,
|
|
||||||
}),
|
|
||||||
none: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function amTopMod(
|
export function amTopMod(
|
||||||
mods: Option<CommunityModeratorView[]>,
|
mods: CommunityModeratorView[],
|
||||||
myUserInfo = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): boolean {
|
): boolean {
|
||||||
return mods.match({
|
return mods.at(0)?.moderator.id == myUserInfo?.local_user_view.person.id;
|
||||||
some: mods =>
|
|
||||||
myUserInfo.match({
|
|
||||||
some: mui => mods[0].moderator.id == mui.local_user_view.person.id,
|
|
||||||
none: false,
|
|
||||||
}),
|
|
||||||
none: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const imageRegex = /(http)?s?:?(\/\/[^"']*\.(?:jpg|jpeg|gif|png|svg|webp))/;
|
const imageRegex = /(http)?s?:?(\/\/[^"']*\.(?:jpg|jpeg|gif|png|svg|webp))/;
|
||||||
|
@ -386,9 +337,7 @@ export function routeSearchTypeToEnum(type: string): SearchType {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSiteMetadata(url: string) {
|
export async function getSiteMetadata(url: string) {
|
||||||
let form = new GetSiteMetadata({
|
let form: GetSiteMetadata = { url };
|
||||||
url,
|
|
||||||
});
|
|
||||||
let client = new LemmyHttp(httpBase);
|
let client = new LemmyHttp(httpBase);
|
||||||
return client.getSiteMetadata(form);
|
return client.getSiteMetadata(form);
|
||||||
}
|
}
|
||||||
|
@ -438,10 +387,8 @@ export function getLanguages(
|
||||||
override?: string,
|
override?: string,
|
||||||
myUserInfo = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): string[] {
|
): string[] {
|
||||||
let myLang = myUserInfo
|
let myLang = myUserInfo?.local_user_view.local_user.interface_language;
|
||||||
.map(m => m.local_user_view.local_user.interface_language)
|
let lang = override || myLang || "browser";
|
||||||
.unwrapOr("browser");
|
|
||||||
let lang = override || myLang;
|
|
||||||
|
|
||||||
if (lang == "browser" && isBrowser()) {
|
if (lang == "browser" && isBrowser()) {
|
||||||
return getBrowserLanguages();
|
return getBrowserLanguages();
|
||||||
|
@ -496,7 +443,7 @@ export async function setTheme(theme: string, forceReload = false) {
|
||||||
let cssLoc = `/css/themes/${theme}.css`;
|
let cssLoc = `/css/themes/${theme}.css`;
|
||||||
|
|
||||||
loadCss(theme, cssLoc);
|
loadCss(theme, cssLoc);
|
||||||
document.getElementById(theme).removeAttribute("disabled");
|
document.getElementById(theme)?.removeAttribute("disabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadCss(id: string, loc: string) {
|
export function loadCss(id: string, loc: string) {
|
||||||
|
@ -521,19 +468,15 @@ export function objectFlip(obj: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showAvatars(
|
export function showAvatars(
|
||||||
myUserInfo: Option<MyUserInfo> = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): boolean {
|
): boolean {
|
||||||
return myUserInfo
|
return myUserInfo?.local_user_view.local_user.show_avatars ?? true;
|
||||||
.map(m => m.local_user_view.local_user.show_avatars)
|
|
||||||
.unwrapOr(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showScores(
|
export function showScores(
|
||||||
myUserInfo: Option<MyUserInfo> = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): boolean {
|
): boolean {
|
||||||
return myUserInfo
|
return myUserInfo?.local_user_view.local_user.show_scores ?? true;
|
||||||
.map(m => m.local_user_view.local_user.show_scores)
|
|
||||||
.unwrapOr(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCakeDay(published: string): boolean {
|
export function isCakeDay(published: string): boolean {
|
||||||
|
@ -595,9 +538,9 @@ export function pictrsDeleteToast(
|
||||||
|
|
||||||
interface NotifyInfo {
|
interface NotifyInfo {
|
||||||
name: string;
|
name: string;
|
||||||
icon: Option<string>;
|
icon?: string;
|
||||||
link: string;
|
link: string;
|
||||||
body: string;
|
body?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function messageToastify(info: NotifyInfo, router: any) {
|
export function messageToastify(info: NotifyInfo, router: any) {
|
||||||
|
@ -607,7 +550,7 @@ export function messageToastify(info: NotifyInfo, router: any) {
|
||||||
|
|
||||||
let toast = Toastify({
|
let toast = Toastify({
|
||||||
text: `${htmlBody}<br />${info.name}`,
|
text: `${htmlBody}<br />${info.name}`,
|
||||||
avatar: toUndefined(info.icon),
|
avatar: info.icon,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
className: "text-dark",
|
className: "text-dark",
|
||||||
close: true,
|
close: true,
|
||||||
|
@ -663,7 +606,7 @@ function notify(info: NotifyInfo, router: any) {
|
||||||
else {
|
else {
|
||||||
var notification = new Notification(info.name, {
|
var notification = new Notification(info.name, {
|
||||||
...{ body: info.body },
|
...{ body: info.body },
|
||||||
...(info.icon.isSome() && { icon: info.icon.unwrap() }),
|
...(info.icon && { icon: info.icon }),
|
||||||
});
|
});
|
||||||
|
|
||||||
notification.onclick = (ev: Event): any => {
|
notification.onclick = (ev: Event): any => {
|
||||||
|
@ -790,15 +733,12 @@ export function getListingTypeFromProps(
|
||||||
defaultListingType: ListingType,
|
defaultListingType: ListingType,
|
||||||
myUserInfo = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): ListingType {
|
): ListingType {
|
||||||
|
let myLt = myUserInfo?.local_user_view.local_user.default_listing_type;
|
||||||
return props.match.params.listing_type
|
return props.match.params.listing_type
|
||||||
? routeListingTypeToEnum(props.match.params.listing_type)
|
? routeListingTypeToEnum(props.match.params.listing_type)
|
||||||
: myUserInfo.match({
|
: myLt
|
||||||
some: me =>
|
? Object.values(ListingType)[myLt]
|
||||||
Object.values(ListingType)[
|
: defaultListingType;
|
||||||
me.local_user_view.local_user.default_listing_type
|
|
||||||
],
|
|
||||||
none: defaultListingType,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getListingTypeFromPropsNoDefault(props: any): ListingType {
|
export function getListingTypeFromPropsNoDefault(props: any): ListingType {
|
||||||
|
@ -817,15 +757,12 @@ export function getSortTypeFromProps(
|
||||||
props: any,
|
props: any,
|
||||||
myUserInfo = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): SortType {
|
): SortType {
|
||||||
|
let mySortType = myUserInfo?.local_user_view.local_user.default_sort_type;
|
||||||
return props.match.params.sort
|
return props.match.params.sort
|
||||||
? routeSortTypeToEnum(props.match.params.sort)
|
? routeSortTypeToEnum(props.match.params.sort)
|
||||||
: myUserInfo.match({
|
: mySortType
|
||||||
some: mui =>
|
? Object.values(SortType)[mySortType]
|
||||||
Object.values(SortType)[
|
: SortType.Active;
|
||||||
mui.local_user_view.local_user.default_sort_type
|
|
||||||
],
|
|
||||||
none: SortType.Active,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPageFromProps(props: any): number {
|
export function getPageFromProps(props: any): number {
|
||||||
|
@ -838,22 +775,22 @@ export function getRecipientIdFromProps(props: any): number {
|
||||||
: 1;
|
: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getIdFromProps(props: any): Option<number> {
|
export function getIdFromProps(props: any): number | undefined {
|
||||||
let id: string = props.match.params.post_id;
|
let id = props.match.params.post_id;
|
||||||
return id ? Some(Number(id)) : None;
|
return id ? Number(id) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCommentIdFromProps(props: any): Option<number> {
|
export function getCommentIdFromProps(props: any): number | undefined {
|
||||||
let id: string = props.match.params.comment_id;
|
let id = props.match.params.comment_id;
|
||||||
return id ? Some(Number(id)) : None;
|
return id ? Number(id) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getUsernameFromProps(props: any): string {
|
export function getUsernameFromProps(props: any): string {
|
||||||
return props.match.params.username;
|
return props.match.params.username;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function editCommentRes(data: CommentView, comments: CommentView[]) {
|
export function editCommentRes(data: CommentView, comments?: CommentView[]) {
|
||||||
let found = comments.find(c => c.comment.id == data.comment.id);
|
let found = comments?.find(c => c.comment.id == data.comment.id);
|
||||||
if (found) {
|
if (found) {
|
||||||
found.comment.content = data.comment.content;
|
found.comment.content = data.comment.content;
|
||||||
found.comment.distinguished = data.comment.distinguished;
|
found.comment.distinguished = data.comment.distinguished;
|
||||||
|
@ -866,67 +803,60 @@ export function editCommentRes(data: CommentView, comments: CommentView[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function saveCommentRes(data: CommentView, comments: CommentView[]) {
|
export function saveCommentRes(data: CommentView, comments?: CommentView[]) {
|
||||||
let found = comments.find(c => c.comment.id == data.comment.id);
|
let found = comments?.find(c => c.comment.id == data.comment.id);
|
||||||
if (found) {
|
if (found) {
|
||||||
found.saved = data.saved;
|
found.saved = data.saved;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Should only use the return now, no state?
|
|
||||||
export function updatePersonBlock(
|
export function updatePersonBlock(
|
||||||
data: BlockPersonResponse,
|
data: BlockPersonResponse,
|
||||||
myUserInfo = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): Option<PersonBlockView[]> {
|
) {
|
||||||
return myUserInfo.match({
|
let mui = myUserInfo;
|
||||||
some: (mui: MyUserInfo) => {
|
if (mui) {
|
||||||
if (data.blocked) {
|
if (data.blocked) {
|
||||||
mui.person_blocks.push({
|
mui.person_blocks.push({
|
||||||
person: mui.local_user_view.person,
|
person: mui.local_user_view.person,
|
||||||
target: data.person_view.person,
|
target: data.person_view.person,
|
||||||
});
|
});
|
||||||
toast(`${i18n.t("blocked")} ${data.person_view.person.name}`);
|
toast(`${i18n.t("blocked")} ${data.person_view.person.name}`);
|
||||||
} else {
|
} else {
|
||||||
mui.person_blocks = mui.person_blocks.filter(
|
mui.person_blocks = mui.person_blocks.filter(
|
||||||
i => i.target.id != data.person_view.person.id
|
i => i.target.id != data.person_view.person.id
|
||||||
);
|
);
|
||||||
toast(`${i18n.t("unblocked")} ${data.person_view.person.name}`);
|
toast(`${i18n.t("unblocked")} ${data.person_view.person.name}`);
|
||||||
}
|
}
|
||||||
return Some(mui.person_blocks);
|
}
|
||||||
},
|
|
||||||
none: None,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateCommunityBlock(
|
export function updateCommunityBlock(
|
||||||
data: BlockCommunityResponse,
|
data: BlockCommunityResponse,
|
||||||
myUserInfo = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): Option<CommunityBlockView[]> {
|
) {
|
||||||
return myUserInfo.match({
|
let mui = myUserInfo;
|
||||||
some: (mui: MyUserInfo) => {
|
if (mui) {
|
||||||
if (data.blocked) {
|
if (data.blocked) {
|
||||||
mui.community_blocks.push({
|
mui.community_blocks.push({
|
||||||
person: mui.local_user_view.person,
|
person: mui.local_user_view.person,
|
||||||
community: data.community_view.community,
|
community: data.community_view.community,
|
||||||
});
|
});
|
||||||
toast(`${i18n.t("blocked")} ${data.community_view.community.name}`);
|
toast(`${i18n.t("blocked")} ${data.community_view.community.name}`);
|
||||||
} else {
|
} else {
|
||||||
mui.community_blocks = mui.community_blocks.filter(
|
mui.community_blocks = mui.community_blocks.filter(
|
||||||
i => i.community.id != data.community_view.community.id
|
i => i.community.id != data.community_view.community.id
|
||||||
);
|
);
|
||||||
toast(`${i18n.t("unblocked")} ${data.community_view.community.name}`);
|
toast(`${i18n.t("unblocked")} ${data.community_view.community.name}`);
|
||||||
}
|
}
|
||||||
return Some(mui.community_blocks);
|
}
|
||||||
},
|
|
||||||
none: None,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createCommentLikeRes(
|
export function createCommentLikeRes(
|
||||||
data: CommentView,
|
data: CommentView,
|
||||||
comments: CommentView[]
|
comments?: CommentView[]
|
||||||
) {
|
) {
|
||||||
let found = comments.find(c => c.comment.id === data.comment.id);
|
let found = comments?.find(c => c.comment.id === data.comment.id);
|
||||||
if (found) {
|
if (found) {
|
||||||
found.counts.score = data.counts.score;
|
found.counts.score = data.counts.score;
|
||||||
found.counts.upvotes = data.counts.upvotes;
|
found.counts.upvotes = data.counts.upvotes;
|
||||||
|
@ -937,14 +867,14 @@ export function createCommentLikeRes(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createPostLikeFindRes(data: PostView, posts: PostView[]) {
|
export function createPostLikeFindRes(data: PostView, posts?: PostView[]) {
|
||||||
let found = posts.find(p => p.post.id == data.post.id);
|
let found = posts?.find(p => p.post.id == data.post.id);
|
||||||
if (found) {
|
if (found) {
|
||||||
createPostLikeRes(data, found);
|
createPostLikeRes(data, found);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createPostLikeRes(data: PostView, post_view: PostView) {
|
export function createPostLikeRes(data: PostView, post_view?: PostView) {
|
||||||
if (post_view) {
|
if (post_view) {
|
||||||
post_view.counts.score = data.counts.score;
|
post_view.counts.score = data.counts.score;
|
||||||
post_view.counts.upvotes = data.counts.upvotes;
|
post_view.counts.upvotes = data.counts.upvotes;
|
||||||
|
@ -955,8 +885,8 @@ export function createPostLikeRes(data: PostView, post_view: PostView) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function editPostFindRes(data: PostView, posts: PostView[]) {
|
export function editPostFindRes(data: PostView, posts?: PostView[]) {
|
||||||
let found = posts.find(p => p.post.id == data.post.id);
|
let found = posts?.find(p => p.post.id == data.post.id);
|
||||||
if (found) {
|
if (found) {
|
||||||
editPostRes(data, found);
|
editPostRes(data, found);
|
||||||
}
|
}
|
||||||
|
@ -980,9 +910,9 @@ export function editPostRes(data: PostView, post: PostView) {
|
||||||
// TODO possible to make these generic?
|
// TODO possible to make these generic?
|
||||||
export function updatePostReportRes(
|
export function updatePostReportRes(
|
||||||
data: PostReportView,
|
data: PostReportView,
|
||||||
reports: PostReportView[]
|
reports?: PostReportView[]
|
||||||
) {
|
) {
|
||||||
let found = reports.find(p => p.post_report.id == data.post_report.id);
|
let found = reports?.find(p => p.post_report.id == data.post_report.id);
|
||||||
if (found) {
|
if (found) {
|
||||||
found.post_report = data.post_report;
|
found.post_report = data.post_report;
|
||||||
}
|
}
|
||||||
|
@ -990,9 +920,9 @@ export function updatePostReportRes(
|
||||||
|
|
||||||
export function updateCommentReportRes(
|
export function updateCommentReportRes(
|
||||||
data: CommentReportView,
|
data: CommentReportView,
|
||||||
reports: CommentReportView[]
|
reports?: CommentReportView[]
|
||||||
) {
|
) {
|
||||||
let found = reports.find(c => c.comment_report.id == data.comment_report.id);
|
let found = reports?.find(c => c.comment_report.id == data.comment_report.id);
|
||||||
if (found) {
|
if (found) {
|
||||||
found.comment_report = data.comment_report;
|
found.comment_report = data.comment_report;
|
||||||
}
|
}
|
||||||
|
@ -1000,9 +930,9 @@ export function updateCommentReportRes(
|
||||||
|
|
||||||
export function updatePrivateMessageReportRes(
|
export function updatePrivateMessageReportRes(
|
||||||
data: PrivateMessageReportView,
|
data: PrivateMessageReportView,
|
||||||
reports: PrivateMessageReportView[]
|
reports?: PrivateMessageReportView[]
|
||||||
) {
|
) {
|
||||||
let found = reports.find(
|
let found = reports?.find(
|
||||||
c => c.private_message_report.id == data.private_message_report.id
|
c => c.private_message_report.id == data.private_message_report.id
|
||||||
);
|
);
|
||||||
if (found) {
|
if (found) {
|
||||||
|
@ -1012,9 +942,9 @@ export function updatePrivateMessageReportRes(
|
||||||
|
|
||||||
export function updateRegistrationApplicationRes(
|
export function updateRegistrationApplicationRes(
|
||||||
data: RegistrationApplicationView,
|
data: RegistrationApplicationView,
|
||||||
applications: RegistrationApplicationView[]
|
applications?: RegistrationApplicationView[]
|
||||||
) {
|
) {
|
||||||
let found = applications.find(
|
let found = applications?.find(
|
||||||
ra => ra.registration_application.id == data.registration_application.id
|
ra => ra.registration_application.id == data.registration_application.id
|
||||||
);
|
);
|
||||||
if (found) {
|
if (found) {
|
||||||
|
@ -1057,13 +987,15 @@ export function buildCommentsTree(
|
||||||
let map = new Map<number, CommentNodeI>();
|
let map = new Map<number, CommentNodeI>();
|
||||||
let depthOffset = !parentComment
|
let depthOffset = !parentComment
|
||||||
? 0
|
? 0
|
||||||
: getDepthFromComment(comments[0].comment);
|
: getDepthFromComment(comments[0].comment) ?? 0;
|
||||||
|
|
||||||
for (let comment_view of comments) {
|
for (let comment_view of comments) {
|
||||||
|
let depthI = getDepthFromComment(comment_view.comment) ?? 0;
|
||||||
|
let depth = depthI ? depthI - depthOffset : 0;
|
||||||
let node: CommentNodeI = {
|
let node: CommentNodeI = {
|
||||||
comment_view: comment_view,
|
comment_view,
|
||||||
children: [],
|
children: [],
|
||||||
depth: getDepthFromComment(comment_view.comment) - depthOffset,
|
depth,
|
||||||
};
|
};
|
||||||
map.set(comment_view.comment.id, { ...node });
|
map.set(comment_view.comment.id, { ...node });
|
||||||
}
|
}
|
||||||
|
@ -1072,45 +1004,46 @@ export function buildCommentsTree(
|
||||||
|
|
||||||
// if its a parent comment fetch, then push the first comment to the top node.
|
// if its a parent comment fetch, then push the first comment to the top node.
|
||||||
if (parentComment) {
|
if (parentComment) {
|
||||||
tree.push(map.get(comments[0].comment.id));
|
let cNode = map.get(comments[0].comment.id);
|
||||||
|
if (cNode) {
|
||||||
|
tree.push(cNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let comment_view of comments) {
|
for (let comment_view of comments) {
|
||||||
let child = map.get(comment_view.comment.id);
|
let child = map.get(comment_view.comment.id);
|
||||||
let parent_id = getCommentParentId(comment_view.comment);
|
if (child) {
|
||||||
parent_id.match({
|
let parent_id = getCommentParentId(comment_view.comment);
|
||||||
some: parentId => {
|
if (parent_id) {
|
||||||
let parent = map.get(parentId);
|
let parent = map.get(parent_id);
|
||||||
// Necessary because blocked comment might not exist
|
// Necessary because blocked comment might not exist
|
||||||
if (parent) {
|
if (parent) {
|
||||||
parent.children.push(child);
|
parent.children.push(child);
|
||||||
}
|
}
|
||||||
},
|
} else {
|
||||||
none: () => {
|
|
||||||
if (!parentComment) {
|
if (!parentComment) {
|
||||||
tree.push(child);
|
tree.push(child);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return tree;
|
return tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCommentParentId(comment: CommentI): Option<number> {
|
export function getCommentParentId(comment?: CommentI): number | undefined {
|
||||||
let split = comment.path.split(".");
|
let split = comment?.path.split(".");
|
||||||
// remove the 0
|
// remove the 0
|
||||||
split.shift();
|
split?.shift();
|
||||||
|
|
||||||
if (split.length > 1) {
|
return split && split.length > 1
|
||||||
return Some(Number(split[split.length - 2]));
|
? Number(split.at(split.length - 2))
|
||||||
} else {
|
: undefined;
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDepthFromComment(comment: CommentI): number {
|
export function getDepthFromComment(comment?: CommentI): number | undefined {
|
||||||
return comment.path.split(".").length - 2;
|
let len = comment?.path.split(".").length;
|
||||||
|
return len ? len - 2 : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function insertCommentIntoTree(
|
export function insertCommentIntoTree(
|
||||||
|
@ -1125,43 +1058,36 @@ export function insertCommentIntoTree(
|
||||||
depth: 0,
|
depth: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
getCommentParentId(cv.comment).match({
|
let parentId = getCommentParentId(cv.comment);
|
||||||
some: parentId => {
|
if (parentId) {
|
||||||
let parentComment = searchCommentTree(tree, parentId);
|
let parent_comment = searchCommentTree(tree, parentId);
|
||||||
parentComment.match({
|
if (parent_comment) {
|
||||||
some: pComment => {
|
node.depth = parent_comment.depth + 1;
|
||||||
node.depth = pComment.depth + 1;
|
parent_comment.children.unshift(node);
|
||||||
pComment.children.unshift(node);
|
}
|
||||||
},
|
} else if (!parentComment) {
|
||||||
none: void 0,
|
tree.unshift(node);
|
||||||
});
|
}
|
||||||
},
|
|
||||||
none: () => {
|
|
||||||
if (!parentComment) {
|
|
||||||
tree.unshift(node);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function searchCommentTree(
|
export function searchCommentTree(
|
||||||
tree: CommentNodeI[],
|
tree: CommentNodeI[],
|
||||||
id: number
|
id: number
|
||||||
): Option<CommentNodeI> {
|
): CommentNodeI | undefined {
|
||||||
for (let node of tree) {
|
for (let node of tree) {
|
||||||
if (node.comment_view.comment.id === id) {
|
if (node.comment_view.comment.id === id) {
|
||||||
return Some(node);
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const child of node.children) {
|
for (const child of node.children) {
|
||||||
let res = searchCommentTree([child], id);
|
let res = searchCommentTree([child], id);
|
||||||
|
|
||||||
if (res.isSome()) {
|
if (res) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return None;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const colorList: string[] = [
|
export const colorList: string[] = [
|
||||||
|
@ -1209,55 +1135,23 @@ export function isBrowser() {
|
||||||
return typeof window !== "undefined";
|
return typeof window !== "undefined";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setIsoData<Type1, Type2, Type3, Type4, Type5>(
|
export function setIsoData(context: any): IsoData {
|
||||||
context: any,
|
|
||||||
cls1?: ClassConstructor<Type1>,
|
|
||||||
cls2?: ClassConstructor<Type2>,
|
|
||||||
cls3?: ClassConstructor<Type3>,
|
|
||||||
cls4?: ClassConstructor<Type4>,
|
|
||||||
cls5?: ClassConstructor<Type5>
|
|
||||||
): IsoData {
|
|
||||||
// If its the browser, you need to deserialize the data from the window
|
// If its the browser, you need to deserialize the data from the window
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
let json = window.isoData;
|
let json = window.isoData;
|
||||||
let routeData = json.routeData;
|
let routeData = json.routeData;
|
||||||
let routeDataOut: any[] = [];
|
let site_res = json.site_res;
|
||||||
|
|
||||||
// Can't do array looping because of specific type constructor required
|
|
||||||
if (routeData[0]) {
|
|
||||||
routeDataOut[0] = convertWindowJson(cls1, routeData[0]);
|
|
||||||
}
|
|
||||||
if (routeData[1]) {
|
|
||||||
routeDataOut[1] = convertWindowJson(cls2, routeData[1]);
|
|
||||||
}
|
|
||||||
if (routeData[2]) {
|
|
||||||
routeDataOut[2] = convertWindowJson(cls3, routeData[2]);
|
|
||||||
}
|
|
||||||
if (routeData[3]) {
|
|
||||||
routeDataOut[3] = convertWindowJson(cls4, routeData[3]);
|
|
||||||
}
|
|
||||||
if (routeData[4]) {
|
|
||||||
routeDataOut[4] = convertWindowJson(cls5, routeData[4]);
|
|
||||||
}
|
|
||||||
let site_res = convertWindowJson(GetSiteResponse, json.site_res);
|
|
||||||
|
|
||||||
let isoData: IsoData = {
|
let isoData: IsoData = {
|
||||||
path: json.path,
|
path: json.path,
|
||||||
site_res,
|
site_res,
|
||||||
routeData: routeDataOut,
|
routeData,
|
||||||
};
|
};
|
||||||
return isoData;
|
return isoData;
|
||||||
} else return context.router.staticContext;
|
} else return context.router.staticContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export function wsSubscribe(parseMessage: any): Subscription | undefined {
|
||||||
* Necessary since window ISOData can't store function types like Option
|
|
||||||
*/
|
|
||||||
export function convertWindowJson<T>(cls: ClassConstructor<T>, data: any): T {
|
|
||||||
return deserialize(cls, serialize(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function wsSubscribe(parseMessage: any): Subscription {
|
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
return WebSocketService.Instance.subject
|
return WebSocketService.Instance.subject
|
||||||
.pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
|
.pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
|
||||||
|
@ -1267,7 +1161,7 @@ export function wsSubscribe(parseMessage: any): Subscription {
|
||||||
() => console.log("complete")
|
() => console.log("complete")
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1305,9 +1199,8 @@ export function restoreScrollPosition(context: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showLocal(isoData: IsoData): boolean {
|
export function showLocal(isoData: IsoData): boolean {
|
||||||
return isoData.site_res.federated_instances
|
let linked = isoData.site_res.federated_instances?.linked;
|
||||||
.map(f => f.linked.length > 0)
|
return linked ? linked.length > 0 : false;
|
||||||
.unwrapOr(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChoicesValue {
|
export interface ChoicesValue {
|
||||||
|
@ -1332,35 +1225,29 @@ export function personToChoice(pvs: PersonViewSafe): ChoicesValue {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchCommunities(q: string) {
|
export async function fetchCommunities(q: string) {
|
||||||
let form = new Search({
|
let form: Search = {
|
||||||
q,
|
q,
|
||||||
type_: Some(SearchType.Communities),
|
type_: SearchType.Communities,
|
||||||
sort: Some(SortType.TopAll),
|
sort: SortType.TopAll,
|
||||||
listing_type: Some(ListingType.All),
|
listing_type: ListingType.All,
|
||||||
page: Some(1),
|
page: 1,
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
community_id: None,
|
auth: myAuth(false),
|
||||||
community_name: None,
|
};
|
||||||
creator_id: None,
|
|
||||||
auth: auth(false).ok(),
|
|
||||||
});
|
|
||||||
let client = new LemmyHttp(httpBase);
|
let client = new LemmyHttp(httpBase);
|
||||||
return client.search(form);
|
return client.search(form);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchUsers(q: string) {
|
export async function fetchUsers(q: string) {
|
||||||
let form = new Search({
|
let form: Search = {
|
||||||
q,
|
q,
|
||||||
type_: Some(SearchType.Users),
|
type_: SearchType.Users,
|
||||||
sort: Some(SortType.TopAll),
|
sort: SortType.TopAll,
|
||||||
listing_type: Some(ListingType.All),
|
listing_type: ListingType.All,
|
||||||
page: Some(1),
|
page: 1,
|
||||||
limit: Some(fetchLimit),
|
limit: fetchLimit,
|
||||||
community_id: None,
|
auth: myAuth(false),
|
||||||
community_name: None,
|
};
|
||||||
creator_id: None,
|
|
||||||
auth: auth(false).ok(),
|
|
||||||
});
|
|
||||||
let client = new LemmyHttp(httpBase);
|
let client = new LemmyHttp(httpBase);
|
||||||
return client.search(form);
|
return client.search(form);
|
||||||
}
|
}
|
||||||
|
@ -1406,7 +1293,7 @@ export function communitySelectName(cv: CommunityView): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function personSelectName(pvs: PersonViewSafe): string {
|
export function personSelectName(pvs: PersonViewSafe): string {
|
||||||
let pName = pvs.person.display_name.unwrapOr(pvs.person.name);
|
let pName = pvs.person.display_name ?? pvs.person.name;
|
||||||
return pvs.person.local ? pName : `${hostname(pvs.person.actor_id)}/${pName}`;
|
return pvs.person.local ? pName : `${hostname(pvs.person.actor_id)}/${pName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1430,8 +1317,8 @@ export function isBanned(ps: PersonSafe): boolean {
|
||||||
let expires = ps.ban_expires;
|
let expires = ps.ban_expires;
|
||||||
// Add Z to convert from UTC date
|
// Add Z to convert from UTC date
|
||||||
// TODO this check probably isn't necessary anymore
|
// TODO this check probably isn't necessary anymore
|
||||||
if (expires.isSome()) {
|
if (expires) {
|
||||||
if (ps.banned && new Date(expires.unwrap() + "Z") > new Date()) {
|
if (ps.banned && new Date(expires + "Z") > new Date()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1447,7 +1334,7 @@ export function pushNotNull(array: any[], new_item?: any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function auth(throwErr = true): Result<string, string> {
|
export function myAuth(throwErr = true): string | undefined {
|
||||||
return UserService.Instance.auth(throwErr);
|
return UserService.Instance.auth(throwErr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1471,26 +1358,18 @@ export function postToCommentSortType(sort: SortType): CommentSortType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function arrayGet<T>(arr: Array<T>, index: number): Result<T, string> {
|
|
||||||
let out = arr.at(index);
|
|
||||||
if (out == undefined) {
|
|
||||||
return Err("Index undefined");
|
|
||||||
} else {
|
|
||||||
return Ok(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function myFirstDiscussionLanguageId(
|
export function myFirstDiscussionLanguageId(
|
||||||
allLanguages: Language[],
|
allLanguages: Language[],
|
||||||
siteLanguages: number[],
|
siteLanguages: number[],
|
||||||
myUserInfo = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): Option<number> {
|
): number | undefined {
|
||||||
return arrayGet(
|
return selectableLanguages(
|
||||||
selectableLanguages(allLanguages, siteLanguages, false, false, myUserInfo),
|
allLanguages,
|
||||||
0
|
siteLanguages,
|
||||||
)
|
false,
|
||||||
.map(l => l.id)
|
false,
|
||||||
.ok();
|
myUserInfo
|
||||||
|
).at(0)?.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canCreateCommunity(
|
export function canCreateCommunity(
|
||||||
|
@ -1505,15 +1384,15 @@ export function isPostBlocked(
|
||||||
pv: PostView,
|
pv: PostView,
|
||||||
myUserInfo = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): boolean {
|
): boolean {
|
||||||
return myUserInfo
|
return (
|
||||||
.map(
|
(myUserInfo?.community_blocks
|
||||||
mui =>
|
.map(c => c.community.id)
|
||||||
mui.community_blocks
|
.includes(pv.community.id) ||
|
||||||
.map(c => c.community.id)
|
myUserInfo?.person_blocks
|
||||||
.includes(pv.community.id) ||
|
.map(p => p.target.id)
|
||||||
mui.person_blocks.map(p => p.target.id).includes(pv.creator.id)
|
.includes(pv.creator.id)) ??
|
||||||
)
|
false
|
||||||
.unwrapOr(false);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks to make sure you can view NSFW posts. Returns true if you can.
|
/// Checks to make sure you can view NSFW posts. Returns true if you can.
|
||||||
|
@ -1522,17 +1401,12 @@ export function nsfwCheck(
|
||||||
myUserInfo = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): boolean {
|
): boolean {
|
||||||
let nsfw = pv.post.nsfw || pv.community.nsfw;
|
let nsfw = pv.post.nsfw || pv.community.nsfw;
|
||||||
return (
|
let myShowNsfw = myUserInfo?.local_user_view.local_user.show_nsfw ?? false;
|
||||||
!nsfw ||
|
return !nsfw || (nsfw && myShowNsfw);
|
||||||
(nsfw &&
|
|
||||||
myUserInfo
|
|
||||||
.map(m => m.local_user_view.local_user.show_nsfw)
|
|
||||||
.unwrapOr(false))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRandomFromList<T>(list: T[]): T {
|
export function getRandomFromList<T>(list?: T[]): T | undefined {
|
||||||
return list[Math.floor(Math.random() * list.length)];
|
return list?.at(Math.floor(Math.random() * list.length));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1545,14 +1419,12 @@ export function getRandomFromList<T>(list: T[]): T {
|
||||||
export function selectableLanguages(
|
export function selectableLanguages(
|
||||||
allLanguages: Language[],
|
allLanguages: Language[],
|
||||||
siteLanguages: number[],
|
siteLanguages: number[],
|
||||||
showAll: boolean,
|
showAll?: boolean,
|
||||||
showSite: boolean,
|
showSite?: boolean,
|
||||||
myUserInfo = UserService.Instance.myUserInfo
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): Language[] {
|
): Language[] {
|
||||||
let allLangIds = allLanguages.map(l => l.id);
|
let allLangIds = allLanguages.map(l => l.id);
|
||||||
let myLangs = myUserInfo
|
let myLangs = myUserInfo?.discussion_languages ?? allLangIds;
|
||||||
.map(mui => mui.discussion_languages)
|
|
||||||
.unwrapOr(allLangIds);
|
|
||||||
myLangs = myLangs.length == 0 ? allLangIds : myLangs;
|
myLangs = myLangs.length == 0 ? allLangIds : myLangs;
|
||||||
let siteLangs = siteLanguages.length == 0 ? allLangIds : siteLanguages;
|
let siteLangs = siteLanguages.length == 0 ? allLangIds : siteLanguages;
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"strictNullChecks": false,
|
"strictNullChecks": true,
|
||||||
"noFallthroughCasesInSwitch": true
|
"noFallthroughCasesInSwitch": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
|
|
26
yarn.lock
26
yarn.lock
|
@ -1105,11 +1105,6 @@
|
||||||
domhandler "^4.2.0"
|
domhandler "^4.2.0"
|
||||||
selderee "^0.6.0"
|
selderee "^0.6.0"
|
||||||
|
|
||||||
"@sniptt/monads@^0.5.10":
|
|
||||||
version "0.5.10"
|
|
||||||
resolved "https://registry.yarnpkg.com/@sniptt/monads/-/monads-0.5.10.tgz#a80cd00738bbd682d36d36dd36bdc0bddc96eb76"
|
|
||||||
integrity sha512-+agDOv9DpDV+9e2zN/Vmdk+XaqGx5Sykl0fqhqgiJ90r18nsBkxe44DmZ2sA1HYK+MSsBeZBiAr6pq4w+5uhfw==
|
|
||||||
|
|
||||||
"@types/autosize@^4.0.0":
|
"@types/autosize@^4.0.0":
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/autosize/-/autosize-4.0.1.tgz#999a7c305b96766248044ebaac1a0299961f3b61"
|
resolved "https://registry.yarnpkg.com/@types/autosize/-/autosize-4.0.1.tgz#999a7c305b96766248044ebaac1a0299961f3b61"
|
||||||
|
@ -2263,11 +2258,6 @@ cidr-regex@1.0.6:
|
||||||
resolved "https://registry.yarnpkg.com/cidr-regex/-/cidr-regex-1.0.6.tgz#74abfd619df370b9d54ab14475568e97dd64c0c1"
|
resolved "https://registry.yarnpkg.com/cidr-regex/-/cidr-regex-1.0.6.tgz#74abfd619df370b9d54ab14475568e97dd64c0c1"
|
||||||
integrity sha512-vIIQZtDT0y3GmcVqi4Uhd43s7HKn5DtH8/CcmHe/XG1Vb4JpUgOfTynZzYSo1zeB+j4GbA38Eu2P9UTbIzDw5g==
|
integrity sha512-vIIQZtDT0y3GmcVqi4Uhd43s7HKn5DtH8/CcmHe/XG1Vb4JpUgOfTynZzYSo1zeB+j4GbA38Eu2P9UTbIzDw5g==
|
||||||
|
|
||||||
class-transformer@^0.5.1:
|
|
||||||
version "0.5.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336"
|
|
||||||
integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==
|
|
||||||
|
|
||||||
classnames@^2.3.1:
|
classnames@^2.3.1:
|
||||||
version "2.3.2"
|
version "2.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
|
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
|
||||||
|
@ -4982,15 +4972,12 @@ lcid@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
invert-kv "^1.0.0"
|
invert-kv "^1.0.0"
|
||||||
|
|
||||||
lemmy-js-client@0.17.0-rc.57:
|
lemmy-js-client@0.17.0-rc.61:
|
||||||
version "0.17.0-rc.57"
|
version "0.17.0-rc.61"
|
||||||
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.57.tgz#f7a243ed53542810e7446b0a28ad162f3e913254"
|
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.61.tgz#c01e129a3d4c3483ecf337f1e4acf0ad91f9684f"
|
||||||
integrity sha512-7kZHi0B+jiKc50itTwngkS5Vxcuvux3LjgD28IXZ049cWQgZDqer6BCmudcbViP+dAoyWs9Fh2SyWkYFhv7bwQ==
|
integrity sha512-xauBCD5i4vlUEWqsTMIXLCXeIjAK7ivVIN3C/g+RMAM7mD3CTcRkDZUerwnvLipIfr7V/4iYLWZW0orBaiV1CQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@sniptt/monads" "^0.5.10"
|
|
||||||
class-transformer "^0.5.1"
|
|
||||||
node-fetch "2.6.6"
|
node-fetch "2.6.6"
|
||||||
reflect-metadata "^0.1.13"
|
|
||||||
|
|
||||||
levn@^0.4.1:
|
levn@^0.4.1:
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
|
@ -6969,11 +6956,6 @@ redux@^4.1.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.9.2"
|
"@babel/runtime" "^7.9.2"
|
||||||
|
|
||||||
reflect-metadata@^0.1.13:
|
|
||||||
version "0.1.13"
|
|
||||||
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08"
|
|
||||||
integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
|
|
||||||
|
|
||||||
regenerate-unicode-properties@^10.1.0:
|
regenerate-unicode-properties@^10.1.0:
|
||||||
version "10.1.0"
|
version "10.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c"
|
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c"
|
||||||
|
|
Loading…
Reference in a new issue