mirror of
https://github.com/LemmyNet/lemmy-ui.git
synced 2024-11-25 22:01:13 +00:00
Adding option types 2 (#689)
* Not working, because of wrong API types. * Adding Rust-style Result and Option types. - Fixes #646 * Updating to use new lemmy-js-client with Options.
This commit is contained in:
parent
d41e19f3f1
commit
d905c91e1b
60 changed files with 5883 additions and 4485 deletions
|
@ -52,11 +52,13 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.17.9",
|
"@babel/core": "^7.17.9",
|
||||||
|
"@babel/plugin-proposal-decorators": "^7.18.2",
|
||||||
"@babel/plugin-transform-runtime": "^7.17.0",
|
"@babel/plugin-transform-runtime": "^7.17.0",
|
||||||
"@babel/plugin-transform-typescript": "^7.16.1",
|
"@babel/plugin-transform-typescript": "^7.16.1",
|
||||||
"@babel/preset-env": "7.16.11",
|
"@babel/preset-env": "7.16.11",
|
||||||
"@babel/preset-typescript": "^7.16.0",
|
"@babel/preset-typescript": "^7.16.0",
|
||||||
"@babel/runtime": "^7.17.9",
|
"@babel/runtime": "^7.17.9",
|
||||||
|
"@sniptt/monads": "^0.5.10",
|
||||||
"@types/autosize": "^4.0.0",
|
"@types/autosize": "^4.0.0",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
"@types/node": "^17.0.29",
|
"@types/node": "^17.0.29",
|
||||||
|
@ -67,6 +69,7 @@
|
||||||
"babel-plugin-inferno": "^6.4.0",
|
"babel-plugin-inferno": "^6.4.0",
|
||||||
"bootstrap": "^5.1.3",
|
"bootstrap": "^5.1.3",
|
||||||
"bootswatch": "^5.1.3",
|
"bootswatch": "^5.1.3",
|
||||||
|
"class-transformer": "^0.5.1",
|
||||||
"clean-webpack-plugin": "^4.0.0",
|
"clean-webpack-plugin": "^4.0.0",
|
||||||
"copy-webpack-plugin": "^10.2.4",
|
"copy-webpack-plugin": "^10.2.4",
|
||||||
"css-loader": "^6.7.1",
|
"css-loader": "^6.7.1",
|
||||||
|
@ -74,7 +77,7 @@
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"husky": "^7.0.4",
|
"husky": "^7.0.4",
|
||||||
"import-sort-style-module": "^6.0.0",
|
"import-sort-style-module": "^6.0.0",
|
||||||
"lemmy-js-client": "0.16.4",
|
"lemmy-js-client": "0.17.0-rc.30",
|
||||||
"lint-staged": "^12.4.1",
|
"lint-staged": "^12.4.1",
|
||||||
"mini-css-extract-plugin": "^2.6.0",
|
"mini-css-extract-plugin": "^2.6.0",
|
||||||
"node-fetch": "^2.6.1",
|
"node-fetch": "^2.6.1",
|
||||||
|
@ -82,6 +85,7 @@
|
||||||
"prettier-plugin-import-sort": "^0.0.7",
|
"prettier-plugin-import-sort": "^0.0.7",
|
||||||
"prettier-plugin-organize-imports": "^2.3.4",
|
"prettier-plugin-organize-imports": "^2.3.4",
|
||||||
"prettier-plugin-packagejson": "^2.2.17",
|
"prettier-plugin-packagejson": "^2.2.17",
|
||||||
|
"reflect-metadata": "^0.1.13",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"run-node-webpack-plugin": "^1.3.0",
|
"run-node-webpack-plugin": "^1.3.0",
|
||||||
"sass-loader": "^12.6.0",
|
"sass-loader": "^12.6.0",
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
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 { initializeSite } from "../shared/utils";
|
import { convertWindowJson, initializeSite } from "../shared/utils";
|
||||||
|
|
||||||
const site = window.isoData.site_res;
|
const site = convertWindowJson(GetSiteResponse, window.isoData.site_res);
|
||||||
initializeSite(site);
|
initializeSite(site);
|
||||||
|
|
||||||
const wrapper = (
|
const wrapper = (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<App siteRes={window.isoData.site_res} />
|
<App />
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
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";
|
||||||
|
@ -5,7 +7,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 } from "lemmy-js-client";
|
import { GetSite, GetSiteResponse, LemmyHttp, toOption } 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";
|
||||||
|
@ -18,7 +20,7 @@ import {
|
||||||
IsoData,
|
IsoData,
|
||||||
} from "../shared/interfaces";
|
} from "../shared/interfaces";
|
||||||
import { routes } from "../shared/routes";
|
import { routes } from "../shared/routes";
|
||||||
import { initializeSite, setOptionalAuth } from "../shared/utils";
|
import { initializeSite } from "../shared/utils";
|
||||||
|
|
||||||
const server = express();
|
const server = express();
|
||||||
const [hostname, port] = process.env["LEMMY_UI_HOST"]
|
const [hostname, port] = process.env["LEMMY_UI_HOST"]
|
||||||
|
@ -115,10 +117,9 @@ 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: string = IsomorphicCookie.load("jwt", req);
|
let auth: Option<string> = toOption(IsomorphicCookie.load("jwt", req));
|
||||||
|
|
||||||
let getSiteForm: GetSite = {};
|
let getSiteForm = new GetSite({ auth });
|
||||||
setOptionalAuth(getSiteForm, auth);
|
|
||||||
|
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
|
@ -138,8 +139,8 @@ 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"
|
||||||
);
|
);
|
||||||
delete getSiteForm.auth;
|
getSiteForm.auth = None;
|
||||||
delete initialFetchReq.auth;
|
initialFetchReq.auth = None;
|
||||||
try_site = await initialFetchReq.client.getSite(getSiteForm);
|
try_site = await initialFetchReq.client.getSite(getSiteForm);
|
||||||
}
|
}
|
||||||
let site: GetSiteResponse = try_site;
|
let site: GetSiteResponse = try_site;
|
||||||
|
@ -170,7 +171,7 @@ server.get("/*", async (req, res) => {
|
||||||
|
|
||||||
const wrapper = (
|
const wrapper = (
|
||||||
<StaticRouter location={req.url} context={isoData}>
|
<StaticRouter location={req.url} context={isoData}>
|
||||||
<App siteRes={isoData.site_res} />
|
<App />
|
||||||
</StaticRouter>
|
</StaticRouter>
|
||||||
);
|
);
|
||||||
if (context.url) {
|
if (context.url) {
|
||||||
|
@ -194,7 +195,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 = ${serialize(isoData)}</script>
|
<script>window.isoData = ${serializeO(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 -->
|
||||||
|
|
|
@ -2,53 +2,46 @@ import { Component } from "inferno";
|
||||||
import { Helmet } from "inferno-helmet";
|
import { Helmet } from "inferno-helmet";
|
||||||
import { Provider } from "inferno-i18next-dess";
|
import { Provider } from "inferno-i18next-dess";
|
||||||
import { Route, Switch } from "inferno-router";
|
import { Route, Switch } from "inferno-router";
|
||||||
import { GetSiteResponse } from "lemmy-js-client";
|
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { routes } from "../../routes";
|
import { routes } from "../../routes";
|
||||||
import { favIconPngUrl, favIconUrl } from "../../utils";
|
import { favIconPngUrl, favIconUrl, setIsoData } from "../../utils";
|
||||||
import { Footer } from "./footer";
|
import { Footer } from "./footer";
|
||||||
import { Navbar } from "./navbar";
|
import { Navbar } from "./navbar";
|
||||||
import { NoMatch } from "./no-match";
|
import { NoMatch } from "./no-match";
|
||||||
import "./styles.scss";
|
import "./styles.scss";
|
||||||
import { Theme } from "./theme";
|
import { Theme } from "./theme";
|
||||||
|
|
||||||
export interface AppProps {
|
export class App extends Component<any, any> {
|
||||||
siteRes: GetSiteResponse;
|
private isoData = setIsoData(this.context);
|
||||||
}
|
|
||||||
|
|
||||||
export class App extends Component<AppProps, any> {
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
}
|
}
|
||||||
render() {
|
render() {
|
||||||
let siteRes = this.props.siteRes;
|
let siteRes = this.isoData.site_res;
|
||||||
|
let siteView = siteRes.site_view;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Provider i18next={i18n}>
|
<Provider i18next={i18n}>
|
||||||
<div>
|
<div>
|
||||||
<Theme
|
<Theme defaultTheme={siteView.map(s => s.site.default_theme)} />
|
||||||
myUserInfo={siteRes.my_user}
|
{siteView
|
||||||
defaultTheme={siteRes?.site_view?.site?.default_theme}
|
.andThen(s => s.site.icon)
|
||||||
/>
|
.match({
|
||||||
{siteRes &&
|
some: icon => (
|
||||||
siteRes.site_view &&
|
<Helmet>
|
||||||
this.props.siteRes.site_view.site.icon && (
|
<link
|
||||||
<Helmet>
|
id="favicon"
|
||||||
<link
|
rel="shortcut icon"
|
||||||
id="favicon"
|
type="image/x-icon"
|
||||||
rel="shortcut icon"
|
href={icon || favIconUrl}
|
||||||
type="image/x-icon"
|
/>
|
||||||
href={this.props.siteRes.site_view.site.icon || favIconUrl}
|
<link rel="apple-touch-icon" href={icon || favIconPngUrl} />
|
||||||
/>
|
</Helmet>
|
||||||
<link
|
),
|
||||||
rel="apple-touch-icon"
|
none: <></>,
|
||||||
href={
|
})}
|
||||||
this.props.siteRes.site_view.site.icon || favIconPngUrl
|
<Navbar siteRes={siteRes} />
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Helmet>
|
|
||||||
)}
|
|
||||||
<Navbar site_res={this.props.siteRes} />
|
|
||||||
<div class="mt-4 p-0 fl-1">
|
<div class="mt-4 p-0 fl-1">
|
||||||
<Switch>
|
<Switch>
|
||||||
{routes.map(({ path, exact, component: C, ...rest }) => (
|
{routes.map(({ path, exact, component: C, ...rest }) => (
|
||||||
|
@ -62,7 +55,7 @@ export class App extends Component<AppProps, any> {
|
||||||
<Route render={props => <NoMatch {...props} />} />
|
<Route render={props => <NoMatch {...props} />} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
<Footer site={this.props.siteRes} />
|
<Footer site={siteRes} />
|
||||||
</div>
|
</div>
|
||||||
</Provider>
|
</Provider>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -32,7 +32,9 @@ export class Footer extends Component<FooterProps, any> {
|
||||||
{i18n.t("modlog")}
|
{i18n.t("modlog")}
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
{this.props.site.site_view?.site.legal_information && (
|
{this.props.site.site_view
|
||||||
|
.andThen(s => s.site.legal_information)
|
||||||
|
.isSome() && (
|
||||||
<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,3 +1,4 @@
|
||||||
|
import { None, Some } 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 {
|
||||||
|
@ -11,12 +12,15 @@ import {
|
||||||
GetUnreadRegistrationApplicationCountResponse,
|
GetUnreadRegistrationApplicationCountResponse,
|
||||||
PrivateMessageResponse,
|
PrivateMessageResponse,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
amAdmin,
|
||||||
|
auth,
|
||||||
donateLemmyUrl,
|
donateLemmyUrl,
|
||||||
getLanguages,
|
getLanguages,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
|
@ -27,19 +31,16 @@ import {
|
||||||
showAvatars,
|
showAvatars,
|
||||||
toast,
|
toast,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { Icon } from "../common/icon";
|
import { Icon } from "../common/icon";
|
||||||
import { PictrsImage } from "../common/pictrs-image";
|
import { PictrsImage } from "../common/pictrs-image";
|
||||||
|
|
||||||
interface NavbarProps {
|
interface NavbarProps {
|
||||||
site_res: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NavbarState {
|
interface NavbarState {
|
||||||
isLoggedIn: boolean;
|
|
||||||
expanded: boolean;
|
expanded: boolean;
|
||||||
unreadInboxCount: number;
|
unreadInboxCount: number;
|
||||||
unreadReportCount: number;
|
unreadReportCount: number;
|
||||||
|
@ -58,7 +59,6 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
private unreadApplicationCountSub: Subscription;
|
private unreadApplicationCountSub: Subscription;
|
||||||
private searchTextField: RefObject<HTMLInputElement>;
|
private searchTextField: RefObject<HTMLInputElement>;
|
||||||
emptyState: NavbarState = {
|
emptyState: NavbarState = {
|
||||||
isLoggedIn: !!this.props.site_res.my_user,
|
|
||||||
unreadInboxCount: 0,
|
unreadInboxCount: 0,
|
||||||
unreadReportCount: 0,
|
unreadReportCount: 0,
|
||||||
unreadApplicationCount: 0,
|
unreadApplicationCount: 0,
|
||||||
|
@ -81,18 +81,13 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
// Subscribe to jwt changes
|
// Subscribe to jwt changes
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
this.searchTextField = createRef();
|
this.searchTextField = createRef();
|
||||||
console.log(`isLoggedIn = ${this.state.isLoggedIn}`);
|
|
||||||
|
|
||||||
// On the first load, check the unreads
|
// On the first load, check the unreads
|
||||||
if (this.state.isLoggedIn == false) {
|
if (UserService.Instance.myUserInfo.isSome()) {
|
||||||
// setTheme(data.my_user.theme, true);
|
|
||||||
// i18n.changeLanguage(getLanguage());
|
|
||||||
// i18n.changeLanguage('de');
|
|
||||||
} else {
|
|
||||||
this.requestNotificationPermission();
|
this.requestNotificationPermission();
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.userJoin({
|
wsClient.userJoin({
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
this.fetchUnreads();
|
this.fetchUnreads();
|
||||||
|
@ -100,13 +95,11 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
|
|
||||||
this.userSub = UserService.Instance.jwtSub.subscribe(res => {
|
this.userSub = UserService.Instance.jwtSub.subscribe(res => {
|
||||||
// A login
|
// A login
|
||||||
if (res !== undefined) {
|
if (res.isSome()) {
|
||||||
this.requestNotificationPermission();
|
this.requestNotificationPermission();
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.getSite({ auth: authField() })
|
wsClient.getSite({ auth: res.map(r => r.jwt) })
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
this.setState({ isLoggedIn: false });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -157,32 +150,28 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
|
|
||||||
// TODO class active corresponding to current page
|
// TODO class active corresponding to current page
|
||||||
navbar() {
|
navbar() {
|
||||||
let myUserInfo =
|
|
||||||
UserService.Instance.myUserInfo || this.props.site_res.my_user;
|
|
||||||
let person = myUserInfo?.local_user_view.person;
|
|
||||||
return (
|
return (
|
||||||
<nav class="navbar navbar-expand-lg navbar-light shadow-sm p-0 px-3">
|
<nav class="navbar navbar-expand-md navbar-light shadow-sm p-0 px-3">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{this.props.site_res.site_view && (
|
{this.props.siteRes.site_view.match({
|
||||||
<NavLink
|
some: siteView => (
|
||||||
to="/"
|
<NavLink
|
||||||
onMouseUp={linkEvent(this, this.handleHideExpandNavbar)}
|
to="/"
|
||||||
title={
|
onMouseUp={linkEvent(this, this.handleHideExpandNavbar)}
|
||||||
this.props.site_res.site_view.site.description ||
|
title={siteView.site.description.unwrapOr(siteView.site.name)}
|
||||||
this.props.site_res.site_view.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({
|
||||||
>
|
some: icon =>
|
||||||
{this.props.site_res.site_view.site.icon && showAvatars() && (
|
showAvatars() && <PictrsImage src={icon} icon />,
|
||||||
<PictrsImage
|
none: <></>,
|
||||||
src={this.props.site_res.site_view.site.icon}
|
})}
|
||||||
icon
|
{siteView.site.name}
|
||||||
/>
|
</NavLink>
|
||||||
)}
|
),
|
||||||
{this.props.site_res.site_view.site.name}
|
none: <></>,
|
||||||
</NavLink>
|
})}
|
||||||
)}
|
{UserService.Instance.myUserInfo.isSome() && (
|
||||||
{this.state.isLoggedIn && (
|
|
||||||
<>
|
<>
|
||||||
<ul class="navbar-nav ml-auto">
|
<ul class="navbar-nav ml-auto">
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
|
@ -204,7 +193,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
{UserService.Instance.myUserInfo?.moderates.length > 0 && (
|
{this.moderatesSomething && (
|
||||||
<ul class="navbar-nav ml-1">
|
<ul class="navbar-nav ml-1">
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink
|
<NavLink
|
||||||
|
@ -226,8 +215,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
{UserService.Instance.myUserInfo?.local_user_view.person
|
{this.amAdmin && (
|
||||||
.admin && (
|
|
||||||
<ul class="navbar-nav ml-1">
|
<ul class="navbar-nav ml-1">
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink
|
<NavLink
|
||||||
|
@ -312,7 +300,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="navbar-nav my-2">
|
<ul class="navbar-nav my-2">
|
||||||
{this.canAdmin && (
|
{this.amAdmin && (
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink
|
<NavLink
|
||||||
to="/admin"
|
to="/admin"
|
||||||
|
@ -358,7 +346,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
)}
|
)}
|
||||||
{this.state.isLoggedIn ? (
|
{UserService.Instance.myUserInfo.isSome() ? (
|
||||||
<>
|
<>
|
||||||
<ul class="navbar-nav my-2">
|
<ul class="navbar-nav my-2">
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
|
@ -380,7 +368,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
{UserService.Instance.myUserInfo?.moderates.length > 0 && (
|
{this.moderatesSomething && (
|
||||||
<ul class="navbar-nav my-2">
|
<ul class="navbar-nav my-2">
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink
|
<NavLink
|
||||||
|
@ -402,8 +390,7 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
{UserService.Instance.myUserInfo?.local_user_view.person
|
{this.amAdmin && (
|
||||||
.admin && (
|
|
||||||
<ul class="navbar-nav my-2">
|
<ul class="navbar-nav my-2">
|
||||||
<li className="nav-item">
|
<li className="nav-item">
|
||||||
<NavLink
|
<NavLink
|
||||||
|
@ -427,69 +414,81 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
<ul class="navbar-nav">
|
{UserService.Instance.myUserInfo
|
||||||
<li class="nav-item dropdown">
|
.map(m => m.local_user_view.person)
|
||||||
<button
|
.match({
|
||||||
class="nav-link btn btn-link dropdown-toggle"
|
some: person => (
|
||||||
onClick={linkEvent(this, this.handleToggleDropdown)}
|
<ul class="navbar-nav">
|
||||||
id="navbarDropdown"
|
<li class="nav-item dropdown">
|
||||||
role="button"
|
|
||||||
aria-expanded="false"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
{person.avatar && showAvatars() && (
|
|
||||||
<PictrsImage src={person.avatar} icon />
|
|
||||||
)}
|
|
||||||
{person.display_name
|
|
||||||
? person.display_name
|
|
||||||
: person.name}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
{this.state.showDropdown && (
|
|
||||||
<div
|
|
||||||
class="dropdown-content"
|
|
||||||
onMouseLeave={linkEvent(
|
|
||||||
this,
|
|
||||||
this.handleToggleDropdown
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<li className="nav-item">
|
|
||||||
<NavLink
|
|
||||||
to={`/u/${UserService.Instance.myUserInfo.local_user_view.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 class="dropdown-divider" />
|
|
||||||
</li>
|
|
||||||
<li className="nav-item">
|
|
||||||
<button
|
<button
|
||||||
className="nav-link btn btn-link"
|
class="nav-link btn btn-link dropdown-toggle"
|
||||||
onClick={linkEvent(this, this.handleLogoutClick)}
|
onClick={linkEvent(this, this.handleToggleDropdown)}
|
||||||
title="test"
|
id="navbarDropdown"
|
||||||
|
role="button"
|
||||||
|
aria-expanded="false"
|
||||||
>
|
>
|
||||||
<Icon icon="log-out" classes="mr-1" />
|
<span>
|
||||||
{i18n.t("logout")}
|
{showAvatars() &&
|
||||||
|
person.avatar.match({
|
||||||
|
some: avatar => (
|
||||||
|
<PictrsImage src={avatar} icon />
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
|
{person.display_name.unwrapOr(person.name)}
|
||||||
|
</span>
|
||||||
</button>
|
</button>
|
||||||
|
{this.state.showDropdown && (
|
||||||
|
<div
|
||||||
|
class="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 class="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>
|
</li>
|
||||||
</div>
|
</ul>
|
||||||
)}
|
),
|
||||||
</li>
|
none: <></>,
|
||||||
</ul>
|
})}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<ul class="navbar-nav my-2">
|
<ul class="navbar-nav my-2">
|
||||||
|
@ -521,6 +520,24 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get moderatesSomething(): boolean {
|
||||||
|
return (
|
||||||
|
UserService.Instance.myUserInfo.map(m => m.moderates).unwrapOr([])
|
||||||
|
.length > 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get amAdmin(): boolean {
|
||||||
|
return amAdmin(Some(this.props.siteRes.admins));
|
||||||
|
}
|
||||||
|
|
||||||
|
get canCreateCommunity(): boolean {
|
||||||
|
let adminOnly = this.props.siteRes.site_view
|
||||||
|
.map(s => s.site.community_creation_admin_only)
|
||||||
|
.unwrapOr(false);
|
||||||
|
return !adminOnly || this.amAdmin;
|
||||||
|
}
|
||||||
|
|
||||||
handleToggleExpandNavbar(i: Navbar) {
|
handleToggleExpandNavbar(i: Navbar) {
|
||||||
i.state.expanded = !i.state.expanded;
|
i.state.expanded = !i.state.expanded;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
@ -561,8 +578,6 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
handleLogoutClick(i: Navbar) {
|
handleLogoutClick(i: Navbar) {
|
||||||
i.setState({ showDropdown: false, expanded: false });
|
i.setState({ showDropdown: false, expanded: false });
|
||||||
UserService.Instance.logout();
|
UserService.Instance.logout();
|
||||||
window.location.href = "/";
|
|
||||||
location.reload();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleToggleDropdown(i: Navbar) {
|
handleToggleDropdown(i: Navbar) {
|
||||||
|
@ -576,98 +591,117 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
if (msg.error == "not_logged_in") {
|
if (msg.error == "not_logged_in") {
|
||||||
UserService.Instance.logout();
|
UserService.Instance.logout();
|
||||||
location.reload();
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else if (msg.reconnect) {
|
} else if (msg.reconnect) {
|
||||||
console.log(i18n.t("websocket_reconnected"));
|
console.log(i18n.t("websocket_reconnected"));
|
||||||
WebSocketService.Instance.send(
|
if (UserService.Instance.myUserInfo.isSome()) {
|
||||||
wsClient.userJoin({
|
WebSocketService.Instance.send(
|
||||||
auth: authField(),
|
wsClient.userJoin({
|
||||||
})
|
auth: auth().unwrap(),
|
||||||
);
|
})
|
||||||
this.fetchUnreads();
|
);
|
||||||
|
this.fetchUnreads();
|
||||||
|
}
|
||||||
} else if (op == UserOperation.GetUnreadCount) {
|
} else if (op == UserOperation.GetUnreadCount) {
|
||||||
let data = wsJsonToRes<GetUnreadCountResponse>(msg).data;
|
let data = wsJsonToRes<GetUnreadCountResponse>(
|
||||||
|
msg,
|
||||||
|
GetUnreadCountResponse
|
||||||
|
);
|
||||||
this.state.unreadInboxCount =
|
this.state.unreadInboxCount =
|
||||||
data.replies + data.mentions + data.private_messages;
|
data.replies + data.mentions + data.private_messages;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
this.sendUnreadCount();
|
this.sendUnreadCount();
|
||||||
} else if (op == UserOperation.GetReportCount) {
|
} else if (op == UserOperation.GetReportCount) {
|
||||||
let data = wsJsonToRes<GetReportCountResponse>(msg).data;
|
let data = wsJsonToRes<GetReportCountResponse>(
|
||||||
|
msg,
|
||||||
|
GetReportCountResponse
|
||||||
|
);
|
||||||
this.state.unreadReportCount = data.post_reports + data.comment_reports;
|
this.state.unreadReportCount = data.post_reports + data.comment_reports;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
this.sendReportUnread();
|
this.sendReportUnread();
|
||||||
} else if (op == UserOperation.GetUnreadRegistrationApplicationCount) {
|
} else if (op == UserOperation.GetUnreadRegistrationApplicationCount) {
|
||||||
let data =
|
let data = wsJsonToRes<GetUnreadRegistrationApplicationCountResponse>(
|
||||||
wsJsonToRes<GetUnreadRegistrationApplicationCountResponse>(msg).data;
|
msg,
|
||||||
|
GetUnreadRegistrationApplicationCountResponse
|
||||||
|
);
|
||||||
this.state.unreadApplicationCount = data.registration_applications;
|
this.state.unreadApplicationCount = data.registration_applications;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
this.sendApplicationUnread();
|
this.sendApplicationUnread();
|
||||||
} else if (op == UserOperation.GetSite) {
|
} else if (op == UserOperation.GetSite) {
|
||||||
// This is only called on a successful login
|
// This is only called on a successful login
|
||||||
let data = wsJsonToRes<GetSiteResponse>(msg).data;
|
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
||||||
console.log(data.my_user);
|
|
||||||
UserService.Instance.myUserInfo = data.my_user;
|
UserService.Instance.myUserInfo = data.my_user;
|
||||||
setTheme(
|
UserService.Instance.myUserInfo.match({
|
||||||
UserService.Instance.myUserInfo.local_user_view.local_user.theme
|
some: mui => {
|
||||||
);
|
setTheme(mui.local_user_view.local_user.theme);
|
||||||
i18n.changeLanguage(getLanguages()[0]);
|
i18n.changeLanguage(getLanguages()[0]);
|
||||||
this.state.isLoggedIn = true;
|
this.setState(this.state);
|
||||||
this.setState(this.state);
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
} else if (op == UserOperation.CreateComment) {
|
} else if (op == UserOperation.CreateComment) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
|
|
||||||
if (this.state.isLoggedIn) {
|
UserService.Instance.myUserInfo.match({
|
||||||
if (
|
some: mui => {
|
||||||
data.recipient_ids.includes(
|
if (data.recipient_ids.includes(mui.local_user_view.local_user.id)) {
|
||||||
UserService.Instance.myUserInfo.local_user_view.local_user.id
|
this.state.unreadInboxCount++;
|
||||||
)
|
this.setState(this.state);
|
||||||
) {
|
this.sendUnreadCount();
|
||||||
this.state.unreadInboxCount++;
|
notifyComment(data.comment_view, this.context.router);
|
||||||
this.setState(this.state);
|
}
|
||||||
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>(msg).data;
|
let data = wsJsonToRes<PrivateMessageResponse>(
|
||||||
|
msg,
|
||||||
|
PrivateMessageResponse
|
||||||
|
);
|
||||||
|
|
||||||
if (this.state.isLoggedIn) {
|
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.state.unreadInboxCount++;
|
) {
|
||||||
this.setState(this.state);
|
this.state.unreadInboxCount++;
|
||||||
this.sendUnreadCount();
|
this.setState(this.state);
|
||||||
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: GetUnreadCount = {
|
let unreadForm = new GetUnreadCount({
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.getUnreadCount(unreadForm));
|
WebSocketService.Instance.send(wsClient.getUnreadCount(unreadForm));
|
||||||
|
|
||||||
console.log("Fetching reports...");
|
console.log("Fetching reports...");
|
||||||
|
|
||||||
let reportCountForm: GetReportCount = {
|
let reportCountForm = new GetReportCount({
|
||||||
auth: authField(),
|
community_id: None,
|
||||||
};
|
auth: auth().unwrap(),
|
||||||
|
});
|
||||||
WebSocketService.Instance.send(wsClient.getReportCount(reportCountForm));
|
WebSocketService.Instance.send(wsClient.getReportCount(reportCountForm));
|
||||||
|
|
||||||
if (UserService.Instance.myUserInfo?.local_user_view.person.admin) {
|
if (this.amAdmin) {
|
||||||
console.log("Fetching applications...");
|
console.log("Fetching applications...");
|
||||||
|
|
||||||
let applicationCountForm: GetUnreadRegistrationApplicationCount = {
|
let applicationCountForm = new GetUnreadRegistrationApplicationCount({
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.getUnreadRegistrationApplicationCount(applicationCountForm)
|
wsClient.getUnreadRegistrationApplicationCount(applicationCountForm)
|
||||||
);
|
);
|
||||||
|
@ -694,23 +728,8 @@ export class Navbar extends Component<NavbarProps, NavbarState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get canAdmin(): boolean {
|
|
||||||
return (
|
|
||||||
UserService.Instance.myUserInfo &&
|
|
||||||
this.props.site_res.admins
|
|
||||||
.map(a => a.person.id)
|
|
||||||
.includes(UserService.Instance.myUserInfo.local_user_view.person.id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get canCreateCommunity(): boolean {
|
|
||||||
let adminOnly =
|
|
||||||
this.props.site_res.site_view?.site.community_creation_admin_only;
|
|
||||||
return !adminOnly || this.canAdmin;
|
|
||||||
}
|
|
||||||
|
|
||||||
requestNotificationPermission() {
|
requestNotificationPermission() {
|
||||||
if (UserService.Instance.myUserInfo) {
|
if (UserService.Instance.myUserInfo.isSome()) {
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
if (!Notification) {
|
if (!Notification) {
|
||||||
toast(i18n.t("notifications_error"), "danger");
|
toast(i18n.t("notifications_error"), "danger");
|
||||||
|
|
|
@ -1,16 +1,18 @@
|
||||||
|
import { Option } from "@sniptt/monads";
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import { Helmet } from "inferno-helmet";
|
import { Helmet } from "inferno-helmet";
|
||||||
import { MyUserInfo } from "lemmy-js-client";
|
import { UserService } from "../../services";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
myUserInfo: MyUserInfo | undefined;
|
defaultTheme: Option<string>;
|
||||||
defaultTheme?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Theme extends Component<Props> {
|
export class Theme extends Component<Props> {
|
||||||
render() {
|
render() {
|
||||||
let user = this.props.myUserInfo;
|
let user = UserService.Instance.myUserInfo;
|
||||||
let hasTheme = user && user.local_user_view.local_user.theme !== "browser";
|
let hasTheme = user
|
||||||
|
.map(m => m.local_user_view.local_user.theme !== "browser")
|
||||||
|
.unwrapOr(false);
|
||||||
|
|
||||||
if (hasTheme) {
|
if (hasTheme) {
|
||||||
return (
|
return (
|
||||||
|
@ -18,20 +20,22 @@ export class Theme extends Component<Props> {
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
type="text/css"
|
type="text/css"
|
||||||
href={`/css/themes/${user.local_user_view.local_user.theme}.css`}
|
href={`/css/themes/${
|
||||||
|
user.unwrap().local_user_view.local_user.theme
|
||||||
|
}.css`}
|
||||||
/>
|
/>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (
|
||||||
this.props.defaultTheme != null &&
|
this.props.defaultTheme.isSome() &&
|
||||||
this.props.defaultTheme != "browser"
|
this.props.defaultTheme.unwrap() != "browser"
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
type="text/css"
|
type="text/css"
|
||||||
href={`/css/themes/${this.props.defaultTheme}.css`}
|
href={`/css/themes/${this.props.defaultTheme.unwrap()}.css`}
|
||||||
/>
|
/>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
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";
|
||||||
|
@ -6,25 +7,27 @@ import {
|
||||||
CreateComment,
|
CreateComment,
|
||||||
EditComment,
|
EditComment,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { CommentNode as CommentNodeI } from "../../interfaces";
|
import { CommentNode as CommentNodeI } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { Icon } from "../common/icon";
|
import { Icon } from "../common/icon";
|
||||||
import { MarkdownTextArea } from "../common/markdown-textarea";
|
import { MarkdownTextArea } from "../common/markdown-textarea";
|
||||||
|
|
||||||
interface CommentFormProps {
|
interface CommentFormProps {
|
||||||
postId?: number;
|
/**
|
||||||
node?: CommentNodeI; // Can either be the parent, or the editable comment
|
* Can either be the parent, or the editable comment. The right side is a postId.
|
||||||
|
*/
|
||||||
|
node: Either<CommentNodeI, number>;
|
||||||
edit?: boolean;
|
edit?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
|
@ -34,19 +37,19 @@ interface CommentFormProps {
|
||||||
interface CommentFormState {
|
interface CommentFormState {
|
||||||
buttonTitle: string;
|
buttonTitle: string;
|
||||||
finished: boolean;
|
finished: boolean;
|
||||||
formId: string;
|
formId: Option<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: CommentFormState = {
|
private emptyState: CommentFormState = {
|
||||||
buttonTitle: !this.props.node
|
buttonTitle: this.props.node.isRight()
|
||||||
? capitalizeFirstLetter(i18n.t("post"))
|
? capitalizeFirstLetter(i18n.t("post"))
|
||||||
: this.props.edit
|
: this.props.edit
|
||||||
? capitalizeFirstLetter(i18n.t("save"))
|
? capitalizeFirstLetter(i18n.t("save"))
|
||||||
: capitalizeFirstLetter(i18n.t("reply")),
|
: capitalizeFirstLetter(i18n.t("reply")),
|
||||||
finished: false,
|
finished: false,
|
||||||
formId: "empty_form",
|
formId: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -66,23 +69,25 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
let initialContent = this.props.node.match({
|
||||||
|
left: node =>
|
||||||
|
this.props.edit ? Some(node.comment_view.comment.content) : None,
|
||||||
|
right: () => None,
|
||||||
|
});
|
||||||
return (
|
return (
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
{UserService.Instance.myUserInfo ? (
|
{UserService.Instance.myUserInfo.isSome() ? (
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={
|
initialContent={initialContent}
|
||||||
this.props.edit
|
buttonTitle={Some(this.state.buttonTitle)}
|
||||||
? this.props.node.comment_view.comment.content
|
maxLength={None}
|
||||||
: null
|
|
||||||
}
|
|
||||||
buttonTitle={this.state.buttonTitle}
|
|
||||||
finished={this.state.finished}
|
finished={this.state.finished}
|
||||||
replyType={!!this.props.node}
|
replyType={this.props.node.isLeft()}
|
||||||
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={i18n.t("comment_here")}
|
placeholder={Some(i18n.t("comment_here"))}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div class="alert alert-warning" role="alert">
|
<div class="alert alert-warning" role="alert">
|
||||||
|
@ -101,28 +106,40 @@ export class CommentForm extends Component<CommentFormProps, CommentFormState> {
|
||||||
|
|
||||||
handleCommentSubmit(msg: { val: string; formId: string }) {
|
handleCommentSubmit(msg: { val: string; formId: string }) {
|
||||||
let content = msg.val;
|
let content = msg.val;
|
||||||
this.state.formId = msg.formId;
|
this.state.formId = Some(msg.formId);
|
||||||
|
|
||||||
let node = this.props.node;
|
this.props.node.match({
|
||||||
|
left: node => {
|
||||||
if (this.props.edit) {
|
if (this.props.edit) {
|
||||||
let form: EditComment = {
|
let form = new EditComment({
|
||||||
content,
|
content,
|
||||||
form_id: this.state.formId,
|
form_id: this.state.formId,
|
||||||
comment_id: node.comment_view.comment.id,
|
comment_id: node.comment_view.comment.id,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.editComment(form));
|
WebSocketService.Instance.send(wsClient.editComment(form));
|
||||||
} else {
|
} else {
|
||||||
let form: CreateComment = {
|
let form = new CreateComment({
|
||||||
content,
|
content,
|
||||||
form_id: this.state.formId,
|
form_id: this.state.formId,
|
||||||
post_id: node ? node.comment_view.post.id : this.props.postId,
|
post_id: node.comment_view.post.id,
|
||||||
parent_id: node ? node.comment_view.comment.id : null,
|
parent_id: Some(node.comment_view.comment.id),
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.createComment(form));
|
WebSocketService.Instance.send(wsClient.createComment(form));
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
right: postId => {
|
||||||
|
let form = new CreateComment({
|
||||||
|
content,
|
||||||
|
form_id: this.state.formId,
|
||||||
|
post_id: postId,
|
||||||
|
parent_id: None,
|
||||||
|
auth: auth().unwrap(),
|
||||||
|
});
|
||||||
|
WebSocketService.Instance.send(wsClient.createComment(form));
|
||||||
|
},
|
||||||
|
});
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,15 +152,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) {
|
if (UserService.Instance.myUserInfo.isSome()) {
|
||||||
if (
|
if (
|
||||||
op == UserOperation.CreateComment ||
|
op == UserOperation.CreateComment ||
|
||||||
op == UserOperation.EditComment
|
op == UserOperation.EditComment
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
|
|
||||||
// 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 == data.form_id) {
|
if (this.state.formId.unwrapOr("") == data.form_id.unwrapOr("")) {
|
||||||
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,3 +1,4 @@
|
||||||
|
import { Option } from "@sniptt/monads";
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import { CommunityModeratorView, PersonViewSafe } from "lemmy-js-client";
|
import { CommunityModeratorView, PersonViewSafe } from "lemmy-js-client";
|
||||||
import { CommentNode as CommentNodeI } from "../../interfaces";
|
import { CommentNode as CommentNodeI } from "../../interfaces";
|
||||||
|
@ -5,9 +6,9 @@ import { CommentNode } from "./comment-node";
|
||||||
|
|
||||||
interface CommentNodesProps {
|
interface CommentNodesProps {
|
||||||
nodes: CommentNodeI[];
|
nodes: CommentNodeI[];
|
||||||
moderators?: CommunityModeratorView[];
|
moderators: Option<CommunityModeratorView[]>;
|
||||||
admins?: PersonViewSafe[];
|
admins: Option<PersonViewSafe[]>;
|
||||||
postCreatorId?: number;
|
maxCommentsShown: Option<number>;
|
||||||
noBorder?: boolean;
|
noBorder?: boolean;
|
||||||
noIndent?: boolean;
|
noIndent?: boolean;
|
||||||
viewOnly?: boolean;
|
viewOnly?: boolean;
|
||||||
|
@ -15,8 +16,7 @@ interface CommentNodesProps {
|
||||||
markable?: boolean;
|
markable?: boolean;
|
||||||
showContext?: boolean;
|
showContext?: boolean;
|
||||||
showCommunity?: boolean;
|
showCommunity?: boolean;
|
||||||
maxCommentsShown?: number;
|
enableDownvotes?: boolean;
|
||||||
enableDownvotes: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CommentNodes extends Component<CommentNodesProps, any> {
|
export class CommentNodes extends Component<CommentNodesProps, any> {
|
||||||
|
@ -25,9 +25,9 @@ export class CommentNodes extends Component<CommentNodesProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let maxComments = this.props.maxCommentsShown
|
let maxComments = this.props.maxCommentsShown.unwrapOr(
|
||||||
? this.props.maxCommentsShown
|
this.props.nodes.length
|
||||||
: this.props.nodes.length;
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="comments">
|
<div className="comments">
|
||||||
|
@ -41,7 +41,6 @@ export class CommentNodes extends Component<CommentNodesProps, any> {
|
||||||
locked={this.props.locked}
|
locked={this.props.locked}
|
||||||
moderators={this.props.moderators}
|
moderators={this.props.moderators}
|
||||||
admins={this.props.admins}
|
admins={this.props.admins}
|
||||||
postCreatorId={this.props.postCreatorId}
|
|
||||||
markable={this.props.markable}
|
markable={this.props.markable}
|
||||||
showContext={this.props.showContext}
|
showContext={this.props.showContext}
|
||||||
showCommunity={this.props.showCommunity}
|
showCommunity={this.props.showCommunity}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
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 {
|
||||||
|
@ -8,7 +9,7 @@ import {
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { CommentNode as CommentNodeI } from "../../interfaces";
|
import { CommentNode as CommentNodeI } from "../../interfaces";
|
||||||
import { WebSocketService } from "../../services";
|
import { WebSocketService } from "../../services";
|
||||||
import { authField, wsClient } from "../../utils";
|
import { auth, 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";
|
||||||
|
@ -42,6 +43,7 @@ export class CommentReport extends Component<CommentReportProps, any> {
|
||||||
subscribed: false,
|
subscribed: false,
|
||||||
saved: false,
|
saved: false,
|
||||||
creator_blocked: false,
|
creator_blocked: false,
|
||||||
|
recipient: None,
|
||||||
my_vote: r.my_vote,
|
my_vote: r.my_vote,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -53,8 +55,8 @@ export class CommentReport extends Component<CommentReportProps, any> {
|
||||||
<div>
|
<div>
|
||||||
<CommentNode
|
<CommentNode
|
||||||
node={node}
|
node={node}
|
||||||
moderators={[]}
|
moderators={None}
|
||||||
admins={[]}
|
admins={None}
|
||||||
enableDownvotes={true}
|
enableDownvotes={true}
|
||||||
viewOnly={true}
|
viewOnly={true}
|
||||||
showCommunity={true}
|
showCommunity={true}
|
||||||
|
@ -65,21 +67,24 @@ 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 && (
|
{r.resolver.match({
|
||||||
<div>
|
some: resolver => (
|
||||||
{r.comment_report.resolved ? (
|
<div>
|
||||||
<T i18nKey="resolved_by">
|
{r.comment_report.resolved ? (
|
||||||
#
|
<T i18nKey="resolved_by">
|
||||||
<PersonListing person={r.resolver} />
|
#
|
||||||
</T>
|
<PersonListing person={resolver} />
|
||||||
) : (
|
</T>
|
||||||
<T i18nKey="unresolved_by">
|
) : (
|
||||||
#
|
<T i18nKey="unresolved_by">
|
||||||
<PersonListing person={r.resolver} />
|
#
|
||||||
</T>
|
<PersonListing person={resolver} />
|
||||||
)}
|
</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)}
|
||||||
|
@ -98,11 +103,11 @@ export class CommentReport extends Component<CommentReportProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleResolveReport(i: CommentReport) {
|
handleResolveReport(i: CommentReport) {
|
||||||
let form: ResolveCommentReport = {
|
let form = new ResolveCommentReport({
|
||||||
report_id: i.props.report.comment_report.id,
|
report_id: i.props.report.comment_report.id,
|
||||||
resolved: !i.props.report.comment_report.resolved,
|
resolved: !i.props.report.comment_report.resolved,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.resolveCommentReport(form));
|
WebSocketService.Instance.send(wsClient.resolveCommentReport(form));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
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?: string;
|
banner: Option<string>;
|
||||||
icon?: string;
|
icon: Option<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BannerIconHeader extends Component<BannerIconHeaderProps, any> {
|
export class BannerIconHeader extends Component<BannerIconHeaderProps, any> {
|
||||||
|
@ -14,17 +15,21 @@ export class BannerIconHeader extends Component<BannerIconHeaderProps, any> {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div class="position-relative mb-2">
|
<div class="position-relative mb-2">
|
||||||
{this.props.banner && (
|
{this.props.banner.match({
|
||||||
<PictrsImage src={this.props.banner} banner alt="" />
|
some: banner => <PictrsImage src={banner} banner alt="" />,
|
||||||
)}
|
none: <></>,
|
||||||
{this.props.icon && (
|
})}
|
||||||
<PictrsImage
|
{this.props.icon.match({
|
||||||
src={this.props.icon}
|
some: icon => (
|
||||||
iconOverlay
|
<PictrsImage
|
||||||
pushup={!!this.props.banner}
|
src={icon}
|
||||||
alt=""
|
iconOverlay
|
||||||
/>
|
pushup={this.props.banner.isSome()}
|
||||||
)}
|
alt=""
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Option } from "@sniptt/monads";
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import { Helmet } from "inferno-helmet";
|
import { Helmet } from "inferno-helmet";
|
||||||
import { httpExternalPath } from "../../env";
|
import { httpExternalPath } from "../../env";
|
||||||
|
@ -6,8 +7,8 @@ import { md } from "../../utils";
|
||||||
interface HtmlTagsProps {
|
interface HtmlTagsProps {
|
||||||
title: string;
|
title: string;
|
||||||
path: string;
|
path: string;
|
||||||
description?: string;
|
description: Option<string>;
|
||||||
image?: string;
|
image: Option<string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Taken from https://metatags.io/
|
/// Taken from https://metatags.io/
|
||||||
|
@ -31,14 +32,17 @@ 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 &&
|
{this.props.description.isSome() &&
|
||||||
["description", "og:description", "twitter:description"].map(n => (
|
["description", "og:description", "twitter:description"].map(n => (
|
||||||
<meta name={n} content={md.renderInline(this.props.description)} />
|
<meta
|
||||||
|
name={n}
|
||||||
|
content={md.renderInline(this.props.description.unwrap())}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{this.props.image &&
|
{this.props.image.isSome() &&
|
||||||
["og:image", "twitter:image"].map(p => (
|
["og:image", "twitter:image"].map(p => (
|
||||||
<meta property={p} content={this.props.image} />
|
<meta property={p} content={this.props.image.unwrap()} />
|
||||||
))}
|
))}
|
||||||
</Helmet>
|
</Helmet>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
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";
|
||||||
|
@ -7,7 +8,7 @@ import { Icon } from "./icon";
|
||||||
|
|
||||||
interface ImageUploadFormProps {
|
interface ImageUploadFormProps {
|
||||||
uploadTitle: string;
|
uploadTitle: string;
|
||||||
imageSrc: string;
|
imageSrc: Option<string>;
|
||||||
onUpload(url: string): any;
|
onUpload(url: string): any;
|
||||||
onRemove(): any;
|
onRemove(): any;
|
||||||
rounded?: boolean;
|
rounded?: boolean;
|
||||||
|
@ -38,26 +39,29 @@ export class ImageUploadForm extends Component<
|
||||||
htmlFor={this.id}
|
htmlFor={this.id}
|
||||||
class="pointer text-muted small font-weight-bold"
|
class="pointer text-muted small font-weight-bold"
|
||||||
>
|
>
|
||||||
{!this.props.imageSrc ? (
|
{this.props.imageSrc.match({
|
||||||
<span class="btn btn-secondary">{this.props.uploadTitle}</span>
|
some: imageSrc => (
|
||||||
) : (
|
<span class="d-inline-block position-relative">
|
||||||
<span class="d-inline-block position-relative">
|
<img
|
||||||
<img
|
src={imageSrc}
|
||||||
src={this.props.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>
|
),
|
||||||
)}
|
none: (
|
||||||
|
<span class="btn btn-secondary">{this.props.uploadTitle}</span>
|
||||||
|
),
|
||||||
|
})}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id={this.id}
|
id={this.id}
|
||||||
|
@ -65,7 +69,7 @@ export class ImageUploadForm extends Component<
|
||||||
accept="image/*,video/*"
|
accept="image/*,video/*"
|
||||||
name={this.id}
|
name={this.id}
|
||||||
class="d-none"
|
class="d-none"
|
||||||
disabled={!UserService.Instance.myUserInfo}
|
disabled={UserService.Instance.myUserInfo.isNone()}
|
||||||
onChange={linkEvent(this, this.handleImageUpload)}
|
onChange={linkEvent(this, this.handleImageUpload)}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -46,11 +46,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 == undefined
|
|
||||||
? "disabled"
|
|
||||||
: "pointer"
|
|
||||||
}
|
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
@ -59,7 +55,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 == undefined}
|
disabled={UserService.Instance.myUserInfo.isNone()}
|
||||||
/>
|
/>
|
||||||
{i18n.t("subscribed")}
|
{i18n.t("subscribed")}
|
||||||
</label>
|
</label>
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
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 { toUndefined } 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";
|
||||||
|
@ -18,22 +20,22 @@ import {
|
||||||
import { Icon, Spinner } from "./icon";
|
import { Icon, Spinner } from "./icon";
|
||||||
|
|
||||||
interface MarkdownTextAreaProps {
|
interface MarkdownTextAreaProps {
|
||||||
initialContent?: string;
|
initialContent: Option<string>;
|
||||||
finished?: boolean;
|
placeholder: Option<string>;
|
||||||
buttonTitle?: string;
|
buttonTitle: Option<string>;
|
||||||
|
maxLength: Option<number>;
|
||||||
replyType?: boolean;
|
replyType?: boolean;
|
||||||
focus?: boolean;
|
focus?: boolean;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
maxLength?: number;
|
finished?: boolean;
|
||||||
onSubmit?(msg: { val: string; formId: string }): any;
|
hideNavigationWarnings?: boolean;
|
||||||
onContentChange?(val: string): any;
|
onContentChange?(val: string): any;
|
||||||
onReplyCancel?(): any;
|
onReplyCancel?(): any;
|
||||||
hideNavigationWarnings?: boolean;
|
onSubmit?(msg: { val: string; formId: string }): any;
|
||||||
placeholder?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MarkdownTextAreaState {
|
interface MarkdownTextAreaState {
|
||||||
content: string;
|
content: Option<string>;
|
||||||
previewMode: boolean;
|
previewMode: boolean;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
imageLoading: boolean;
|
imageLoading: boolean;
|
||||||
|
@ -68,7 +70,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.state.content = textarea.value;
|
this.state.content = Some(textarea.value);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
autosize.update(textarea);
|
autosize.update(textarea);
|
||||||
});
|
});
|
||||||
|
@ -85,7 +87,7 @@ export class MarkdownTextArea extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if (!this.props.hideNavigationWarnings && this.state.content) {
|
if (!this.props.hideNavigationWarnings && this.state.content.isSome()) {
|
||||||
window.onbeforeunload = () => true;
|
window.onbeforeunload = () => true;
|
||||||
} else {
|
} else {
|
||||||
window.onbeforeunload = undefined;
|
window.onbeforeunload = undefined;
|
||||||
|
@ -96,7 +98,7 @@ export class MarkdownTextArea extends Component<
|
||||||
if (nextProps.finished) {
|
if (nextProps.finished) {
|
||||||
this.state.previewMode = false;
|
this.state.previewMode = false;
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
this.state.content = "";
|
this.state.content = None;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
if (this.props.replyType) {
|
if (this.props.replyType) {
|
||||||
this.props.onReplyCancel();
|
this.props.onReplyCancel();
|
||||||
|
@ -118,7 +120,9 @@ export class MarkdownTextArea extends Component<
|
||||||
return (
|
return (
|
||||||
<form id={this.formId} onSubmit={linkEvent(this, this.handleSubmit)}>
|
<form id={this.formId} onSubmit={linkEvent(this, this.handleSubmit)}>
|
||||||
<Prompt
|
<Prompt
|
||||||
when={!this.props.hideNavigationWarnings && this.state.content}
|
when={
|
||||||
|
!this.props.hideNavigationWarnings && this.state.content.isSome()
|
||||||
|
}
|
||||||
message={i18n.t("block_leaving")}
|
message={i18n.t("block_leaving")}
|
||||||
/>
|
/>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
|
@ -126,21 +130,25 @@ 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={this.state.content}
|
value={toUndefined(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 || 10000}
|
maxLength={this.props.maxLength.unwrapOr(10000)}
|
||||||
placeholder={this.props.placeholder}
|
placeholder={toUndefined(this.props.placeholder)}
|
||||||
/>
|
/>
|
||||||
{this.state.previewMode && (
|
{this.state.previewMode &&
|
||||||
<div
|
this.state.content.match({
|
||||||
className="card border-secondary card-body md-div"
|
some: content => (
|
||||||
dangerouslySetInnerHTML={mdToHtml(this.state.content)}
|
<div
|
||||||
/>
|
className="card border-secondary card-body md-div"
|
||||||
)}
|
dangerouslySetInnerHTML={mdToHtml(content)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
<label class="sr-only" htmlFor={this.id}>
|
<label class="sr-only" htmlFor={this.id}>
|
||||||
{i18n.t("body")}
|
{i18n.t("body")}
|
||||||
|
@ -148,19 +156,22 @@ export class MarkdownTextArea extends Component<
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12 d-flex flex-wrap">
|
<div class="col-sm-12 d-flex flex-wrap">
|
||||||
{this.props.buttonTitle && (
|
{this.props.buttonTitle.match({
|
||||||
<button
|
some: buttonTitle => (
|
||||||
type="submit"
|
<button
|
||||||
class="btn btn-sm btn-secondary mr-2"
|
type="submit"
|
||||||
disabled={this.props.disabled || this.state.loading}
|
class="btn btn-sm btn-secondary mr-2"
|
||||||
>
|
disabled={this.props.disabled || this.state.loading}
|
||||||
{this.state.loading ? (
|
>
|
||||||
<Spinner />
|
{this.state.loading ? (
|
||||||
) : (
|
<Spinner />
|
||||||
<span>{this.props.buttonTitle}</span>
|
) : (
|
||||||
)}
|
<span>{buttonTitle}</span>
|
||||||
</button>
|
)}
|
||||||
)}
|
</button>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
{this.props.replyType && (
|
{this.props.replyType && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
@ -170,7 +181,7 @@ export class MarkdownTextArea extends Component<
|
||||||
{i18n.t("cancel")}
|
{i18n.t("cancel")}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{this.state.content && (
|
{this.state.content.isSome() && (
|
||||||
<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"
|
||||||
|
@ -210,7 +221,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 && "pointer"
|
UserService.Instance.myUserInfo.isSome() && "pointer"
|
||||||
}`}
|
}`}
|
||||||
data-tippy-content={i18n.t("upload_image")}
|
data-tippy-content={i18n.t("upload_image")}
|
||||||
>
|
>
|
||||||
|
@ -226,7 +237,7 @@ export class MarkdownTextArea extends Component<
|
||||||
accept="image/*,video/*"
|
accept="image/*,video/*"
|
||||||
name="file"
|
name="file"
|
||||||
class="d-none"
|
class="d-none"
|
||||||
disabled={!UserService.Instance.myUserInfo}
|
disabled={UserService.Instance.myUserInfo.isNone()}
|
||||||
onChange={linkEvent(this, this.handleImageUpload)}
|
onChange={linkEvent(this, this.handleImageUpload)}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
@ -344,9 +355,12 @@ 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.state.content = Some(
|
||||||
content = content ? `${content}\n${imageMarkdown}` : imageMarkdown;
|
i.state.content.match({
|
||||||
i.state.content = content;
|
some: content => `${content}\n${imageMarkdown}`,
|
||||||
|
none: imageMarkdown,
|
||||||
|
})
|
||||||
|
);
|
||||||
i.state.imageLoading = false;
|
i.state.imageLoading = false;
|
||||||
i.contentChange();
|
i.contentChange();
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
@ -373,12 +387,12 @@ export class MarkdownTextArea extends Component<
|
||||||
|
|
||||||
contentChange() {
|
contentChange() {
|
||||||
if (this.props.onContentChange) {
|
if (this.props.onContentChange) {
|
||||||
this.props.onContentChange(this.state.content);
|
this.props.onContentChange(toUndefined(this.state.content));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleContentChange(i: MarkdownTextArea, event: any) {
|
handleContentChange(i: MarkdownTextArea, event: any) {
|
||||||
i.state.content = event.target.value;
|
i.state.content = Some(event.target.value);
|
||||||
i.contentChange();
|
i.contentChange();
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
@ -393,7 +407,7 @@ export class MarkdownTextArea extends Component<
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.loading = true;
|
i.state.loading = true;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
let msg = { val: i.state.content, formId: i.formId };
|
let msg = { val: toUndefined(i.state.content), formId: i.formId };
|
||||||
i.props.onSubmit(msg);
|
i.props.onSubmit(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,23 +417,28 @@ export class MarkdownTextArea extends Component<
|
||||||
|
|
||||||
handleInsertLink(i: MarkdownTextArea, event: any) {
|
handleInsertLink(i: MarkdownTextArea, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!i.state.content) {
|
|
||||||
i.state.content = "";
|
|
||||||
}
|
|
||||||
let textarea: any = document.getElementById(i.id);
|
let textarea: any = document.getElementById(i.id);
|
||||||
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()) {
|
||||||
|
i.state.content = Some("");
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = i.state.content.unwrap();
|
||||||
|
|
||||||
if (start !== end) {
|
if (start !== end) {
|
||||||
let selectedText = i.state.content.substring(start, end);
|
let selectedText = content.substring(start, end);
|
||||||
i.state.content = `${i.state.content.substring(
|
i.state.content = Some(
|
||||||
0,
|
`${content.substring(0, start)}[${selectedText}]()${content.substring(
|
||||||
start
|
end
|
||||||
)}[${selectedText}]()${i.state.content.substring(end)}`;
|
)}`
|
||||||
|
);
|
||||||
textarea.focus();
|
textarea.focus();
|
||||||
setTimeout(() => (textarea.selectionEnd = end + 3), 10);
|
setTimeout(() => (textarea.selectionEnd = end + 3), 10);
|
||||||
} else {
|
} else {
|
||||||
i.state.content += "[]()";
|
i.state.content = Some(`${content} []()`);
|
||||||
textarea.focus();
|
textarea.focus();
|
||||||
setTimeout(() => (textarea.selectionEnd -= 1), 10);
|
setTimeout(() => (textarea.selectionEnd -= 1), 10);
|
||||||
}
|
}
|
||||||
|
@ -432,7 +451,7 @@ export class MarkdownTextArea extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
simpleBeginningofLine(chars: string) {
|
simpleBeginningofLine(chars: string) {
|
||||||
this.simpleSurroundBeforeAfter(`${chars} `, "", "");
|
this.simpleSurroundBeforeAfter(`${chars}`, "", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
simpleSurroundBeforeAfter(
|
simpleSurroundBeforeAfter(
|
||||||
|
@ -440,23 +459,27 @@ export class MarkdownTextArea extends Component<
|
||||||
afterChars: string,
|
afterChars: string,
|
||||||
emptyChars = "___"
|
emptyChars = "___"
|
||||||
) {
|
) {
|
||||||
if (!this.state.content) {
|
if (this.state.content.isNone()) {
|
||||||
this.state.content = "";
|
this.state.content = Some("");
|
||||||
}
|
}
|
||||||
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 = this.state.content.substring(start, end);
|
let selectedText = content.substring(start, end);
|
||||||
this.state.content = `${this.state.content.substring(
|
this.state.content = Some(
|
||||||
0,
|
`${content.substring(
|
||||||
start
|
0,
|
||||||
)}${beforeChars}${selectedText}${afterChars}${this.state.content.substring(
|
start
|
||||||
end
|
)}${beforeChars}${selectedText}${afterChars}${content.substring(end)}`
|
||||||
)}`;
|
);
|
||||||
} else {
|
} else {
|
||||||
this.state.content += `${beforeChars}${emptyChars}${afterChars}`;
|
this.state.content = Some(
|
||||||
|
`${content}${beforeChars}${emptyChars}${afterChars}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.contentChange();
|
this.contentChange();
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
@ -530,10 +553,10 @@ export class MarkdownTextArea extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
simpleInsert(chars: string) {
|
simpleInsert(chars: string) {
|
||||||
if (!this.state.content) {
|
if (this.state.content.isNone()) {
|
||||||
this.state.content = `${chars} `;
|
this.state.content = Some(`${chars} `);
|
||||||
} else {
|
} else {
|
||||||
this.state.content += `\n${chars} `;
|
this.state.content = Some(`${this.state.content.unwrap()}\n${chars} `);
|
||||||
}
|
}
|
||||||
|
|
||||||
let textarea: any = document.getElementById(this.id);
|
let textarea: any = document.getElementById(this.id);
|
||||||
|
@ -561,12 +584,12 @@ export class MarkdownTextArea extends Component<
|
||||||
.split("\n")
|
.split("\n")
|
||||||
.map(t => `> ${t}`)
|
.map(t => `> ${t}`)
|
||||||
.join("\n") + "\n\n";
|
.join("\n") + "\n\n";
|
||||||
if (this.state.content == null) {
|
if (this.state.content.isNone()) {
|
||||||
this.state.content = "";
|
this.state.content = Some("");
|
||||||
} else {
|
} else {
|
||||||
this.state.content += "\n";
|
this.state.content = Some(`${this.state.content.unwrap()}\n`);
|
||||||
}
|
}
|
||||||
this.state.content += quotedText;
|
this.state.content = Some(`${this.state.content.unwrap()}${quotedText}`);
|
||||||
this.contentChange();
|
this.contentChange();
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
// Not sure why this needs a delay
|
// Not sure why this needs a delay
|
||||||
|
@ -578,6 +601,8 @@ 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 ? this.state.content.substring(start, end) : "";
|
return start !== end
|
||||||
|
? this.state.content.unwrap().substring(start, end)
|
||||||
|
: "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
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";
|
||||||
|
@ -5,11 +6,8 @@ import { capitalizeFirstLetter, getLanguages } from "../../utils";
|
||||||
import { Icon } from "./icon";
|
import { Icon } from "./icon";
|
||||||
|
|
||||||
interface MomentTimeProps {
|
interface MomentTimeProps {
|
||||||
data: {
|
published: string;
|
||||||
published?: string;
|
updated: Option<string>;
|
||||||
when_?: string;
|
|
||||||
updated?: string;
|
|
||||||
};
|
|
||||||
showAgo?: boolean;
|
showAgo?: boolean;
|
||||||
ignoreUpdated?: boolean;
|
ignoreUpdated?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -24,40 +22,32 @@ export class MomentTime extends Component<MomentTimeProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
createdAndModifiedTimes() {
|
createdAndModifiedTimes() {
|
||||||
let created = this.props.data.published || this.props.data.when_;
|
return `${capitalizeFirstLetter(i18n.t("created"))}: ${this.format(
|
||||||
return `
|
this.props.published
|
||||||
<div>
|
)}\n\n\n${
|
||||||
<div>
|
this.props.updated.isSome() && capitalizeFirstLetter(i18n.t("modified"))
|
||||||
${capitalizeFirstLetter(i18n.t("created"))}: ${this.format(created)}
|
} ${this.format(this.props.updated.unwrap())}`;
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
${capitalizeFirstLetter(i18n.t("modified"))} ${this.format(
|
|
||||||
this.props.data.updated
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (!this.props.ignoreUpdated && this.props.data.updated) {
|
if (!this.props.ignoreUpdated && this.props.updated.isSome()) {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
data-tippy-content={this.createdAndModifiedTimes()}
|
data-tippy-content={this.createdAndModifiedTimes()}
|
||||||
data-tippy-allowHtml={true}
|
|
||||||
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.data.updated).fromNow(!this.props.showAgo)}
|
{moment.utc(this.props.updated.unwrap()).fromNow(!this.props.showAgo)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let created = this.props.data.published || this.props.data.when_;
|
let published = this.props.published;
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className="pointer unselectable"
|
className="pointer unselectable"
|
||||||
data-tippy-content={this.format(created)}
|
data-tippy-content={this.format(published)}
|
||||||
>
|
>
|
||||||
{moment.utc(created).fromNow(!this.props.showAgo)}
|
{moment.utc(published).fromNow(!this.props.showAgo)}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
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 {
|
||||||
|
@ -6,7 +7,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 { authField, mdToHtml, wsClient } from "../../utils";
|
import { auth, mdToHtml, 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";
|
||||||
|
@ -16,7 +17,7 @@ interface RegistrationApplicationProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RegistrationApplicationState {
|
interface RegistrationApplicationState {
|
||||||
denyReason?: string;
|
denyReason: Option<string>;
|
||||||
denyExpanded: boolean;
|
denyExpanded: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,35 +48,44 @@ 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")}: <MomentTime showAgo data={ra} />
|
{i18n.t("created")}:{" "}
|
||||||
|
<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 && (
|
{a.admin.match({
|
||||||
<div>
|
some: admin => (
|
||||||
{accepted ? (
|
<div>
|
||||||
<T i18nKey="approved_by">
|
{accepted ? (
|
||||||
#
|
<T i18nKey="approved_by">
|
||||||
<PersonListing person={a.admin} />
|
|
||||||
</T>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
<T i18nKey="denied_by">
|
|
||||||
#
|
#
|
||||||
<PersonListing person={a.admin} />
|
<PersonListing person={admin} />
|
||||||
</T>
|
</T>
|
||||||
|
) : (
|
||||||
<div>
|
<div>
|
||||||
{i18n.t("deny_reason")}:{" "}
|
<T i18nKey="denied_by">
|
||||||
<div
|
#
|
||||||
className="md-div d-inline-flex"
|
<PersonListing person={admin} />
|
||||||
dangerouslySetInnerHTML={mdToHtml(ra.deny_reason || "")}
|
</T>
|
||||||
/>
|
{ra.deny_reason.match({
|
||||||
|
some: deny_reason => (
|
||||||
|
<div>
|
||||||
|
{i18n.t("deny_reason")}:{" "}
|
||||||
|
<div
|
||||||
|
className="md-div d-inline-flex"
|
||||||
|
dangerouslySetInnerHTML={mdToHtml(deny_reason)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
),
|
||||||
)}
|
none: <></>,
|
||||||
|
})}
|
||||||
|
|
||||||
{this.state.denyExpanded && (
|
{this.state.denyExpanded && (
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
|
@ -86,6 +96,9 @@ export class RegistrationApplication extends Component<
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.denyReason}
|
initialContent={this.state.denyReason}
|
||||||
onContentChange={this.handleDenyReasonChange}
|
onContentChange={this.handleDenyReasonChange}
|
||||||
|
placeholder={None}
|
||||||
|
buttonTitle={None}
|
||||||
|
maxLength={None}
|
||||||
hideNavigationWarnings
|
hideNavigationWarnings
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -115,12 +128,12 @@ export class RegistrationApplication extends Component<
|
||||||
|
|
||||||
handleApprove(i: RegistrationApplication) {
|
handleApprove(i: RegistrationApplication) {
|
||||||
i.setState({ denyExpanded: false });
|
i.setState({ denyExpanded: false });
|
||||||
let form: ApproveRegistrationApplication = {
|
let form = new ApproveRegistrationApplication({
|
||||||
id: i.props.application.registration_application.id,
|
id: i.props.application.registration_application.id,
|
||||||
deny_reason: "",
|
deny_reason: None,
|
||||||
approve: true,
|
approve: true,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.approveRegistrationApplication(form)
|
wsClient.approveRegistrationApplication(form)
|
||||||
);
|
);
|
||||||
|
@ -129,12 +142,12 @@ export class RegistrationApplication extends Component<
|
||||||
handleDeny(i: RegistrationApplication) {
|
handleDeny(i: RegistrationApplication) {
|
||||||
if (i.state.denyExpanded) {
|
if (i.state.denyExpanded) {
|
||||||
i.setState({ denyExpanded: false });
|
i.setState({ denyExpanded: false });
|
||||||
let form: ApproveRegistrationApplication = {
|
let form = new ApproveRegistrationApplication({
|
||||||
id: i.props.application.registration_application.id,
|
id: i.props.application.registration_application.id,
|
||||||
approve: false,
|
approve: false,
|
||||||
deny_reason: i.state.denyReason,
|
deny_reason: i.state.denyReason,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.approveRegistrationApplication(form)
|
wsClient.approveRegistrationApplication(form)
|
||||||
);
|
);
|
||||||
|
@ -144,7 +157,7 @@ export class RegistrationApplication extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDenyReasonChange(val: string) {
|
handleDenyReasonChange(val: string) {
|
||||||
this.state.denyReason = val;
|
this.state.denyReason = Some(val);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,32 @@
|
||||||
|
import { None, Option, Some } from "@sniptt/monads";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommunityResponse,
|
CommunityResponse,
|
||||||
CommunityView,
|
|
||||||
FollowCommunity,
|
FollowCommunity,
|
||||||
|
GetSiteResponse,
|
||||||
ListCommunities,
|
ListCommunities,
|
||||||
ListCommunitiesResponse,
|
ListCommunitiesResponse,
|
||||||
ListingType,
|
ListingType,
|
||||||
SiteView,
|
|
||||||
SortType,
|
SortType,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { InitialFetchRequest } from "shared/interfaces";
|
import { InitialFetchRequest } from "shared/interfaces";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { WebSocketService } from "../../services";
|
import { WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
getListingTypeFromPropsNoDefault,
|
getListingTypeFromPropsNoDefault,
|
||||||
getPageFromProps,
|
getPageFromProps,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
numToSI,
|
numToSI,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
setOptionalAuth,
|
|
||||||
showLocal,
|
showLocal,
|
||||||
toast,
|
toast,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
|
@ -38,10 +37,10 @@ import { CommunityLink } from "./community-link";
|
||||||
const communityLimit = 100;
|
const communityLimit = 100;
|
||||||
|
|
||||||
interface CommunitiesState {
|
interface CommunitiesState {
|
||||||
communities: CommunityView[];
|
listCommunitiesResponse: Option<ListCommunitiesResponse>;
|
||||||
page: number;
|
page: number;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
site_view: SiteView;
|
siteRes: GetSiteResponse;
|
||||||
searchText: string;
|
searchText: string;
|
||||||
listingType: ListingType;
|
listingType: ListingType;
|
||||||
}
|
}
|
||||||
|
@ -53,13 +52,13 @@ 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);
|
private isoData = setIsoData(this.context, ListCommunitiesResponse);
|
||||||
private emptyState: CommunitiesState = {
|
private emptyState: CommunitiesState = {
|
||||||
communities: [],
|
listCommunitiesResponse: None,
|
||||||
loading: true,
|
loading: true,
|
||||||
page: getPageFromProps(this.props),
|
page: getPageFromProps(this.props),
|
||||||
listingType: getListingTypeFromPropsNoDefault(this.props),
|
listingType: getListingTypeFromPropsNoDefault(this.props),
|
||||||
site_view: this.isoData.site_res.site_view,
|
siteRes: this.isoData.site_res,
|
||||||
searchText: "",
|
searchText: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -74,7 +73,8 @@ 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) {
|
||||||
this.state.communities = this.isoData.routeData[0].communities;
|
let listRes = Some(this.isoData.routeData[0] as ListCommunitiesResponse);
|
||||||
|
this.state.listCommunitiesResponse = listRes;
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
} else {
|
} else {
|
||||||
this.refetch();
|
this.refetch();
|
||||||
|
@ -105,7 +105,10 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `${i18n.t("communities")} - ${this.state.site_view.site.name}`;
|
return this.state.siteRes.site_view.match({
|
||||||
|
some: siteView => `${i18n.t("communities")} - ${siteView.site.name}`,
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -114,6 +117,8 @@ 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>
|
||||||
|
@ -157,48 +162,51 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{this.state.communities.map(cv => (
|
{this.state.listCommunitiesResponse
|
||||||
<tr>
|
.map(l => l.communities)
|
||||||
<td>
|
.unwrapOr([])
|
||||||
<CommunityLink community={cv.community} />
|
.map(cv => (
|
||||||
</td>
|
<tr>
|
||||||
<td class="text-right">
|
<td>
|
||||||
{numToSI(cv.counts.subscribers)}
|
<CommunityLink community={cv.community} />
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
{numToSI(cv.counts.users_active_month)}
|
{numToSI(cv.counts.subscribers)}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right d-none d-lg-table-cell">
|
<td class="text-right">
|
||||||
{numToSI(cv.counts.posts)}
|
{numToSI(cv.counts.users_active_month)}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right d-none d-lg-table-cell">
|
<td class="text-right d-none d-lg-table-cell">
|
||||||
{numToSI(cv.counts.comments)}
|
{numToSI(cv.counts.posts)}
|
||||||
</td>
|
</td>
|
||||||
<td class="text-right">
|
<td class="text-right d-none d-lg-table-cell">
|
||||||
{cv.subscribed ? (
|
{numToSI(cv.counts.comments)}
|
||||||
<button
|
</td>
|
||||||
class="btn btn-link d-inline-block"
|
<td class="text-right">
|
||||||
onClick={linkEvent(
|
{cv.subscribed ? (
|
||||||
cv.community.id,
|
<button
|
||||||
this.handleUnsubscribe
|
class="btn btn-link d-inline-block"
|
||||||
)}
|
onClick={linkEvent(
|
||||||
>
|
cv.community.id,
|
||||||
{i18n.t("unsubscribe")}
|
this.handleUnsubscribe
|
||||||
</button>
|
)}
|
||||||
) : (
|
>
|
||||||
<button
|
{i18n.t("unsubscribe")}
|
||||||
class="btn btn-link d-inline-block"
|
</button>
|
||||||
onClick={linkEvent(
|
) : (
|
||||||
cv.community.id,
|
<button
|
||||||
this.handleSubscribe
|
class="btn btn-link d-inline-block"
|
||||||
)}
|
onClick={linkEvent(
|
||||||
>
|
cv.community.id,
|
||||||
{i18n.t("subscribe")}
|
this.handleSubscribe
|
||||||
</button>
|
)}
|
||||||
)}
|
>
|
||||||
</td>
|
{i18n.t("subscribe")}
|
||||||
</tr>
|
</button>
|
||||||
))}
|
)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -258,20 +266,20 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnsubscribe(communityId: number) {
|
handleUnsubscribe(communityId: number) {
|
||||||
let form: FollowCommunity = {
|
let form = new FollowCommunity({
|
||||||
community_id: communityId,
|
community_id: communityId,
|
||||||
follow: false,
|
follow: false,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubscribe(communityId: number) {
|
handleSubscribe(communityId: number) {
|
||||||
let form: FollowCommunity = {
|
let form = new FollowCommunity({
|
||||||
community_id: communityId,
|
community_id: communityId,
|
||||||
follow: true,
|
follow: true,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,13 +295,13 @@ export class Communities extends Component<any, CommunitiesState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
refetch() {
|
refetch() {
|
||||||
let listCommunitiesForm: ListCommunities = {
|
let listCommunitiesForm = new ListCommunities({
|
||||||
type_: this.state.listingType,
|
type_: Some(this.state.listingType),
|
||||||
sort: SortType.TopMonth,
|
sort: Some(SortType.TopMonth),
|
||||||
limit: communityLimit,
|
limit: Some(communityLimit),
|
||||||
page: this.state.page,
|
page: Some(this.state.page),
|
||||||
auth: authField(false),
|
auth: auth(false).ok(),
|
||||||
};
|
});
|
||||||
|
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.listCommunities(listCommunitiesForm)
|
wsClient.listCommunities(listCommunitiesForm)
|
||||||
|
@ -302,17 +310,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_: ListingType = pathSplit[3]
|
let type_: Option<ListingType> = Some(
|
||||||
? ListingType[pathSplit[3]]
|
pathSplit[3] ? ListingType[pathSplit[3]] : ListingType.Local
|
||||||
: ListingType.Local;
|
);
|
||||||
let page = pathSplit[5] ? Number(pathSplit[5]) : 1;
|
let page = Some(pathSplit[5] ? Number(pathSplit[5]) : 1);
|
||||||
let listCommunitiesForm: ListCommunities = {
|
let listCommunitiesForm = new ListCommunities({
|
||||||
type_,
|
type_,
|
||||||
sort: SortType.TopMonth,
|
sort: Some(SortType.TopMonth),
|
||||||
limit: communityLimit,
|
limit: Some(communityLimit),
|
||||||
page,
|
page,
|
||||||
};
|
auth: req.auth,
|
||||||
setOptionalAuth(listCommunitiesForm, req.auth);
|
});
|
||||||
|
|
||||||
return [req.client.listCommunities(listCommunitiesForm)];
|
return [req.client.listCommunities(listCommunitiesForm)];
|
||||||
}
|
}
|
||||||
|
@ -324,18 +332,26 @@ 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>(msg).data;
|
let data = wsJsonToRes<ListCommunitiesResponse>(
|
||||||
this.state.communities = data.communities;
|
msg,
|
||||||
|
ListCommunitiesResponse
|
||||||
|
);
|
||||||
|
this.state.listCommunitiesResponse = Some(data);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.FollowCommunity) {
|
} else if (op == UserOperation.FollowCommunity) {
|
||||||
let data = wsJsonToRes<CommunityResponse>(msg).data;
|
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
||||||
let found = this.state.communities.find(
|
this.state.listCommunitiesResponse.match({
|
||||||
c => c.community.id == data.community_view.community.id
|
some: res => {
|
||||||
);
|
let found = res.communities.find(
|
||||||
found.subscribed = data.community_view.subscribed;
|
c => c.community.id == data.community_view.community.id
|
||||||
found.counts.subscribers = data.community_view.counts.subscribers;
|
);
|
||||||
|
found.subscribed = data.community_view.subscribed;
|
||||||
|
found.counts.subscribers = data.community_view.counts.subscribers;
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
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 {
|
||||||
|
@ -5,30 +6,31 @@ import {
|
||||||
CommunityView,
|
CommunityView,
|
||||||
CreateCommunity,
|
CreateCommunity,
|
||||||
EditCommunity,
|
EditCommunity,
|
||||||
|
toUndefined,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
randomStr,
|
randomStr,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { Icon, Spinner } from "../common/icon";
|
import { Icon, Spinner } from "../common/icon";
|
||||||
import { ImageUploadForm } from "../common/image-upload-form";
|
import { ImageUploadForm } from "../common/image-upload-form";
|
||||||
import { MarkdownTextArea } from "../common/markdown-textarea";
|
import { MarkdownTextArea } from "../common/markdown-textarea";
|
||||||
|
|
||||||
interface CommunityFormProps {
|
interface CommunityFormProps {
|
||||||
community_view?: CommunityView; // If a community is given, that means this is an edit
|
community_view: Option<CommunityView>; // If a community is given, that means this is an edit
|
||||||
onCancel?(): any;
|
onCancel?(): any;
|
||||||
onCreate?(community: CommunityView): any;
|
onCreate?(community: CommunityView): any;
|
||||||
onEdit?(community: CommunityView): any;
|
onEdit?(community: CommunityView): any;
|
||||||
enableNsfw: boolean;
|
enableNsfw?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CommunityFormState {
|
interface CommunityFormState {
|
||||||
|
@ -44,15 +46,16 @@ export class CommunityForm extends Component<
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
|
|
||||||
private emptyState: CommunityFormState = {
|
private emptyState: CommunityFormState = {
|
||||||
communityForm: {
|
communityForm: new CreateCommunity({
|
||||||
name: null,
|
name: undefined,
|
||||||
title: null,
|
title: undefined,
|
||||||
nsfw: false,
|
description: None,
|
||||||
icon: null,
|
nsfw: None,
|
||||||
banner: null,
|
icon: None,
|
||||||
posting_restricted_to_mods: false,
|
banner: None,
|
||||||
auth: authField(false),
|
posting_restricted_to_mods: None,
|
||||||
},
|
auth: undefined,
|
||||||
|
}),
|
||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -70,31 +73,34 @@ export class CommunityForm extends Component<
|
||||||
this.handleBannerUpload = this.handleBannerUpload.bind(this);
|
this.handleBannerUpload = this.handleBannerUpload.bind(this);
|
||||||
this.handleBannerRemove = this.handleBannerRemove.bind(this);
|
this.handleBannerRemove = this.handleBannerRemove.bind(this);
|
||||||
|
|
||||||
let cv = this.props.community_view;
|
this.props.community_view.match({
|
||||||
if (cv) {
|
some: cv => {
|
||||||
this.state.communityForm = {
|
this.state.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: cv.community.nsfw,
|
nsfw: Some(cv.community.nsfw),
|
||||||
icon: cv.community.icon,
|
icon: cv.community.icon,
|
||||||
banner: cv.community.banner,
|
banner: cv.community.banner,
|
||||||
posting_restricted_to_mods: cv.community.posting_restricted_to_mods,
|
posting_restricted_to_mods: Some(
|
||||||
auth: authField(),
|
cv.community.posting_restricted_to_mods
|
||||||
};
|
),
|
||||||
}
|
auth: auth().unwrap(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO this should be checked out
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if (
|
if (
|
||||||
!this.state.loading &&
|
!this.state.loading &&
|
||||||
(this.state.communityForm.name ||
|
(this.state.communityForm.name ||
|
||||||
this.state.communityForm.title ||
|
this.state.communityForm.title ||
|
||||||
this.state.communityForm.description)
|
this.state.communityForm.description.isSome())
|
||||||
) {
|
) {
|
||||||
window.onbeforeunload = () => true;
|
window.onbeforeunload = () => true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -115,12 +121,12 @@ export class CommunityForm extends Component<
|
||||||
!this.state.loading &&
|
!this.state.loading &&
|
||||||
(this.state.communityForm.name ||
|
(this.state.communityForm.name ||
|
||||||
this.state.communityForm.title ||
|
this.state.communityForm.title ||
|
||||||
this.state.communityForm.description)
|
this.state.communityForm.description.isSome())
|
||||||
}
|
}
|
||||||
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 && (
|
{this.props.community_view.isNone() && (
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label
|
<label
|
||||||
class="col-12 col-sm-2 col-form-label"
|
class="col-12 col-sm-2 col-form-label"
|
||||||
|
@ -205,6 +211,9 @@ export class CommunityForm extends Component<
|
||||||
<div class="col-12 col-sm-10">
|
<div class="col-12 col-sm-10">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.communityForm.description}
|
initialContent={this.state.communityForm.description}
|
||||||
|
placeholder={Some("description")}
|
||||||
|
buttonTitle={None}
|
||||||
|
maxLength={None}
|
||||||
onContentChange={this.handleCommunityDescriptionChange}
|
onContentChange={this.handleCommunityDescriptionChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -221,7 +230,7 @@ export class CommunityForm extends Component<
|
||||||
class="form-check-input position-static"
|
class="form-check-input position-static"
|
||||||
id="community-nsfw"
|
id="community-nsfw"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.communityForm.nsfw}
|
checked={toUndefined(this.state.communityForm.nsfw)}
|
||||||
onChange={linkEvent(this, this.handleCommunityNsfwChange)}
|
onChange={linkEvent(this, this.handleCommunityNsfwChange)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -238,7 +247,9 @@ export class CommunityForm extends Component<
|
||||||
class="form-check-input position-static"
|
class="form-check-input position-static"
|
||||||
id="community-only-mods-can-post"
|
id="community-only-mods-can-post"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.communityForm.posting_restricted_to_mods}
|
checked={toUndefined(
|
||||||
|
this.state.communityForm.posting_restricted_to_mods
|
||||||
|
)}
|
||||||
onChange={linkEvent(
|
onChange={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleCommunityPostingRestrictedToMods
|
this.handleCommunityPostingRestrictedToMods
|
||||||
|
@ -256,13 +267,13 @@ export class CommunityForm extends Component<
|
||||||
>
|
>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : this.props.community_view ? (
|
) : this.props.community_view.isSome() ? (
|
||||||
capitalizeFirstLetter(i18n.t("save"))
|
capitalizeFirstLetter(i18n.t("save"))
|
||||||
) : (
|
) : (
|
||||||
capitalizeFirstLetter(i18n.t("create"))
|
capitalizeFirstLetter(i18n.t("create"))
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
{this.props.community_view && (
|
{this.props.community_view.isSome() && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
|
@ -281,17 +292,29 @@ export class CommunityForm extends Component<
|
||||||
handleCreateCommunitySubmit(i: CommunityForm, event: any) {
|
handleCreateCommunitySubmit(i: CommunityForm, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.loading = true;
|
i.state.loading = true;
|
||||||
if (i.props.community_view) {
|
let cForm = i.state.communityForm;
|
||||||
let form: EditCommunity = {
|
|
||||||
...i.state.communityForm,
|
i.props.community_view.match({
|
||||||
community_id: i.props.community_view.community.id,
|
some: cv => {
|
||||||
};
|
let form = new EditCommunity({
|
||||||
WebSocketService.Instance.send(wsClient.editCommunity(form));
|
community_id: cv.community.id,
|
||||||
} else {
|
title: Some(cForm.title),
|
||||||
WebSocketService.Instance.send(
|
description: cForm.description,
|
||||||
wsClient.createCommunity(i.state.communityForm)
|
icon: cForm.icon,
|
||||||
);
|
banner: cForm.banner,
|
||||||
}
|
nsfw: cForm.nsfw,
|
||||||
|
posting_restricted_to_mods: cForm.posting_restricted_to_mods,
|
||||||
|
auth: auth().unwrap(),
|
||||||
|
});
|
||||||
|
|
||||||
|
WebSocketService.Instance.send(wsClient.editCommunity(form));
|
||||||
|
},
|
||||||
|
none: () => {
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.createCommunity(i.state.communityForm)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,7 +329,7 @@ export class CommunityForm extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommunityDescriptionChange(val: string) {
|
handleCommunityDescriptionChange(val: string) {
|
||||||
this.state.communityForm.description = val;
|
this.state.communityForm.description = Some(val);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,22 +348,22 @@ export class CommunityForm extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleIconUpload(url: string) {
|
handleIconUpload(url: string) {
|
||||||
this.state.communityForm.icon = url;
|
this.state.communityForm.icon = Some(url);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleIconRemove() {
|
handleIconRemove() {
|
||||||
this.state.communityForm.icon = "";
|
this.state.communityForm.icon = Some("");
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerUpload(url: string) {
|
handleBannerUpload(url: string) {
|
||||||
this.state.communityForm.banner = url;
|
this.state.communityForm.banner = Some(url);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerRemove() {
|
handleBannerRemove() {
|
||||||
this.state.communityForm.banner = "";
|
this.state.communityForm.banner = Some("");
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,42 +377,51 @@ export class CommunityForm extends Component<
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.CreateCommunity) {
|
} else if (op == UserOperation.CreateCommunity) {
|
||||||
let data = wsJsonToRes<CommunityResponse>(msg).data;
|
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
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;
|
||||||
let person = UserService.Instance.myUserInfo.local_user_view.person;
|
|
||||||
UserService.Instance.myUserInfo.follows.push({
|
UserService.Instance.myUserInfo.match({
|
||||||
community,
|
some: mui => {
|
||||||
follower: person,
|
let person = mui.local_user_view.person;
|
||||||
});
|
mui.follows.push({
|
||||||
UserService.Instance.myUserInfo.moderates.push({
|
community,
|
||||||
community,
|
follower: person,
|
||||||
moderator: person,
|
});
|
||||||
|
mui.moderates.push({
|
||||||
|
community,
|
||||||
|
moderator: person,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
});
|
});
|
||||||
} else if (op == UserOperation.EditCommunity) {
|
} else if (op == UserOperation.EditCommunity) {
|
||||||
let data = wsJsonToRes<CommunityResponse>(msg).data;
|
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
||||||
this.state.loading = false;
|
this.state.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;
|
||||||
|
|
||||||
let followFound = UserService.Instance.myUserInfo.follows.findIndex(
|
UserService.Instance.myUserInfo.match({
|
||||||
f => f.community.id == community.id
|
some: mui => {
|
||||||
);
|
let followFound = mui.follows.findIndex(
|
||||||
if (followFound) {
|
f => f.community.id == community.id
|
||||||
UserService.Instance.myUserInfo.follows[followFound].community =
|
);
|
||||||
community;
|
if (followFound) {
|
||||||
}
|
mui.follows[followFound].community = community;
|
||||||
|
}
|
||||||
|
|
||||||
let moderatesFound = UserService.Instance.myUserInfo.moderates.findIndex(
|
let moderatesFound = mui.moderates.findIndex(
|
||||||
f => f.community.id == community.id
|
f => f.community.id == community.id
|
||||||
);
|
);
|
||||||
if (moderatesFound) {
|
if (moderatesFound) {
|
||||||
UserService.Instance.myUserInfo.moderates[moderatesFound].community =
|
mui.moderates[moderatesFound].community = community;
|
||||||
community;
|
}
|
||||||
}
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,12 +56,14 @@ export class CommunityLink extends Component<CommunityLinkProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
avatarAndName(displayName: string) {
|
avatarAndName(displayName: string) {
|
||||||
let community = this.props.community;
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!this.props.hideAvatar && community.icon && showAvatars() && (
|
{!this.props.hideAvatar &&
|
||||||
<PictrsImage src={community.icon} icon />
|
showAvatars() &&
|
||||||
)}
|
this.props.community.icon.match({
|
||||||
|
some: icon => <PictrsImage src={icon} icon />,
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
<span class="overflow-wrap-anywhere">{displayName}</span>
|
<span class="overflow-wrap-anywhere">{displayName}</span>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { None, Option, Some } from "@sniptt/monads";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
AddModToCommunityResponse,
|
AddModToCommunityResponse,
|
||||||
|
@ -19,20 +20,25 @@ import {
|
||||||
PostResponse,
|
PostResponse,
|
||||||
PostView,
|
PostView,
|
||||||
SortType,
|
SortType,
|
||||||
|
toOption,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { DataType, InitialFetchRequest } from "../../interfaces";
|
import { DataType, InitialFetchRequest } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
communityRSSUrl,
|
communityRSSUrl,
|
||||||
createCommentLikeRes,
|
createCommentLikeRes,
|
||||||
createPostLikeFindRes,
|
createPostLikeFindRes,
|
||||||
editCommentRes,
|
editCommentRes,
|
||||||
editPostFindRes,
|
editPostFindRes,
|
||||||
|
enableDownvotes,
|
||||||
|
enableNsfw,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
getDataTypeFromProps,
|
getDataTypeFromProps,
|
||||||
getPageFromProps,
|
getPageFromProps,
|
||||||
|
@ -43,15 +49,12 @@ import {
|
||||||
saveCommentRes,
|
saveCommentRes,
|
||||||
saveScrollPosition,
|
saveScrollPosition,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
setOptionalAuth,
|
|
||||||
setupTippy,
|
setupTippy,
|
||||||
showLocal,
|
showLocal,
|
||||||
toast,
|
toast,
|
||||||
updatePersonBlock,
|
updatePersonBlock,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { CommentNodes } from "../comment/comment-nodes";
|
import { CommentNodes } from "../comment/comment-nodes";
|
||||||
import { BannerIconHeader } from "../common/banner-icon-header";
|
import { BannerIconHeader } from "../common/banner-icon-header";
|
||||||
|
@ -66,7 +69,7 @@ import { PostListings } from "../post/post-listings";
|
||||||
import { CommunityLink } from "./community-link";
|
import { CommunityLink } from "./community-link";
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
communityRes: GetCommunityResponse;
|
communityRes: Option<GetCommunityResponse>;
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
communityName: string;
|
communityName: string;
|
||||||
communityLoading: boolean;
|
communityLoading: boolean;
|
||||||
|
@ -93,10 +96,15 @@ interface UrlParams {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Community extends Component<any, State> {
|
export class Community extends Component<any, State> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(
|
||||||
|
this.context,
|
||||||
|
GetCommunityResponse,
|
||||||
|
GetPostsResponse,
|
||||||
|
GetCommentsResponse
|
||||||
|
);
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: State = {
|
private emptyState: State = {
|
||||||
communityRes: undefined,
|
communityRes: None,
|
||||||
communityName: this.props.match.params.name,
|
communityName: this.props.match.params.name,
|
||||||
communityLoading: true,
|
communityLoading: true,
|
||||||
postsLoading: true,
|
postsLoading: true,
|
||||||
|
@ -123,12 +131,21 @@ export class Community extends Component<any, State> {
|
||||||
|
|
||||||
// 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.communityRes = this.isoData.routeData[0];
|
this.state.communityRes = Some(
|
||||||
if (this.state.dataType == DataType.Post) {
|
this.isoData.routeData[0] as GetCommunityResponse
|
||||||
this.state.posts = this.isoData.routeData[1].posts;
|
);
|
||||||
} else {
|
let postsRes = Some(this.isoData.routeData[1] as GetPostsResponse);
|
||||||
this.state.comments = this.isoData.routeData[1].comments;
|
let commentsRes = Some(this.isoData.routeData[2] as GetCommentsResponse);
|
||||||
}
|
|
||||||
|
postsRes.match({
|
||||||
|
some: pvs => (this.state.posts = pvs.posts),
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
|
commentsRes.match({
|
||||||
|
some: cvs => (this.state.comments = cvs.comments),
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
|
|
||||||
this.state.communityLoading = false;
|
this.state.communityLoading = false;
|
||||||
this.state.postsLoading = false;
|
this.state.postsLoading = false;
|
||||||
this.state.commentsLoading = false;
|
this.state.commentsLoading = false;
|
||||||
|
@ -139,10 +156,11 @@ export class Community extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchCommunity() {
|
fetchCommunity() {
|
||||||
let form: GetCommunity = {
|
let form = new GetCommunity({
|
||||||
name: this.state.communityName ? this.state.communityName : null,
|
name: Some(this.state.communityName),
|
||||||
auth: authField(false),
|
id: None,
|
||||||
};
|
auth: auth(false).ok(),
|
||||||
|
});
|
||||||
WebSocketService.Instance.send(wsClient.getCommunity(form));
|
WebSocketService.Instance.send(wsClient.getCommunity(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,56 +187,62 @@ 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: GetCommunity = { name: communityName };
|
let communityForm = new GetCommunity({
|
||||||
setOptionalAuth(communityForm, req.auth);
|
name: Some(communityName),
|
||||||
|
id: None,
|
||||||
|
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: SortType = pathSplit[6]
|
let sort: Option<SortType> = toOption(
|
||||||
? SortType[pathSplit[6]]
|
pathSplit[6]
|
||||||
: UserService.Instance.myUserInfo
|
? SortType[pathSplit[6]]
|
||||||
? Object.values(SortType)[
|
: UserService.Instance.myUserInfo.match({
|
||||||
UserService.Instance.myUserInfo.local_user_view.local_user
|
some: mui =>
|
||||||
.default_sort_type
|
Object.values(SortType)[
|
||||||
]
|
mui.local_user_view.local_user.default_sort_type
|
||||||
: SortType.Active;
|
],
|
||||||
|
none: SortType.Active,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
let page = pathSplit[8] ? Number(pathSplit[8]) : 1;
|
let page = toOption(pathSplit[8] ? Number(pathSplit[8]) : 1);
|
||||||
|
|
||||||
if (dataType == DataType.Post) {
|
if (dataType == DataType.Post) {
|
||||||
let getPostsForm: GetPosts = {
|
let getPostsForm = new GetPosts({
|
||||||
|
community_name: Some(communityName),
|
||||||
|
community_id: None,
|
||||||
page,
|
page,
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
sort,
|
sort,
|
||||||
type_: ListingType.Community,
|
type_: Some(ListingType.Community),
|
||||||
saved_only: false,
|
saved_only: Some(false),
|
||||||
};
|
auth: req.auth,
|
||||||
setOptionalAuth(getPostsForm, req.auth);
|
});
|
||||||
this.setName(getPostsForm, communityName);
|
|
||||||
promises.push(req.client.getPosts(getPostsForm));
|
promises.push(req.client.getPosts(getPostsForm));
|
||||||
|
promises.push(Promise.resolve());
|
||||||
} else {
|
} else {
|
||||||
let getCommentsForm: GetComments = {
|
let getCommentsForm = new GetComments({
|
||||||
|
community_name: Some(communityName),
|
||||||
|
community_id: None,
|
||||||
page,
|
page,
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
sort,
|
sort,
|
||||||
type_: ListingType.Community,
|
type_: Some(ListingType.Community),
|
||||||
saved_only: false,
|
saved_only: Some(false),
|
||||||
};
|
auth: req.auth,
|
||||||
this.setName(getCommentsForm, communityName);
|
});
|
||||||
setOptionalAuth(getCommentsForm, req.auth);
|
promises.push(Promise.resolve());
|
||||||
promises.push(req.client.getComments(getCommentsForm));
|
promises.push(req.client.getComments(getCommentsForm));
|
||||||
}
|
}
|
||||||
|
|
||||||
return promises;
|
return promises;
|
||||||
}
|
}
|
||||||
|
|
||||||
static setName(obj: any, name_: string) {
|
|
||||||
obj.community_name = name_;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate(_: any, lastState: State) {
|
componentDidUpdate(_: any, lastState: State) {
|
||||||
if (
|
if (
|
||||||
lastState.dataType !== this.state.dataType ||
|
lastState.dataType !== this.state.dataType ||
|
||||||
|
@ -231,11 +255,18 @@ export class Community extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `${this.state.communityRes.community_view.community.title} - ${this.state.siteRes.site_view.site.name}`;
|
return this.state.communityRes.match({
|
||||||
|
some: res =>
|
||||||
|
this.state.siteRes.site_view.match({
|
||||||
|
some: siteView =>
|
||||||
|
`${res.community_view.community.title} - ${siteView.site.name}`,
|
||||||
|
none: "",
|
||||||
|
}),
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let cv = this.state.communityRes?.community_view;
|
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{this.state.communityLoading ? (
|
{this.state.communityLoading ? (
|
||||||
|
@ -243,83 +274,99 @@ export class Community extends Component<any, State> {
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
</h5>
|
</h5>
|
||||||
) : (
|
) : (
|
||||||
<>
|
this.state.communityRes.match({
|
||||||
<HtmlTags
|
some: res => (
|
||||||
title={this.documentTitle}
|
<>
|
||||||
path={this.context.router.route.match.url}
|
<HtmlTags
|
||||||
description={cv.community.description}
|
title={this.documentTitle}
|
||||||
image={cv.community.icon}
|
path={this.context.router.route.match.url}
|
||||||
/>
|
description={res.community_view.community.description}
|
||||||
|
image={res.community_view.community.icon}
|
||||||
|
/>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-md-8">
|
<div class="col-12 col-md-8">
|
||||||
{this.communityInfo()}
|
{this.communityInfo()}
|
||||||
<div class="d-block d-md-none">
|
<div class="d-block d-md-none">
|
||||||
<button
|
<button
|
||||||
class="btn btn-secondary d-inline-block mb-2 mr-3"
|
class="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>
|
|
||||||
{this.state.showSidebarMobile && (
|
|
||||||
<>
|
|
||||||
<Sidebar
|
|
||||||
community_view={cv}
|
|
||||||
moderators={this.state.communityRes.moderators}
|
|
||||||
admins={this.state.siteRes.admins}
|
|
||||||
online={this.state.communityRes.online}
|
|
||||||
enableNsfw={
|
|
||||||
this.state.siteRes.site_view.site.enable_nsfw
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{!cv.community.local && this.state.communityRes.site && (
|
|
||||||
<SiteSidebar
|
|
||||||
site={this.state.communityRes.site}
|
|
||||||
showLocal={showLocal(this.isoData)}
|
|
||||||
/>
|
/>
|
||||||
|
</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)}
|
||||||
|
/>
|
||||||
|
{!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 class="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)}
|
||||||
|
/>
|
||||||
|
{!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>
|
||||||
</div>
|
</div>
|
||||||
{this.selects()}
|
</>
|
||||||
{this.listings()}
|
),
|
||||||
<Paginator
|
none: <></>,
|
||||||
page={this.state.page}
|
})
|
||||||
onChange={this.handlePageChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="d-none d-md-block col-md-4">
|
|
||||||
<Sidebar
|
|
||||||
community_view={cv}
|
|
||||||
moderators={this.state.communityRes.moderators}
|
|
||||||
admins={this.state.siteRes.admins}
|
|
||||||
online={this.state.communityRes.online}
|
|
||||||
enableNsfw={this.state.siteRes.site_view.site.enable_nsfw}
|
|
||||||
/>
|
|
||||||
{!cv.community.local && this.state.communityRes.site && (
|
|
||||||
<SiteSidebar
|
|
||||||
site={this.state.communityRes.site}
|
|
||||||
showLocal={showLocal(this.isoData)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
listings() {
|
listings() {
|
||||||
let site = this.state.siteRes.site_view.site;
|
|
||||||
return this.state.dataType == DataType.Post ? (
|
return this.state.dataType == DataType.Post ? (
|
||||||
this.state.postsLoading ? (
|
this.state.postsLoading ? (
|
||||||
<h5>
|
<h5>
|
||||||
|
@ -329,8 +376,8 @@ export class Community extends Component<any, State> {
|
||||||
<PostListings
|
<PostListings
|
||||||
posts={this.state.posts}
|
posts={this.state.posts}
|
||||||
removeDuplicates
|
removeDuplicates
|
||||||
enableDownvotes={site.enable_downvotes}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
enableNsfw={site.enable_nsfw}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
) : this.state.commentsLoading ? (
|
) : this.state.commentsLoading ? (
|
||||||
|
@ -342,32 +389,38 @@ export class Community extends Component<any, State> {
|
||||||
nodes={commentsToFlatNodes(this.state.comments)}
|
nodes={commentsToFlatNodes(this.state.comments)}
|
||||||
noIndent
|
noIndent
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={site.enable_downvotes}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
|
moderators={this.state.communityRes.map(r => r.moderators)}
|
||||||
|
admins={Some(this.state.siteRes.admins)}
|
||||||
|
maxCommentsShown={None}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
communityInfo() {
|
communityInfo() {
|
||||||
let community = this.state.communityRes.community_view.community;
|
return this.state.communityRes
|
||||||
return (
|
.map(r => r.community_view.community)
|
||||||
<div class="mb-2">
|
.match({
|
||||||
<BannerIconHeader banner={community.banner} icon={community.icon} />
|
some: community => (
|
||||||
<h5 class="mb-0 overflow-wrap-anywhere">{community.title}</h5>
|
<div class="mb-2">
|
||||||
<CommunityLink
|
<BannerIconHeader banner={community.banner} icon={community.icon} />
|
||||||
community={community}
|
<h5 class="mb-0 overflow-wrap-anywhere">{community.title}</h5>
|
||||||
realLink
|
<CommunityLink
|
||||||
useApubName
|
community={community}
|
||||||
muted
|
realLink
|
||||||
hideAvatar
|
useApubName
|
||||||
/>
|
muted
|
||||||
</div>
|
hideAvatar
|
||||||
);
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
selects() {
|
selects() {
|
||||||
let communityRss = communityRSSUrl(
|
let communityRss = this.state.communityRes.map(r =>
|
||||||
this.state.communityRes.community_view.community.actor_id,
|
communityRSSUrl(r.community_view.community.actor_id, this.state.sort)
|
||||||
this.state.sort
|
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
|
@ -380,10 +433,17 @@ export class Community extends Component<any, State> {
|
||||||
<span class="mr-2">
|
<span class="mr-2">
|
||||||
<SortSelect sort={this.state.sort} onChange={this.handleSortChange} />
|
<SortSelect sort={this.state.sort} onChange={this.handleSortChange} />
|
||||||
</span>
|
</span>
|
||||||
<a href={communityRss} title="RSS" rel={relTags}>
|
{communityRss.match({
|
||||||
<Icon icon="rss" classes="text-muted small" />
|
some: rss => (
|
||||||
</a>
|
<>
|
||||||
<link rel="alternate" type="application/atom+xml" href={communityRss} />
|
<a href={rss} title="RSS" rel={relTags}>
|
||||||
|
<Icon icon="rss" classes="text-muted small" />
|
||||||
|
</a>
|
||||||
|
<link rel="alternate" type="application/atom+xml" href={rss} />
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -422,26 +482,28 @@ export class Community extends Component<any, State> {
|
||||||
|
|
||||||
fetchData() {
|
fetchData() {
|
||||||
if (this.state.dataType == DataType.Post) {
|
if (this.state.dataType == DataType.Post) {
|
||||||
let form: GetPosts = {
|
let form = new GetPosts({
|
||||||
page: this.state.page,
|
page: Some(this.state.page),
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
sort: this.state.sort,
|
sort: Some(this.state.sort),
|
||||||
type_: ListingType.Community,
|
type_: Some(ListingType.Community),
|
||||||
community_name: this.state.communityName,
|
community_name: Some(this.state.communityName),
|
||||||
saved_only: false,
|
community_id: None,
|
||||||
auth: authField(false),
|
saved_only: Some(false),
|
||||||
};
|
auth: auth(false).ok(),
|
||||||
|
});
|
||||||
WebSocketService.Instance.send(wsClient.getPosts(form));
|
WebSocketService.Instance.send(wsClient.getPosts(form));
|
||||||
} else {
|
} else {
|
||||||
let form: GetComments = {
|
let form = new GetComments({
|
||||||
page: this.state.page,
|
page: Some(this.state.page),
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
sort: this.state.sort,
|
sort: Some(this.state.sort),
|
||||||
type_: ListingType.Community,
|
type_: Some(ListingType.Community),
|
||||||
community_name: this.state.communityName,
|
community_name: Some(this.state.communityName),
|
||||||
saved_only: false,
|
community_id: None,
|
||||||
auth: authField(false),
|
saved_only: Some(false),
|
||||||
};
|
auth: auth(false).ok(),
|
||||||
|
});
|
||||||
WebSocketService.Instance.send(wsClient.getComments(form));
|
WebSocketService.Instance.send(wsClient.getComments(form));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -454,15 +516,20 @@ export class Community extends Component<any, State> {
|
||||||
this.context.router.history.push("/");
|
this.context.router.history.push("/");
|
||||||
return;
|
return;
|
||||||
} else if (msg.reconnect) {
|
} else if (msg.reconnect) {
|
||||||
WebSocketService.Instance.send(
|
this.state.communityRes.match({
|
||||||
wsClient.communityJoin({
|
some: res => {
|
||||||
community_id: this.state.communityRes.community_view.community.id,
|
WebSocketService.Instance.send(
|
||||||
})
|
wsClient.communityJoin({
|
||||||
);
|
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).data;
|
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
||||||
this.state.communityRes = data;
|
this.state.communityRes = Some(data);
|
||||||
this.state.communityLoading = false;
|
this.state.communityLoading = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
// TODO why is there no auth in this form?
|
// TODO why is there no auth in this form?
|
||||||
|
@ -476,18 +543,25 @@ export class Community extends Component<any, State> {
|
||||||
op == UserOperation.DeleteCommunity ||
|
op == UserOperation.DeleteCommunity ||
|
||||||
op == UserOperation.RemoveCommunity
|
op == UserOperation.RemoveCommunity
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<CommunityResponse>(msg).data;
|
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
||||||
this.state.communityRes.community_view = data.community_view;
|
this.state.communityRes.match({
|
||||||
|
some: res => (res.community_view = data.community_view),
|
||||||
|
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).data;
|
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
||||||
this.state.communityRes.community_view.subscribed =
|
this.state.communityRes.match({
|
||||||
data.community_view.subscribed;
|
some: res => {
|
||||||
this.state.communityRes.community_view.counts.subscribers =
|
res.community_view.subscribed = data.community_view.subscribed;
|
||||||
data.community_view.counts.subscribers;
|
res.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).data;
|
let data = wsJsonToRes<GetPostsResponse>(msg, GetPostsResponse);
|
||||||
this.state.posts = data.posts;
|
this.state.posts = data.posts;
|
||||||
this.state.postsLoading = false;
|
this.state.postsLoading = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
@ -501,29 +575,39 @@ export class Community extends Component<any, State> {
|
||||||
op == UserOperation.StickyPost ||
|
op == UserOperation.StickyPost ||
|
||||||
op == UserOperation.SavePost
|
op == UserOperation.SavePost
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg).data;
|
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
||||||
this.state.posts.unshift(data.post_view);
|
this.state.posts.unshift(data.post_view);
|
||||||
if (
|
if (
|
||||||
UserService.Instance.myUserInfo?.local_user_view.local_user
|
UserService.Instance.myUserInfo
|
||||||
.show_new_post_notifs
|
.map(m => m.local_user_view.local_user.show_new_post_notifs)
|
||||||
|
.unwrapOr(false)
|
||||||
) {
|
) {
|
||||||
notifyPost(data.post_view, this.context.router);
|
notifyPost(data.post_view, this.context.router);
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.CreatePostLike) {
|
} else if (op == UserOperation.CreatePostLike) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg).data;
|
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
||||||
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>(msg).data;
|
let data = wsJsonToRes<AddModToCommunityResponse>(
|
||||||
this.state.communityRes.moderators = data.moderators;
|
msg,
|
||||||
|
AddModToCommunityResponse
|
||||||
|
);
|
||||||
|
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>(msg).data;
|
let data = wsJsonToRes<BanFromCommunityResponse>(
|
||||||
|
msg,
|
||||||
|
BanFromCommunityResponse
|
||||||
|
);
|
||||||
|
|
||||||
// TODO this might be incorrect
|
// TODO this might be incorrect
|
||||||
this.state.posts
|
this.state.posts
|
||||||
|
@ -532,7 +616,7 @@ 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).data;
|
let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
|
||||||
this.state.comments = data.comments;
|
this.state.comments = data.comments;
|
||||||
this.state.commentsLoading = false;
|
this.state.commentsLoading = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
@ -541,11 +625,11 @@ export class Community extends Component<any, State> {
|
||||||
op == UserOperation.DeleteComment ||
|
op == UserOperation.DeleteComment ||
|
||||||
op == UserOperation.RemoveComment
|
op == UserOperation.RemoveComment
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
|
|
||||||
// Necessary since it might be a user reply
|
// Necessary since it might be a user reply
|
||||||
if (data.form_id) {
|
if (data.form_id) {
|
||||||
|
@ -553,23 +637,23 @@ 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).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
|
||||||
updatePersonBlock(data);
|
updatePersonBlock(data);
|
||||||
} else if (op == UserOperation.CreatePostReport) {
|
} else if (op == UserOperation.CreatePostReport) {
|
||||||
let data = wsJsonToRes<PostReportResponse>(msg).data;
|
let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
toast(i18n.t("report_created"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,22 @@
|
||||||
|
import { None } from "@sniptt/monads";
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import { CommunityView, SiteView } from "lemmy-js-client";
|
import { CommunityView, GetSiteResponse } from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService } from "../../services";
|
import { UserService } from "../../services";
|
||||||
import { isBrowser, setIsoData, toast, wsSubscribe } from "../../utils";
|
import {
|
||||||
|
enableNsfw,
|
||||||
|
isBrowser,
|
||||||
|
setIsoData,
|
||||||
|
toast,
|
||||||
|
wsSubscribe,
|
||||||
|
} from "../../utils";
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
import { CommunityForm } from "./community-form";
|
import { CommunityForm } from "./community-form";
|
||||||
|
|
||||||
interface CreateCommunityState {
|
interface CreateCommunityState {
|
||||||
site_view: SiteView;
|
siteRes: GetSiteResponse;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +24,7 @@ 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 = {
|
private emptyState: CreateCommunityState = {
|
||||||
site_view: this.isoData.site_res.site_view,
|
siteRes: this.isoData.site_res,
|
||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -28,7 +35,7 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
|
||||||
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 && isBrowser()) {
|
if (UserService.Instance.myUserInfo.isNone() && 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`);
|
||||||
}
|
}
|
||||||
|
@ -41,7 +48,10 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `${i18n.t("create_community")} - ${this.state.site_view.site.name}`;
|
return this.state.siteRes.site_view.match({
|
||||||
|
some: siteView => `${i18n.t("create_community")} - ${siteView.site.name}`,
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -50,6 +60,8 @@ 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>
|
||||||
|
@ -60,8 +72,9 @@ export class CreateCommunity extends Component<any, CreateCommunityState> {
|
||||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
<div class="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={this.state.site_view.site.enable_nsfw}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { 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 {
|
||||||
|
@ -8,11 +9,15 @@ import {
|
||||||
FollowCommunity,
|
FollowCommunity,
|
||||||
PersonViewSafe,
|
PersonViewSafe,
|
||||||
RemoveCommunity,
|
RemoveCommunity,
|
||||||
|
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 {
|
import {
|
||||||
authField,
|
amAdmin,
|
||||||
|
amMod,
|
||||||
|
amTopMod,
|
||||||
|
auth,
|
||||||
getUnixTime,
|
getUnixTime,
|
||||||
mdToHtml,
|
mdToHtml,
|
||||||
numToSI,
|
numToSI,
|
||||||
|
@ -29,15 +34,15 @@ interface SidebarProps {
|
||||||
moderators: CommunityModeratorView[];
|
moderators: CommunityModeratorView[];
|
||||||
admins: PersonViewSafe[];
|
admins: PersonViewSafe[];
|
||||||
online: number;
|
online: number;
|
||||||
enableNsfw: boolean;
|
enableNsfw?: boolean;
|
||||||
showIcon?: boolean;
|
showIcon?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SidebarState {
|
interface SidebarState {
|
||||||
|
removeReason: Option<string>;
|
||||||
|
removeExpires: Option<string>;
|
||||||
showEdit: boolean;
|
showEdit: boolean;
|
||||||
showRemoveDialog: boolean;
|
showRemoveDialog: boolean;
|
||||||
removeReason: string;
|
|
||||||
removeExpires: string;
|
|
||||||
showConfirmLeaveModTeam: boolean;
|
showConfirmLeaveModTeam: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +69,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
this.sidebar()
|
this.sidebar()
|
||||||
) : (
|
) : (
|
||||||
<CommunityForm
|
<CommunityForm
|
||||||
community_view={this.props.community_view}
|
community_view={Some(this.props.community_view)}
|
||||||
onEdit={this.handleEditCommunity}
|
onEdit={this.handleEditCommunity}
|
||||||
onCancel={this.handleEditCancel}
|
onCancel={this.handleEditCancel}
|
||||||
enableNsfw={this.props.enableNsfw}
|
enableNsfw={this.props.enableNsfw}
|
||||||
|
@ -284,14 +289,12 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
|
|
||||||
description() {
|
description() {
|
||||||
let description = this.props.community_view.community.description;
|
let description = this.props.community_view.community.description;
|
||||||
return (
|
return description.match({
|
||||||
description && (
|
some: desc => (
|
||||||
<div
|
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(desc)} />
|
||||||
className="md-div"
|
),
|
||||||
dangerouslySetInnerHTML={mdToHtml(description)}
|
none: <></>,
|
||||||
/>
|
});
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
adminButtons() {
|
adminButtons() {
|
||||||
|
@ -299,7 +302,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ul class="list-inline mb-1 text-muted font-weight-bold">
|
<ul class="list-inline mb-1 text-muted font-weight-bold">
|
||||||
{this.canMod && (
|
{amMod(Some(this.props.moderators)) && (
|
||||||
<>
|
<>
|
||||||
<li className="list-inline-item-action">
|
<li className="list-inline-item-action">
|
||||||
<button
|
<button
|
||||||
|
@ -311,7 +314,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
<Icon icon="edit" classes="icon-inline" />
|
<Icon icon="edit" classes="icon-inline" />
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{!this.amTopMod &&
|
{!amTopMod(Some(this.props.moderators)) &&
|
||||||
(!this.state.showConfirmLeaveModTeam ? (
|
(!this.state.showConfirmLeaveModTeam ? (
|
||||||
<li className="list-inline-item-action">
|
<li className="list-inline-item-action">
|
||||||
<button
|
<button
|
||||||
|
@ -350,7 +353,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
</li>
|
</li>
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
{this.amTopMod && (
|
{amTopMod(Some(this.props.moderators)) && (
|
||||||
<li className="list-inline-item-action">
|
<li className="list-inline-item-action">
|
||||||
<button
|
<button
|
||||||
class="btn btn-link text-muted d-inline-block"
|
class="btn btn-link text-muted d-inline-block"
|
||||||
|
@ -377,7 +380,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{this.canAdmin && (
|
{amAdmin(Some(this.props.admins)) && (
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
{!this.props.community_view.community.removed ? (
|
{!this.props.community_view.community.removed ? (
|
||||||
<button
|
<button
|
||||||
|
@ -408,7 +411,7 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
id="remove-reason"
|
id="remove-reason"
|
||||||
class="form-control mr-2"
|
class="form-control mr-2"
|
||||||
placeholder={i18n.t("optional")}
|
placeholder={i18n.t("optional")}
|
||||||
value={this.state.removeReason}
|
value={toUndefined(this.state.removeReason)}
|
||||||
onInput={linkEvent(this, this.handleModRemoveReasonChange)}
|
onInput={linkEvent(this, this.handleModRemoveReasonChange)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -445,11 +448,11 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
|
|
||||||
handleDeleteClick(i: Sidebar, event: any) {
|
handleDeleteClick(i: Sidebar, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let deleteForm: DeleteCommunity = {
|
let deleteForm = new DeleteCommunity({
|
||||||
community_id: i.props.community_view.community.id,
|
community_id: i.props.community_view.community.id,
|
||||||
deleted: !i.props.community_view.community.deleted,
|
deleted: !i.props.community_view.community.deleted,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.deleteCommunity(deleteForm));
|
WebSocketService.Instance.send(wsClient.deleteCommunity(deleteForm));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,15 +462,20 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLeaveModTeamClick(i: Sidebar) {
|
handleLeaveModTeamClick(i: Sidebar) {
|
||||||
let form: AddModToCommunity = {
|
UserService.Instance.myUserInfo.match({
|
||||||
person_id: UserService.Instance.myUserInfo.local_user_view.person.id,
|
some: mui => {
|
||||||
community_id: i.props.community_view.community.id,
|
let form = new AddModToCommunity({
|
||||||
added: false,
|
person_id: mui.local_user_view.person.id,
|
||||||
auth: authField(),
|
community_id: i.props.community_view.community.id,
|
||||||
};
|
added: false,
|
||||||
WebSocketService.Instance.send(wsClient.addModToCommunity(form));
|
auth: auth().unwrap(),
|
||||||
i.state.showConfirmLeaveModTeam = false;
|
});
|
||||||
i.setState(i.state);
|
WebSocketService.Instance.send(wsClient.addModToCommunity(form));
|
||||||
|
i.state.showConfirmLeaveModTeam = false;
|
||||||
|
i.setState(i.state);
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCancelLeaveModTeamClick(i: Sidebar) {
|
handleCancelLeaveModTeamClick(i: Sidebar) {
|
||||||
|
@ -478,67 +486,47 @@ 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: FollowCommunity = {
|
let form = new FollowCommunity({
|
||||||
community_id,
|
community_id,
|
||||||
follow: false,
|
follow: false,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
||||||
|
|
||||||
// Update myUserInfo
|
// Update myUserInfo
|
||||||
UserService.Instance.myUserInfo.follows =
|
UserService.Instance.myUserInfo.match({
|
||||||
UserService.Instance.myUserInfo.follows.filter(
|
some: mui =>
|
||||||
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: FollowCommunity = {
|
let form = new FollowCommunity({
|
||||||
community_id,
|
community_id,
|
||||||
follow: true,
|
follow: true,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
WebSocketService.Instance.send(wsClient.followCommunity(form));
|
||||||
|
|
||||||
// Update myUserInfo
|
// Update myUserInfo
|
||||||
UserService.Instance.myUserInfo.follows.push({
|
UserService.Instance.myUserInfo.match({
|
||||||
community: i.props.community_view.community,
|
some: mui =>
|
||||||
follower: UserService.Instance.myUserInfo.local_user_view.person,
|
mui.follows.push({
|
||||||
|
community: i.props.community_view.community,
|
||||||
|
follower: mui.local_user_view.person,
|
||||||
|
}),
|
||||||
|
none: void 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private get amTopMod(): boolean {
|
|
||||||
return (
|
|
||||||
this.props.moderators[0].moderator.id ==
|
|
||||||
UserService.Instance.myUserInfo.local_user_view.person.id
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get canMod(): boolean {
|
|
||||||
return (
|
|
||||||
UserService.Instance.myUserInfo &&
|
|
||||||
this.props.moderators
|
|
||||||
.map(m => m.moderator.id)
|
|
||||||
.includes(UserService.Instance.myUserInfo.local_user_view.person.id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get canAdmin(): boolean {
|
|
||||||
return (
|
|
||||||
UserService.Instance.myUserInfo &&
|
|
||||||
this.props.admins
|
|
||||||
.map(a => a.person.id)
|
|
||||||
.includes(UserService.Instance.myUserInfo.local_user_view.person.id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ||
|
||||||
this.canMod ||
|
amMod(Some(this.props.moderators)) ||
|
||||||
this.canAdmin
|
amAdmin(Some(this.props.admins))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -560,13 +548,13 @@ export class Sidebar extends Component<SidebarProps, SidebarState> {
|
||||||
|
|
||||||
handleModRemoveSubmit(i: Sidebar, event: any) {
|
handleModRemoveSubmit(i: Sidebar, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let removeForm: RemoveCommunity = {
|
let removeForm = new RemoveCommunity({
|
||||||
community_id: i.props.community_view.community.id,
|
community_id: i.props.community_view.community.id,
|
||||||
removed: !i.props.community_view.community.removed,
|
removed: !i.props.community_view.community.removed,
|
||||||
reason: i.state.removeReason,
|
reason: i.state.removeReason,
|
||||||
expires: getUnixTime(i.state.removeExpires),
|
expires: i.state.removeExpires.map(getUnixTime),
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.removeCommunity(removeForm));
|
WebSocketService.Instance.send(wsClient.removeCommunity(removeForm));
|
||||||
|
|
||||||
i.state.showRemoveDialog = false;
|
i.state.showRemoveDialog = false;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
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 {
|
import {
|
||||||
|
@ -9,14 +10,17 @@ import {
|
||||||
PersonViewSafe,
|
PersonViewSafe,
|
||||||
SaveSiteConfig,
|
SaveSiteConfig,
|
||||||
SiteResponse,
|
SiteResponse,
|
||||||
|
toUndefined,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { InitialFetchRequest } from "../../interfaces";
|
import { InitialFetchRequest } from "../../interfaces";
|
||||||
import { WebSocketService } from "../../services";
|
import { WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
randomStr,
|
randomStr,
|
||||||
|
@ -24,9 +28,7 @@ import {
|
||||||
showLocal,
|
showLocal,
|
||||||
toast,
|
toast,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
|
@ -35,24 +37,26 @@ import { SiteForm } from "./site-form";
|
||||||
|
|
||||||
interface AdminSettingsState {
|
interface AdminSettingsState {
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
siteConfigRes: GetSiteConfigResponse;
|
siteConfigRes: Option<GetSiteConfigResponse>;
|
||||||
siteConfigHjson: string;
|
siteConfigHjson: Option<string>;
|
||||||
loading: boolean;
|
|
||||||
banned: PersonViewSafe[];
|
banned: PersonViewSafe[];
|
||||||
|
loading: boolean;
|
||||||
siteConfigLoading: boolean;
|
siteConfigLoading: boolean;
|
||||||
leaveAdminTeamLoading: boolean;
|
leaveAdminTeamLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
private isoData = setIsoData(
|
||||||
|
this.context,
|
||||||
|
GetSiteConfigResponse,
|
||||||
|
BannedPersonsResponse
|
||||||
|
);
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: AdminSettingsState = {
|
private emptyState: AdminSettingsState = {
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
siteConfigHjson: null,
|
siteConfigHjson: None,
|
||||||
siteConfigRes: {
|
siteConfigRes: None,
|
||||||
config_hjson: null,
|
|
||||||
},
|
|
||||||
banned: [],
|
banned: [],
|
||||||
loading: true,
|
loading: true,
|
||||||
siteConfigLoading: null,
|
siteConfigLoading: null,
|
||||||
|
@ -69,20 +73,26 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
|
|
||||||
// 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.siteConfigRes = this.isoData.routeData[0];
|
this.state.siteConfigRes = Some(
|
||||||
this.state.siteConfigHjson = this.state.siteConfigRes.config_hjson;
|
this.isoData.routeData[0] as GetSiteConfigResponse
|
||||||
this.state.banned = this.isoData.routeData[1].banned;
|
);
|
||||||
|
this.state.siteConfigHjson = this.state.siteConfigRes.map(
|
||||||
|
s => s.config_hjson
|
||||||
|
);
|
||||||
|
this.state.banned = (
|
||||||
|
this.isoData.routeData[1] as BannedPersonsResponse
|
||||||
|
).banned;
|
||||||
this.state.siteConfigLoading = false;
|
this.state.siteConfigLoading = false;
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
} else {
|
} else {
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.getSiteConfig({
|
wsClient.getSiteConfig({
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.getBannedPersons({
|
wsClient.getBannedPersons({
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -91,10 +101,10 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
let siteConfigForm: GetSiteConfig = { auth: req.auth };
|
let siteConfigForm = new GetSiteConfig({ auth: req.auth.unwrap() });
|
||||||
promises.push(req.client.getSiteConfig(siteConfigForm));
|
promises.push(req.client.getSiteConfig(siteConfigForm));
|
||||||
|
|
||||||
let bannedPersonsForm: GetBannedPersons = { auth: req.auth };
|
let bannedPersonsForm = new GetBannedPersons({ auth: req.auth.unwrap() });
|
||||||
promises.push(req.client.getBannedPersons(bannedPersonsForm));
|
promises.push(req.client.getBannedPersons(bannedPersonsForm));
|
||||||
|
|
||||||
return promises;
|
return promises;
|
||||||
|
@ -114,9 +124,10 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `${i18n.t("admin_settings")} - ${
|
return this.state.siteRes.site_view.match({
|
||||||
this.state.siteRes.site_view.site.name
|
some: siteView => `${i18n.t("admin_settings")} - ${siteView.site.name}`,
|
||||||
}`;
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -132,13 +143,18 @@ 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}
|
||||||
/>
|
/>
|
||||||
{this.state.siteRes.site_view.site.id && (
|
{this.state.siteRes.site_view.match({
|
||||||
<SiteForm
|
some: siteView => (
|
||||||
site={this.state.siteRes.site_view.site}
|
<SiteForm
|
||||||
showLocal={showLocal(this.isoData)}
|
site={Some(siteView.site)}
|
||||||
/>
|
showLocal={showLocal(this.isoData)}
|
||||||
)}
|
/>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
{this.admins()}
|
{this.admins()}
|
||||||
{this.bannedUsers()}
|
{this.bannedUsers()}
|
||||||
</div>
|
</div>
|
||||||
|
@ -210,7 +226,7 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<textarea
|
<textarea
|
||||||
id={this.siteConfigTextAreaId}
|
id={this.siteConfigTextAreaId}
|
||||||
value={this.state.siteConfigHjson}
|
value={toUndefined(this.state.siteConfigHjson)}
|
||||||
onInput={linkEvent(this, this.handleSiteConfigHjsonChange)}
|
onInput={linkEvent(this, this.handleSiteConfigHjsonChange)}
|
||||||
class="form-control text-monospace"
|
class="form-control text-monospace"
|
||||||
rows={3}
|
rows={3}
|
||||||
|
@ -236,10 +252,10 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
handleSiteConfigSubmit(i: AdminSettings, event: any) {
|
handleSiteConfigSubmit(i: AdminSettings, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.siteConfigLoading = true;
|
i.state.siteConfigLoading = true;
|
||||||
let form: SaveSiteConfig = {
|
let form = new SaveSiteConfig({
|
||||||
config_hjson: i.state.siteConfigHjson,
|
config_hjson: toUndefined(i.state.siteConfigHjson),
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.saveSiteConfig(form));
|
WebSocketService.Instance.send(wsClient.saveSiteConfig(form));
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
@ -251,7 +267,9 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
|
|
||||||
handleLeaveAdminTeam(i: AdminSettings) {
|
handleLeaveAdminTeam(i: AdminSettings) {
|
||||||
i.state.leaveAdminTeamLoading = true;
|
i.state.leaveAdminTeamLoading = true;
|
||||||
WebSocketService.Instance.send(wsClient.leaveAdmin({ auth: authField() }));
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.leaveAdmin({ auth: auth().unwrap() })
|
||||||
|
);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,24 +283,26 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.EditSite) {
|
} else if (op == UserOperation.EditSite) {
|
||||||
let data = wsJsonToRes<SiteResponse>(msg).data;
|
let data = wsJsonToRes<SiteResponse>(msg, SiteResponse);
|
||||||
this.state.siteRes.site_view = data.site_view;
|
this.state.siteRes.site_view = Some(data.site_view);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
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).data;
|
let data = wsJsonToRes<BannedPersonsResponse>(msg, BannedPersonsResponse);
|
||||||
this.state.banned = data.banned;
|
this.state.banned = data.banned;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.GetSiteConfig) {
|
} else if (op == UserOperation.GetSiteConfig) {
|
||||||
let data = wsJsonToRes<GetSiteConfigResponse>(msg).data;
|
let data = wsJsonToRes<GetSiteConfigResponse>(msg, GetSiteConfigResponse);
|
||||||
this.state.siteConfigRes = data;
|
this.state.siteConfigRes = Some(data);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
this.state.siteConfigHjson = this.state.siteConfigRes.config_hjson;
|
this.state.siteConfigHjson = this.state.siteConfigRes.map(
|
||||||
|
s => s.config_hjson
|
||||||
|
);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
var textarea: any = document.getElementById(this.siteConfigTextAreaId);
|
var textarea: any = document.getElementById(this.siteConfigTextAreaId);
|
||||||
autosize(textarea);
|
autosize(textarea);
|
||||||
} else if (op == UserOperation.LeaveAdmin) {
|
} else if (op == UserOperation.LeaveAdmin) {
|
||||||
let data = wsJsonToRes<GetSiteResponse>(msg).data;
|
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
||||||
this.state.siteRes.site_view = data.site_view;
|
this.state.siteRes.site_view = data.site_view;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
this.state.leaveAdminTeamLoading = false;
|
this.state.leaveAdminTeamLoading = false;
|
||||||
|
@ -290,9 +310,11 @@ export class AdminSettings extends Component<any, AdminSettingsState> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
this.context.router.history.push("/");
|
this.context.router.history.push("/");
|
||||||
} else if (op == UserOperation.SaveSiteConfig) {
|
} else if (op == UserOperation.SaveSiteConfig) {
|
||||||
let data = wsJsonToRes<GetSiteConfigResponse>(msg).data;
|
let data = wsJsonToRes<GetSiteConfigResponse>(msg, GetSiteConfigResponse);
|
||||||
this.state.siteConfigRes = data;
|
this.state.siteConfigRes = Some(data);
|
||||||
this.state.siteConfigHjson = this.state.siteConfigRes.config_hjson;
|
this.state.siteConfigHjson = this.state.siteConfigRes.map(
|
||||||
|
s => s.config_hjson
|
||||||
|
);
|
||||||
this.state.siteConfigLoading = false;
|
this.state.siteConfigLoading = false;
|
||||||
toast(i18n.t("site_saved"));
|
toast(i18n.t("site_saved"));
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
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";
|
||||||
|
@ -23,38 +24,41 @@ import {
|
||||||
SiteResponse,
|
SiteResponse,
|
||||||
SortType,
|
SortType,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { DataType, InitialFetchRequest } from "../../interfaces";
|
import { DataType, InitialFetchRequest } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
createCommentLikeRes,
|
createCommentLikeRes,
|
||||||
createPostLikeFindRes,
|
createPostLikeFindRes,
|
||||||
editCommentRes,
|
editCommentRes,
|
||||||
editPostFindRes,
|
editPostFindRes,
|
||||||
|
enableDownvotes,
|
||||||
|
enableNsfw,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
getDataTypeFromProps,
|
getDataTypeFromProps,
|
||||||
getListingTypeFromProps,
|
getListingTypeFromProps,
|
||||||
getPageFromProps,
|
getPageFromProps,
|
||||||
getSortTypeFromProps,
|
getSortTypeFromProps,
|
||||||
|
isBrowser,
|
||||||
notifyPost,
|
notifyPost,
|
||||||
relTags,
|
relTags,
|
||||||
restoreScrollPosition,
|
restoreScrollPosition,
|
||||||
saveCommentRes,
|
saveCommentRes,
|
||||||
saveScrollPosition,
|
saveScrollPosition,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
setOptionalAuth,
|
|
||||||
setupTippy,
|
setupTippy,
|
||||||
showLocal,
|
showLocal,
|
||||||
toast,
|
toast,
|
||||||
|
trendingFetchLimit,
|
||||||
updatePersonBlock,
|
updatePersonBlock,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { CommentNodes } from "../comment/comment-nodes";
|
import { CommentNodes } from "../comment/comment-nodes";
|
||||||
import { DataTypeSelect } from "../common/data-type-select";
|
import { DataTypeSelect } from "../common/data-type-select";
|
||||||
|
@ -70,17 +74,17 @@ import { SiteSidebar } from "./site-sidebar";
|
||||||
interface HomeState {
|
interface HomeState {
|
||||||
trendingCommunities: CommunityView[];
|
trendingCommunities: CommunityView[];
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
showSubscribedMobile: boolean;
|
|
||||||
showTrendingMobile: boolean;
|
|
||||||
showSidebarMobile: boolean;
|
|
||||||
subscribedCollapsed: boolean;
|
|
||||||
loading: boolean;
|
|
||||||
posts: PostView[];
|
posts: PostView[];
|
||||||
comments: CommentView[];
|
comments: CommentView[];
|
||||||
listingType: ListingType;
|
listingType: ListingType;
|
||||||
dataType: DataType;
|
dataType: DataType;
|
||||||
sort: SortType;
|
sort: SortType;
|
||||||
page: number;
|
page: number;
|
||||||
|
showSubscribedMobile: boolean;
|
||||||
|
showTrendingMobile: boolean;
|
||||||
|
showSidebarMobile: boolean;
|
||||||
|
subscribedCollapsed: boolean;
|
||||||
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HomeProps {
|
interface HomeProps {
|
||||||
|
@ -98,7 +102,12 @@ interface UrlParams {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Home extends Component<any, HomeState> {
|
export class Home extends Component<any, HomeState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(
|
||||||
|
this.context,
|
||||||
|
GetPostsResponse,
|
||||||
|
GetCommentsResponse,
|
||||||
|
ListCommunitiesResponse
|
||||||
|
);
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: HomeState = {
|
private emptyState: HomeState = {
|
||||||
trendingCommunities: [],
|
trendingCommunities: [],
|
||||||
|
@ -113,7 +122,7 @@ export class Home extends Component<any, HomeState> {
|
||||||
listingType: getListingTypeFromProps(
|
listingType: getListingTypeFromProps(
|
||||||
this.props,
|
this.props,
|
||||||
ListingType[
|
ListingType[
|
||||||
this.isoData.site_res.site_view?.site.default_post_listing_type
|
this.isoData.site_res.site_view.unwrap().site.default_post_listing_type
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
dataType: getDataTypeFromProps(this.props),
|
dataType: getDataTypeFromProps(this.props),
|
||||||
|
@ -135,12 +144,25 @@ 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) {
|
||||||
if (this.state.dataType == DataType.Post) {
|
let postsRes = Some(this.isoData.routeData[0] as GetPostsResponse);
|
||||||
this.state.posts = this.isoData.routeData[0].posts;
|
let commentsRes = Some(this.isoData.routeData[1] as GetCommentsResponse);
|
||||||
} else {
|
let trendingRes = this.isoData.routeData[2] as ListCommunitiesResponse;
|
||||||
this.state.comments = this.isoData.routeData[0].comments;
|
|
||||||
|
postsRes.match({
|
||||||
|
some: pvs => (this.state.posts = pvs.posts),
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
|
commentsRes.match({
|
||||||
|
some: cvs => (this.state.comments = cvs.comments),
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
|
this.state.trendingCommunities = trendingRes.communities;
|
||||||
|
|
||||||
|
if (isBrowser()) {
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.communityJoin({ community_id: 0 })
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.state.trendingCommunities = this.isoData.routeData[1].communities;
|
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
} else {
|
} else {
|
||||||
this.fetchTrendingCommunities();
|
this.fetchTrendingCommunities();
|
||||||
|
@ -149,12 +171,13 @@ export class Home extends Component<any, HomeState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchTrendingCommunities() {
|
fetchTrendingCommunities() {
|
||||||
let listCommunitiesForm: ListCommunities = {
|
let listCommunitiesForm = new ListCommunities({
|
||||||
type_: ListingType.Local,
|
type_: Some(ListingType.Local),
|
||||||
sort: SortType.Hot,
|
sort: Some(SortType.Hot),
|
||||||
limit: 6,
|
limit: Some(trendingFetchLimit),
|
||||||
auth: authField(false),
|
page: None,
|
||||||
};
|
auth: auth(false).ok(),
|
||||||
|
});
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.listCommunities(listCommunitiesForm)
|
wsClient.listCommunities(listCommunitiesForm)
|
||||||
);
|
);
|
||||||
|
@ -165,8 +188,6 @@ export class Home extends Component<any, HomeState> {
|
||||||
if (!this.state.siteRes.site_view) {
|
if (!this.state.siteRes.site_view) {
|
||||||
this.context.router.history.push("/setup");
|
this.context.router.history.push("/setup");
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketService.Instance.send(wsClient.communityJoin({ community_id: 0 }));
|
|
||||||
setupTippy();
|
setupTippy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,58 +213,69 @@ export class Home extends Component<any, HomeState> {
|
||||||
: DataType.Post;
|
: DataType.Post;
|
||||||
|
|
||||||
// TODO figure out auth default_listingType, default_sort_type
|
// TODO figure out auth default_listingType, default_sort_type
|
||||||
let type_: ListingType = pathSplit[5]
|
let type_: Option<ListingType> = Some(
|
||||||
? ListingType[pathSplit[5]]
|
pathSplit[5]
|
||||||
: UserService.Instance.myUserInfo
|
? ListingType[pathSplit[5]]
|
||||||
? Object.values(ListingType)[
|
: UserService.Instance.myUserInfo.match({
|
||||||
UserService.Instance.myUserInfo.local_user_view.local_user
|
some: mui =>
|
||||||
.default_listing_type
|
Object.values(ListingType)[
|
||||||
]
|
mui.local_user_view.local_user.default_listing_type
|
||||||
: null;
|
],
|
||||||
let sort: SortType = pathSplit[7]
|
none: ListingType.Local,
|
||||||
? SortType[pathSplit[7]]
|
})
|
||||||
: UserService.Instance.myUserInfo
|
);
|
||||||
? Object.values(SortType)[
|
let sort: Option<SortType> = Some(
|
||||||
UserService.Instance.myUserInfo.local_user_view.local_user
|
pathSplit[7]
|
||||||
.default_sort_type
|
? SortType[pathSplit[7]]
|
||||||
]
|
: UserService.Instance.myUserInfo.match({
|
||||||
: SortType.Active;
|
some: mui =>
|
||||||
|
Object.values(SortType)[
|
||||||
|
mui.local_user_view.local_user.default_sort_type
|
||||||
|
],
|
||||||
|
none: SortType.Active,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
let page = pathSplit[9] ? Number(pathSplit[9]) : 1;
|
let page = Some(pathSplit[9] ? Number(pathSplit[9]) : 1);
|
||||||
|
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
if (dataType == DataType.Post) {
|
if (dataType == DataType.Post) {
|
||||||
let getPostsForm: GetPosts = {
|
let getPostsForm = new GetPosts({
|
||||||
|
community_id: None,
|
||||||
|
community_name: None,
|
||||||
|
type_,
|
||||||
page,
|
page,
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
sort,
|
sort,
|
||||||
saved_only: false,
|
saved_only: Some(false),
|
||||||
};
|
auth: req.auth,
|
||||||
if (type_) {
|
});
|
||||||
getPostsForm.type_ = type_;
|
|
||||||
}
|
|
||||||
|
|
||||||
setOptionalAuth(getPostsForm, req.auth);
|
|
||||||
promises.push(req.client.getPosts(getPostsForm));
|
promises.push(req.client.getPosts(getPostsForm));
|
||||||
|
promises.push(Promise.resolve());
|
||||||
} else {
|
} else {
|
||||||
let getCommentsForm: GetComments = {
|
let getCommentsForm = new GetComments({
|
||||||
|
community_id: None,
|
||||||
|
community_name: None,
|
||||||
page,
|
page,
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
sort,
|
sort,
|
||||||
type_: type_ || ListingType.Local,
|
type_,
|
||||||
saved_only: false,
|
saved_only: Some(false),
|
||||||
};
|
auth: req.auth,
|
||||||
setOptionalAuth(getCommentsForm, req.auth);
|
});
|
||||||
|
promises.push(Promise.resolve());
|
||||||
promises.push(req.client.getComments(getCommentsForm));
|
promises.push(req.client.getComments(getCommentsForm));
|
||||||
}
|
}
|
||||||
|
|
||||||
let trendingCommunitiesForm: ListCommunities = {
|
let trendingCommunitiesForm = new ListCommunities({
|
||||||
type_: ListingType.Local,
|
type_: Some(ListingType.Local),
|
||||||
sort: SortType.Hot,
|
sort: Some(SortType.Hot),
|
||||||
limit: 6,
|
limit: Some(trendingFetchLimit),
|
||||||
};
|
page: None,
|
||||||
setOptionalAuth(trendingCommunitiesForm, req.auth);
|
auth: req.auth,
|
||||||
|
});
|
||||||
promises.push(req.client.listCommunities(trendingCommunitiesForm));
|
promises.push(req.client.listCommunities(trendingCommunitiesForm));
|
||||||
|
|
||||||
return promises;
|
return promises;
|
||||||
|
@ -262,13 +294,14 @@ export class Home extends Component<any, HomeState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `${
|
return this.state.siteRes.site_view.match({
|
||||||
this.state.siteRes.site_view
|
some: siteView =>
|
||||||
? this.state.siteRes.site_view.site.description
|
siteView.site.description.match({
|
||||||
? `${this.state.siteRes.site_view.site.name} - ${this.state.siteRes.site_view.site.description}`
|
some: desc => `${siteView.site.name} - ${desc}`,
|
||||||
: this.state.siteRes.site_view.site.name
|
none: siteView.site.name,
|
||||||
: "Lemmy"
|
}),
|
||||||
}`;
|
none: "Lemmy",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -277,8 +310,10 @@ export class Home extends Component<any, HomeState> {
|
||||||
<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?.site && (
|
{this.state.siteRes.site_view.isSome() && (
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<main role="main" class="col-12 col-md-8">
|
<main role="main" class="col-12 col-md-8">
|
||||||
<div class="d-block d-md-none">{this.mobileView()}</div>
|
<div class="d-block d-md-none">{this.mobileView()}</div>
|
||||||
|
@ -291,28 +326,34 @@ export class Home extends Component<any, HomeState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get hasFollows(): boolean {
|
||||||
|
return UserService.Instance.myUserInfo.match({
|
||||||
|
some: mui => mui.follows.length > 0,
|
||||||
|
none: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
mobileView() {
|
mobileView() {
|
||||||
let siteRes = this.state.siteRes;
|
let siteRes = this.state.siteRes;
|
||||||
return (
|
return (
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
{UserService.Instance.myUserInfo &&
|
{this.hasFollows && (
|
||||||
UserService.Instance.myUserInfo.follows.length > 0 && (
|
<button
|
||||||
<button
|
class="btn btn-secondary d-inline-block mb-2 mr-3"
|
||||||
class="btn btn-secondary d-inline-block mb-2 mr-3"
|
onClick={linkEvent(this, this.handleShowSubscribedMobile)}
|
||||||
onClick={linkEvent(this, this.handleShowSubscribedMobile)}
|
>
|
||||||
>
|
{i18n.t("subscribed")}{" "}
|
||||||
{i18n.t("subscribed")}{" "}
|
<Icon
|
||||||
<Icon
|
icon={
|
||||||
icon={
|
this.state.showSubscribedMobile
|
||||||
this.state.showSubscribedMobile
|
? `minus-square`
|
||||||
? `minus-square`
|
: `plus-square`
|
||||||
: `plus-square`
|
}
|
||||||
}
|
classes="icon-inline"
|
||||||
classes="icon-inline"
|
/>
|
||||||
/>
|
</button>
|
||||||
</button>
|
)}
|
||||||
)}
|
|
||||||
<button
|
<button
|
||||||
class="btn btn-secondary d-inline-block mb-2 mr-3"
|
class="btn btn-secondary d-inline-block mb-2 mr-3"
|
||||||
onClick={linkEvent(this, this.handleShowTrendingMobile)}
|
onClick={linkEvent(this, this.handleShowTrendingMobile)}
|
||||||
|
@ -337,15 +378,19 @@ export class Home extends Component<any, HomeState> {
|
||||||
classes="icon-inline"
|
classes="icon-inline"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
{this.state.showSidebarMobile && (
|
{this.state.showSidebarMobile &&
|
||||||
<SiteSidebar
|
siteRes.site_view.match({
|
||||||
site={siteRes.site_view.site}
|
some: siteView => (
|
||||||
admins={siteRes.admins}
|
<SiteSidebar
|
||||||
counts={siteRes.site_view.counts}
|
site={siteView.site}
|
||||||
online={siteRes.online}
|
admins={Some(siteRes.admins)}
|
||||||
showLocal={showLocal(this.isoData)}
|
counts={Some(siteView.counts)}
|
||||||
/>
|
online={Some(siteRes.online)}
|
||||||
)}
|
showLocal={showLocal(this.isoData)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
{this.state.showTrendingMobile && (
|
{this.state.showTrendingMobile && (
|
||||||
<div class="col-12 card border-secondary mb-3">
|
<div class="col-12 card border-secondary mb-3">
|
||||||
<div class="card-body">{this.trendingCommunities()}</div>
|
<div class="card-body">{this.trendingCommunities()}</div>
|
||||||
|
@ -374,21 +419,23 @@ export class Home extends Component<any, HomeState> {
|
||||||
{this.exploreCommunitiesButton()}
|
{this.exploreCommunitiesButton()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{siteRes.site_view.match({
|
||||||
<SiteSidebar
|
some: siteView => (
|
||||||
site={siteRes.site_view.site}
|
<SiteSidebar
|
||||||
admins={siteRes.admins}
|
site={siteView.site}
|
||||||
counts={siteRes.site_view.counts}
|
admins={Some(siteRes.admins)}
|
||||||
online={siteRes.online}
|
counts={Some(siteView.counts)}
|
||||||
showLocal={showLocal(this.isoData)}
|
online={Some(siteRes.online)}
|
||||||
/>
|
showLocal={showLocal(this.isoData)}
|
||||||
|
/>
|
||||||
{UserService.Instance.myUserInfo &&
|
),
|
||||||
UserService.Instance.myUserInfo.follows.length > 0 && (
|
none: <></>,
|
||||||
<div class="card border-secondary mb-3">
|
})}
|
||||||
<div class="card-body">{this.subscribedCommunities()}</div>
|
{this.hasFollows && (
|
||||||
</div>
|
<div class="card border-secondary mb-3">
|
||||||
)}
|
<div class="card-body">{this.subscribedCommunities()}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -458,11 +505,14 @@ export class Home extends Component<any, HomeState> {
|
||||||
</h5>
|
</h5>
|
||||||
{!this.state.subscribedCollapsed && (
|
{!this.state.subscribedCollapsed && (
|
||||||
<ul class="list-inline mb-0">
|
<ul class="list-inline mb-0">
|
||||||
{UserService.Instance.myUserInfo.follows.map(cfv => (
|
{UserService.Instance.myUserInfo
|
||||||
<li class="list-inline-item d-inline-block">
|
.map(m => m.follows)
|
||||||
<CommunityLink community={cfv.community} />
|
.unwrapOr([])
|
||||||
</li>
|
.map(cfv => (
|
||||||
))}
|
<li class="list-inline-item d-inline-block">
|
||||||
|
<CommunityLink community={cfv.community} />
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -501,22 +551,24 @@ export class Home extends Component<any, HomeState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
listings() {
|
listings() {
|
||||||
let site = this.state.siteRes.site_view.site;
|
|
||||||
return this.state.dataType == DataType.Post ? (
|
return this.state.dataType == DataType.Post ? (
|
||||||
<PostListings
|
<PostListings
|
||||||
posts={this.state.posts}
|
posts={this.state.posts}
|
||||||
showCommunity
|
showCommunity
|
||||||
removeDuplicates
|
removeDuplicates
|
||||||
enableDownvotes={site.enable_downvotes}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
enableNsfw={site.enable_nsfw}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<CommentNodes
|
<CommentNodes
|
||||||
nodes={commentsToFlatNodes(this.state.comments)}
|
nodes={commentsToFlatNodes(this.state.comments)}
|
||||||
|
moderators={None}
|
||||||
|
admins={None}
|
||||||
|
maxCommentsShown={None}
|
||||||
noIndent
|
noIndent
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={site.enable_downvotes}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -524,9 +576,9 @@ 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 = UserService.Instance.myUserInfo
|
let frontRss = auth(false)
|
||||||
? `/feeds/front/${UserService.Instance.auth}.xml?sort=${this.state.sort}`
|
.ok()
|
||||||
: "";
|
.map(auth => `/feeds/front/${auth}.xml?sort=${this.state.sort}`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
|
@ -563,19 +615,18 @@ 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} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{UserService.Instance.myUserInfo &&
|
{this.state.listingType == ListingType.Subscribed &&
|
||||||
this.state.listingType == ListingType.Subscribed && (
|
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
|
</a>
|
||||||
rel="alternate"
|
<link rel="alternate" type="application/atom+xml" href={rss} />
|
||||||
type="application/atom+xml"
|
</>
|
||||||
href={frontRss}
|
),
|
||||||
/>
|
none: <></>,
|
||||||
</>
|
})}
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -622,27 +673,29 @@ export class Home extends Component<any, HomeState> {
|
||||||
|
|
||||||
fetchData() {
|
fetchData() {
|
||||||
if (this.state.dataType == DataType.Post) {
|
if (this.state.dataType == DataType.Post) {
|
||||||
let getPostsForm: GetPosts = {
|
let getPostsForm = new GetPosts({
|
||||||
page: this.state.page,
|
community_id: None,
|
||||||
limit: fetchLimit,
|
community_name: None,
|
||||||
sort: this.state.sort,
|
page: Some(this.state.page),
|
||||||
saved_only: false,
|
limit: Some(fetchLimit),
|
||||||
auth: authField(false),
|
sort: Some(this.state.sort),
|
||||||
};
|
saved_only: Some(false),
|
||||||
if (this.state.listingType) {
|
auth: auth(false).ok(),
|
||||||
getPostsForm.type_ = this.state.listingType;
|
type_: Some(this.state.listingType),
|
||||||
}
|
});
|
||||||
|
|
||||||
WebSocketService.Instance.send(wsClient.getPosts(getPostsForm));
|
WebSocketService.Instance.send(wsClient.getPosts(getPostsForm));
|
||||||
} else {
|
} else {
|
||||||
let getCommentsForm: GetComments = {
|
let getCommentsForm = new GetComments({
|
||||||
page: this.state.page,
|
community_id: None,
|
||||||
limit: fetchLimit,
|
community_name: None,
|
||||||
sort: this.state.sort,
|
page: Some(this.state.page),
|
||||||
type_: this.state.listingType,
|
limit: Some(fetchLimit),
|
||||||
saved_only: false,
|
sort: Some(this.state.sort),
|
||||||
auth: authField(false),
|
saved_only: Some(false),
|
||||||
};
|
auth: auth(false).ok(),
|
||||||
|
type_: Some(this.state.listingType),
|
||||||
|
});
|
||||||
WebSocketService.Instance.send(wsClient.getComments(getCommentsForm));
|
WebSocketService.Instance.send(wsClient.getComments(getCommentsForm));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -659,46 +712,55 @@ 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>(msg).data;
|
let data = wsJsonToRes<ListCommunitiesResponse>(
|
||||||
|
msg,
|
||||||
|
ListCommunitiesResponse
|
||||||
|
);
|
||||||
this.state.trendingCommunities = data.communities;
|
this.state.trendingCommunities = data.communities;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.EditSite) {
|
} else if (op == UserOperation.EditSite) {
|
||||||
let data = wsJsonToRes<SiteResponse>(msg).data;
|
let data = wsJsonToRes<SiteResponse>(msg, SiteResponse);
|
||||||
this.state.siteRes.site_view = data.site_view;
|
this.state.siteRes.site_view = Some(data.site_view);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
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).data;
|
let data = wsJsonToRes<GetPostsResponse>(msg, GetPostsResponse);
|
||||||
this.state.posts = data.posts;
|
this.state.posts = data.posts;
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.communityJoin({ community_id: 0 })
|
||||||
|
);
|
||||||
restoreScrollPosition(this.context);
|
restoreScrollPosition(this.context);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.CreatePost) {
|
} else if (op == UserOperation.CreatePost) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg).data;
|
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
||||||
|
|
||||||
// NSFW check
|
// NSFW check
|
||||||
let nsfw = data.post_view.post.nsfw || data.post_view.community.nsfw;
|
let nsfw = data.post_view.post.nsfw || data.post_view.community.nsfw;
|
||||||
let nsfwCheck =
|
let nsfwCheck =
|
||||||
!nsfw ||
|
!nsfw ||
|
||||||
(nsfw &&
|
(nsfw &&
|
||||||
UserService.Instance.myUserInfo &&
|
UserService.Instance.myUserInfo
|
||||||
UserService.Instance.myUserInfo.local_user_view.local_user.show_nsfw);
|
.map(m => m.local_user_view.local_user.show_nsfw)
|
||||||
|
.unwrapOr(false));
|
||||||
|
|
||||||
|
let showPostNotifs = UserService.Instance.myUserInfo
|
||||||
|
.map(m => m.local_user_view.local_user.show_new_post_notifs)
|
||||||
|
.unwrapOr(false);
|
||||||
|
|
||||||
// Only push these if you're on the first page, and you pass the nsfw check
|
// Only push these if you're on the first page, and you pass the nsfw check
|
||||||
if (this.state.page == 1 && nsfwCheck) {
|
if (this.state.page == 1 && nsfwCheck) {
|
||||||
// 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.follows
|
UserService.Instance.myUserInfo
|
||||||
|
.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)
|
||||||
) {
|
) {
|
||||||
this.state.posts.unshift(data.post_view);
|
this.state.posts.unshift(data.post_view);
|
||||||
if (
|
if (showPostNotifs) {
|
||||||
UserService.Instance.myUserInfo?.local_user_view.local_user
|
|
||||||
.show_new_post_notifs
|
|
||||||
) {
|
|
||||||
notifyPost(data.post_view, this.context.router);
|
notifyPost(data.post_view, this.context.router);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -706,19 +768,13 @@ export class Home extends Component<any, HomeState> {
|
||||||
// If you're on the local view, only push it if its local
|
// If you're on the local view, only push it if its local
|
||||||
if (data.post_view.post.local) {
|
if (data.post_view.post.local) {
|
||||||
this.state.posts.unshift(data.post_view);
|
this.state.posts.unshift(data.post_view);
|
||||||
if (
|
if (showPostNotifs) {
|
||||||
UserService.Instance.myUserInfo?.local_user_view.local_user
|
|
||||||
.show_new_post_notifs
|
|
||||||
) {
|
|
||||||
notifyPost(data.post_view, this.context.router);
|
notifyPost(data.post_view, this.context.router);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.state.posts.unshift(data.post_view);
|
this.state.posts.unshift(data.post_view);
|
||||||
if (
|
if (showPostNotifs) {
|
||||||
UserService.Instance.myUserInfo?.local_user_view.local_user
|
|
||||||
.show_new_post_notifs
|
|
||||||
) {
|
|
||||||
notifyPost(data.post_view, this.context.router);
|
notifyPost(data.post_view, this.context.router);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -732,26 +788,26 @@ export class Home extends Component<any, HomeState> {
|
||||||
op == UserOperation.StickyPost ||
|
op == UserOperation.StickyPost ||
|
||||||
op == UserOperation.SavePost
|
op == UserOperation.SavePost
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg).data;
|
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
|
||||||
this.state.siteRes.admins = data.admins;
|
this.state.siteRes.admins = data.admins;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.BanPerson) {
|
} else if (op == UserOperation.BanPerson) {
|
||||||
let data = wsJsonToRes<BanPersonResponse>(msg).data;
|
let data = wsJsonToRes<BanPersonResponse>(msg, BanPersonResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<GetCommentsResponse>(msg, GetCommentsResponse);
|
||||||
this.state.comments = data.comments;
|
this.state.comments = data.comments;
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
@ -760,18 +816,20 @@ export class Home extends Component<any, HomeState> {
|
||||||
op == UserOperation.DeleteComment ||
|
op == UserOperation.DeleteComment ||
|
||||||
op == UserOperation.RemoveComment
|
op == UserOperation.RemoveComment
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
|
|
||||||
// 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.follows
|
UserService.Instance.myUserInfo
|
||||||
|
.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)
|
||||||
) {
|
) {
|
||||||
|
@ -783,23 +841,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).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
|
||||||
updatePersonBlock(data);
|
updatePersonBlock(data);
|
||||||
} else if (op == UserOperation.CreatePostReport) {
|
} else if (op == UserOperation.CreatePostReport) {
|
||||||
let data = wsJsonToRes<PostReportResponse>(msg).data;
|
let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
toast(i18n.t("report_created"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
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";
|
||||||
|
@ -20,42 +21,56 @@ export class Instances extends Component<any, InstancesState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `${i18n.t("instances")} - ${this.state.siteRes.site_view.site.name}`;
|
return this.state.siteRes.site_view.match({
|
||||||
|
some: siteView => `${i18n.t("instances")} - ${siteView.site.name}`,
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let federated_instances = this.state.siteRes?.federated_instances;
|
return this.state.siteRes.federated_instances.match({
|
||||||
return (
|
some: federated_instances => (
|
||||||
federated_instances && (
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<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 class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<h5>{i18n.t("linked_instances")}</h5>
|
<h5>{i18n.t("linked_instances")}</h5>
|
||||||
{this.itemList(federated_instances.linked)}
|
{this.itemList(federated_instances.linked)}
|
||||||
</div>
|
</div>
|
||||||
{federated_instances.allowed?.length > 0 && (
|
{federated_instances.allowed.match({
|
||||||
<div class="col-md-6">
|
some: allowed =>
|
||||||
<h5>{i18n.t("allowed_instances")}</h5>
|
allowed.length > 0 && (
|
||||||
{this.itemList(federated_instances.allowed)}
|
<div class="col-md-6">
|
||||||
</div>
|
<h5>{i18n.t("allowed_instances")}</h5>
|
||||||
)}
|
{this.itemList(allowed)}
|
||||||
{federated_instances.blocked?.length > 0 && (
|
</div>
|
||||||
<div class="col-md-6">
|
),
|
||||||
<h5>{i18n.t("blocked_instances")}</h5>
|
none: <></>,
|
||||||
{this.itemList(federated_instances.blocked)}
|
})}
|
||||||
</div>
|
{federated_instances.blocked.match({
|
||||||
)}
|
some: blocked =>
|
||||||
|
blocked.length > 0 && (
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h5>{i18n.t("blocked_instances")}</h5>
|
||||||
|
{this.itemList(blocked)}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
),
|
||||||
);
|
none: <></>,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
itemList(items: string[]) {
|
itemList(items: string[]) {
|
||||||
|
let noneFound = <div>{i18n.t("none_found")}</div>;
|
||||||
return items.length > 0 ? (
|
return items.length > 0 ? (
|
||||||
<ul>
|
<ul>
|
||||||
{items.map(i => (
|
{items.map(i => (
|
||||||
|
@ -67,7 +82,7 @@ export class Instances extends Component<any, InstancesState> {
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
) : (
|
) : (
|
||||||
<div>{i18n.t("none_found")}</div>
|
noneFound
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
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";
|
||||||
|
@ -29,13 +30,22 @@ export class Legal extends Component<any, LegalState> {
|
||||||
<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
|
{this.state.siteRes.site_view.match({
|
||||||
className="md-div"
|
some: siteView =>
|
||||||
dangerouslySetInnerHTML={mdToHtml(
|
siteView.site.legal_information.match({
|
||||||
this.state.siteRes.site_view.site.legal_information
|
some: legal => (
|
||||||
)}
|
<div
|
||||||
/>
|
className="md-div"
|
||||||
|
dangerouslySetInnerHTML={mdToHtml(legal)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
}),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,25 @@
|
||||||
|
import { None } from "@sniptt/monads";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
Login as LoginForm,
|
Login as LoginForm,
|
||||||
LoginResponse,
|
LoginResponse,
|
||||||
PasswordReset,
|
PasswordReset,
|
||||||
SiteView,
|
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
toast,
|
toast,
|
||||||
validEmail,
|
validEmail,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
|
@ -27,7 +27,7 @@ import { Spinner } from "../common/icon";
|
||||||
interface State {
|
interface State {
|
||||||
loginForm: LoginForm;
|
loginForm: LoginForm;
|
||||||
loginLoading: boolean;
|
loginLoading: boolean;
|
||||||
site_view: SiteView;
|
siteRes: GetSiteResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Login extends Component<any, State> {
|
export class Login extends Component<any, State> {
|
||||||
|
@ -35,12 +35,12 @@ export class Login extends Component<any, State> {
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
|
|
||||||
emptyState: State = {
|
emptyState: State = {
|
||||||
loginForm: {
|
loginForm: new LoginForm({
|
||||||
username_or_email: undefined,
|
username_or_email: undefined,
|
||||||
password: undefined,
|
password: undefined,
|
||||||
},
|
}),
|
||||||
loginLoading: false,
|
loginLoading: false,
|
||||||
site_view: this.isoData.site_res.site_view,
|
siteRes: this.isoData.site_res,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -58,7 +58,7 @@ 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) {
|
if (UserService.Instance.myUserInfo.isSome()) {
|
||||||
this.context.router.history.push("/");
|
this.context.router.history.push("/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,10 @@ export class Login extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `${i18n.t("login")} - ${this.state.site_view.site.name}`;
|
return this.state.siteRes.site_view.match({
|
||||||
|
some: siteView => `${i18n.t("login")} - ${siteView.site.name}`,
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get isLemmyMl(): boolean {
|
get isLemmyMl(): boolean {
|
||||||
|
@ -83,6 +86,8 @@ 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 class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-lg-6 offset-lg-3">{this.loginForm()}</div>
|
<div class="col-12 col-lg-6 offset-lg-3">{this.loginForm()}</div>
|
||||||
|
@ -173,9 +178,9 @@ export class Login extends Component<any, State> {
|
||||||
|
|
||||||
handlePasswordReset(i: Login, event: any) {
|
handlePasswordReset(i: Login, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
let resetForm: PasswordReset = {
|
let resetForm = new PasswordReset({
|
||||||
email: i.state.loginForm.username_or_email,
|
email: i.state.loginForm.username_or_email,
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.passwordReset(resetForm));
|
WebSocketService.Instance.send(wsClient.passwordReset(resetForm));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,13 +194,13 @@ export class Login extends Component<any, State> {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (op == UserOperation.Login) {
|
if (op == UserOperation.Login) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg).data;
|
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.userJoin({
|
wsClient.userJoin({
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
toast(i18n.t("logged_in"));
|
toast(i18n.t("logged_in"));
|
||||||
|
@ -203,8 +208,8 @@ export class Login extends Component<any, State> {
|
||||||
} 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).data;
|
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
||||||
this.state.site_view = data.site_view;
|
this.state.siteRes = data;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
|
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 { LoginResponse, Register, UserOperation } from "lemmy-js-client";
|
import {
|
||||||
|
LoginResponse,
|
||||||
|
Register,
|
||||||
|
toUndefined,
|
||||||
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { delay, retryWhen, take } from "rxjs/operators";
|
import { delay, retryWhen, take } from "rxjs/operators";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import { toast, wsClient, wsJsonToRes, wsUserOp } from "../../utils";
|
import { toast, wsClient } from "../../utils";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
import { SiteForm } from "./site-form";
|
import { SiteForm } from "./site-form";
|
||||||
|
|
||||||
|
@ -19,15 +27,18 @@ export class Setup extends Component<any, State> {
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
|
|
||||||
private emptyState: State = {
|
private emptyState: State = {
|
||||||
userForm: {
|
userForm: new Register({
|
||||||
username: undefined,
|
username: undefined,
|
||||||
password: undefined,
|
password: undefined,
|
||||||
password_verify: undefined,
|
password_verify: undefined,
|
||||||
show_nsfw: true,
|
show_nsfw: true,
|
||||||
// The first admin signup doesn't need a captcha
|
// The first admin signup doesn't need a captcha
|
||||||
captcha_uuid: "",
|
captcha_uuid: None,
|
||||||
captcha_answer: "",
|
captcha_answer: None,
|
||||||
},
|
email: None,
|
||||||
|
honeypot: None,
|
||||||
|
answer: None,
|
||||||
|
}),
|
||||||
doneRegisteringUser: false,
|
doneRegisteringUser: false,
|
||||||
userLoading: false,
|
userLoading: false,
|
||||||
};
|
};
|
||||||
|
@ -64,7 +75,7 @@ export class Setup extends Component<any, State> {
|
||||||
{!this.state.doneRegisteringUser ? (
|
{!this.state.doneRegisteringUser ? (
|
||||||
this.registerUser()
|
this.registerUser()
|
||||||
) : (
|
) : (
|
||||||
<SiteForm showLocal />
|
<SiteForm site={None} showLocal />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -104,7 +115,7 @@ export class Setup extends Component<any, State> {
|
||||||
id="email"
|
id="email"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
placeholder={i18n.t("optional")}
|
placeholder={i18n.t("optional")}
|
||||||
value={this.state.userForm.email}
|
value={toUndefined(this.state.userForm.email)}
|
||||||
onInput={linkEvent(this, this.handleRegisterEmailChange)}
|
onInput={linkEvent(this, this.handleRegisterEmailChange)}
|
||||||
minLength={3}
|
minLength={3}
|
||||||
/>
|
/>
|
||||||
|
@ -171,7 +182,7 @@ export class Setup extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegisterEmailChange(i: Setup, event: any) {
|
handleRegisterEmailChange(i: Setup, event: any) {
|
||||||
i.state.userForm.email = event.target.value;
|
i.state.userForm.email = Some(event.target.value);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +204,7 @@ export class Setup extends Component<any, State> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.Register) {
|
} else if (op == UserOperation.Register) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg).data;
|
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
||||||
this.state.userLoading = false;
|
this.state.userLoading = false;
|
||||||
this.state.doneRegisteringUser = true;
|
this.state.doneRegisteringUser = true;
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
|
|
|
@ -1,20 +1,25 @@
|
||||||
|
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";
|
||||||
import { T } from "inferno-i18next-dess";
|
import { T } from "inferno-i18next-dess";
|
||||||
import {
|
import {
|
||||||
|
CaptchaResponse,
|
||||||
GetCaptchaResponse,
|
GetCaptchaResponse,
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
LoginResponse,
|
LoginResponse,
|
||||||
Register,
|
Register,
|
||||||
SiteView,
|
SiteView,
|
||||||
|
toUndefined,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
joinLemmyUrl,
|
joinLemmyUrl,
|
||||||
mdToHtml,
|
mdToHtml,
|
||||||
|
@ -22,9 +27,7 @@ import {
|
||||||
toast,
|
toast,
|
||||||
validEmail,
|
validEmail,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
import { Icon, Spinner } from "../common/icon";
|
import { Icon, Spinner } from "../common/icon";
|
||||||
|
@ -60,9 +63,9 @@ const passwordStrengthOptions: Options<string> = [
|
||||||
interface State {
|
interface State {
|
||||||
registerForm: Register;
|
registerForm: Register;
|
||||||
registerLoading: boolean;
|
registerLoading: boolean;
|
||||||
captcha: GetCaptchaResponse;
|
captcha: Option<GetCaptchaResponse>;
|
||||||
captchaPlaying: boolean;
|
captchaPlaying: boolean;
|
||||||
site_view: SiteView;
|
siteRes: GetSiteResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Signup extends Component<any, State> {
|
export class Signup extends Component<any, State> {
|
||||||
|
@ -71,20 +74,21 @@ export class Signup extends Component<any, State> {
|
||||||
private audio: HTMLAudioElement;
|
private audio: HTMLAudioElement;
|
||||||
|
|
||||||
emptyState: State = {
|
emptyState: State = {
|
||||||
registerForm: {
|
registerForm: new Register({
|
||||||
username: undefined,
|
username: undefined,
|
||||||
password: undefined,
|
password: undefined,
|
||||||
password_verify: undefined,
|
password_verify: undefined,
|
||||||
show_nsfw: false,
|
show_nsfw: false,
|
||||||
captcha_uuid: undefined,
|
captcha_uuid: None,
|
||||||
captcha_answer: undefined,
|
captcha_answer: None,
|
||||||
honeypot: undefined,
|
honeypot: None,
|
||||||
answer: undefined,
|
answer: None,
|
||||||
},
|
email: None,
|
||||||
|
}),
|
||||||
registerLoading: false,
|
registerLoading: false,
|
||||||
captcha: undefined,
|
captcha: None,
|
||||||
captchaPlaying: false,
|
captchaPlaying: false,
|
||||||
site_view: this.isoData.site_res.site_view,
|
siteRes: this.isoData.site_res,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -108,13 +112,14 @@ export class Signup extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `${this.titleName} - ${this.state.site_view.site.name}`;
|
return this.state.siteRes.site_view.match({
|
||||||
|
some: siteView => `${this.titleName(siteView)} - ${siteView.site.name}`,
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get titleName(): string {
|
titleName(siteView: SiteView): string {
|
||||||
return `${i18n.t(
|
return i18n.t(siteView.site.private_instance ? "apply_to_join" : "sign_up");
|
||||||
this.state.site_view.site.private_instance ? "apply_to_join" : "sign_up"
|
|
||||||
)}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get isLemmyMl(): boolean {
|
get isLemmyMl(): boolean {
|
||||||
|
@ -127,6 +132,8 @@ 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 class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-lg-6 offset-lg-3">{this.registerForm()}</div>
|
<div class="col-12 col-lg-6 offset-lg-3">{this.registerForm()}</div>
|
||||||
|
@ -136,244 +143,272 @@ export class Signup extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
registerForm() {
|
registerForm() {
|
||||||
return (
|
return this.state.siteRes.site_view.match({
|
||||||
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
|
some: siteView => (
|
||||||
<h5>{this.titleName}</h5>
|
<form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
|
||||||
|
<h5>{this.titleName(siteView)}</h5>
|
||||||
|
|
||||||
{this.isLemmyMl && (
|
{this.isLemmyMl && (
|
||||||
<div class="form-group row">
|
|
||||||
<div class="mt-2 mb-0 alert alert-warning" role="alert">
|
|
||||||
<T i18nKey="lemmy_ml_registration_message">
|
|
||||||
#<a href={joinLemmyUrl}>#</a>
|
|
||||||
</T>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div class="form-group row">
|
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="register-username">
|
|
||||||
{i18n.t("username")}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="register-username"
|
|
||||||
class="form-control"
|
|
||||||
value={this.state.registerForm.username}
|
|
||||||
onInput={linkEvent(this, this.handleRegisterUsernameChange)}
|
|
||||||
required
|
|
||||||
minLength={3}
|
|
||||||
pattern="[a-zA-Z0-9_]+"
|
|
||||||
title={i18n.t("community_reqs")}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group row">
|
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="register-email">
|
|
||||||
{i18n.t("email")}
|
|
||||||
</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
id="register-email"
|
|
||||||
class="form-control"
|
|
||||||
placeholder={
|
|
||||||
this.state.site_view.site.require_email_verification
|
|
||||||
? i18n.t("required")
|
|
||||||
: i18n.t("optional")
|
|
||||||
}
|
|
||||||
value={this.state.registerForm.email}
|
|
||||||
autoComplete="email"
|
|
||||||
onInput={linkEvent(this, this.handleRegisterEmailChange)}
|
|
||||||
required={this.state.site_view.site.require_email_verification}
|
|
||||||
minLength={3}
|
|
||||||
/>
|
|
||||||
{!this.state.site_view.site.require_email_verification &&
|
|
||||||
!validEmail(this.state.registerForm.email) && (
|
|
||||||
<div class="mt-2 mb-0 alert alert-warning" role="alert">
|
|
||||||
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
|
||||||
{i18n.t("no_password_reset")}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group row">
|
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="register-password">
|
|
||||||
{i18n.t("password")}
|
|
||||||
</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
id="register-password"
|
|
||||||
value={this.state.registerForm.password}
|
|
||||||
autoComplete="new-password"
|
|
||||||
onInput={linkEvent(this, this.handleRegisterPasswordChange)}
|
|
||||||
minLength={10}
|
|
||||||
maxLength={60}
|
|
||||||
class="form-control"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
{this.state.registerForm.password && (
|
|
||||||
<div class={this.passwordColorClass}>
|
|
||||||
{i18n.t(this.passwordStrength as I18nKeys)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group row">
|
|
||||||
<label
|
|
||||||
class="col-sm-2 col-form-label"
|
|
||||||
htmlFor="register-verify-password"
|
|
||||||
>
|
|
||||||
{i18n.t("verify_password")}
|
|
||||||
</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
id="register-verify-password"
|
|
||||||
value={this.state.registerForm.password_verify}
|
|
||||||
autoComplete="new-password"
|
|
||||||
onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
|
|
||||||
maxLength={60}
|
|
||||||
class="form-control"
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{this.state.site_view.site.require_application && (
|
|
||||||
<>
|
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="offset-sm-2 col-sm-10">
|
<div class="mt-2 mb-0 alert alert-warning" role="alert">
|
||||||
<div class="mt-2 alert alert-warning" role="alert">
|
<T i18nKey="lemmy_ml_registration_message">
|
||||||
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
#<a href={joinLemmyUrl}>#</a>
|
||||||
{i18n.t("fill_out_application")}
|
</T>
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className="md-div"
|
|
||||||
dangerouslySetInnerHTML={mdToHtml(
|
|
||||||
this.state.site_view.site.application_question || ""
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div class="form-group row">
|
|
||||||
<label
|
|
||||||
class="col-sm-2 col-form-label"
|
|
||||||
htmlFor="application_answer"
|
|
||||||
>
|
|
||||||
{i18n.t("answer")}
|
|
||||||
</label>
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<MarkdownTextArea
|
|
||||||
onContentChange={this.handleAnswerChange}
|
|
||||||
hideNavigationWarnings
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{this.state.captcha && (
|
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-2" htmlFor="register-captcha">
|
<label class="col-sm-2 col-form-label" htmlFor="register-username">
|
||||||
<span class="mr-2">{i18n.t("enter_code")}</span>
|
{i18n.t("username")}
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn btn-secondary"
|
|
||||||
onClick={linkEvent(this, this.handleRegenCaptcha)}
|
|
||||||
aria-label={i18n.t("captcha")}
|
|
||||||
>
|
|
||||||
<Icon icon="refresh-cw" classes="icon-refresh-cw" />
|
|
||||||
</button>
|
|
||||||
</label>
|
</label>
|
||||||
{this.showCaptcha()}
|
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-10">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
id="register-username"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="register-captcha"
|
value={this.state.registerForm.username}
|
||||||
value={this.state.registerForm.captcha_answer}
|
onInput={linkEvent(this, this.handleRegisterUsernameChange)}
|
||||||
|
required
|
||||||
|
minLength={3}
|
||||||
|
pattern="[a-zA-Z0-9_]+"
|
||||||
|
title={i18n.t("community_reqs")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-sm-2 col-form-label" htmlFor="register-email">
|
||||||
|
{i18n.t("email")}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="register-email"
|
||||||
|
class="form-control"
|
||||||
|
placeholder={
|
||||||
|
siteView.site.require_email_verification
|
||||||
|
? i18n.t("required")
|
||||||
|
: i18n.t("optional")
|
||||||
|
}
|
||||||
|
value={toUndefined(this.state.registerForm.email)}
|
||||||
|
autoComplete="email"
|
||||||
|
onInput={linkEvent(this, this.handleRegisterEmailChange)}
|
||||||
|
required={siteView.site.require_email_verification}
|
||||||
|
minLength={3}
|
||||||
|
/>
|
||||||
|
{!siteView.site.require_email_verification &&
|
||||||
|
!this.state.registerForm.email
|
||||||
|
.map(validEmail)
|
||||||
|
.unwrapOr(true) && (
|
||||||
|
<div class="mt-2 mb-0 alert alert-warning" role="alert">
|
||||||
|
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
||||||
|
{i18n.t("no_password_reset")}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-sm-2 col-form-label" htmlFor="register-password">
|
||||||
|
{i18n.t("password")}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="register-password"
|
||||||
|
value={this.state.registerForm.password}
|
||||||
|
autoComplete="new-password"
|
||||||
|
onInput={linkEvent(this, this.handleRegisterPasswordChange)}
|
||||||
|
minLength={10}
|
||||||
|
maxLength={60}
|
||||||
|
class="form-control"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
{this.state.registerForm.password && (
|
||||||
|
<div class={this.passwordColorClass}>
|
||||||
|
{i18n.t(this.passwordStrength as I18nKeys)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label
|
||||||
|
class="col-sm-2 col-form-label"
|
||||||
|
htmlFor="register-verify-password"
|
||||||
|
>
|
||||||
|
{i18n.t("verify_password")}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="register-verify-password"
|
||||||
|
value={this.state.registerForm.password_verify}
|
||||||
|
autoComplete="new-password"
|
||||||
onInput={linkEvent(
|
onInput={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleRegisterCaptchaAnswerChange
|
this.handleRegisterPasswordVerifyChange
|
||||||
)}
|
)}
|
||||||
|
maxLength={60}
|
||||||
|
class="form-control"
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
{this.state.site_view.site.enable_nsfw && (
|
{siteView.site.require_application && (
|
||||||
<div class="form-group row">
|
<>
|
||||||
<div class="col-sm-10">
|
<div class="form-group row">
|
||||||
<div class="form-check">
|
<div class="offset-sm-2 col-sm-10">
|
||||||
<input
|
<div class="mt-2 alert alert-warning" role="alert">
|
||||||
class="form-check-input"
|
<Icon icon="alert-triangle" classes="icon-inline mr-2" />
|
||||||
id="register-show-nsfw"
|
{i18n.t("fill_out_application")}
|
||||||
type="checkbox"
|
</div>
|
||||||
checked={this.state.registerForm.show_nsfw}
|
{siteView.site.application_question.match({
|
||||||
onChange={linkEvent(this, this.handleRegisterShowNsfwChange)}
|
some: question => (
|
||||||
/>
|
<div
|
||||||
<label class="form-check-label" htmlFor="register-show-nsfw">
|
className="md-div"
|
||||||
{i18n.t("show_nsfw")}
|
dangerouslySetInnerHTML={mdToHtml(question)}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label
|
||||||
|
class="col-sm-2 col-form-label"
|
||||||
|
htmlFor="application_answer"
|
||||||
|
>
|
||||||
|
{i18n.t("answer")}
|
||||||
</label>
|
</label>
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<MarkdownTextArea
|
||||||
|
initialContent={None}
|
||||||
|
placeholder={None}
|
||||||
|
buttonTitle={None}
|
||||||
|
maxLength={None}
|
||||||
|
onContentChange={this.handleAnswerChange}
|
||||||
|
hideNavigationWarnings
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{this.state.captcha.isSome() && (
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-sm-2" htmlFor="register-captcha">
|
||||||
|
<span class="mr-2">{i18n.t("enter_code")}</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-secondary"
|
||||||
|
onClick={linkEvent(this, this.handleRegenCaptcha)}
|
||||||
|
aria-label={i18n.t("captcha")}
|
||||||
|
>
|
||||||
|
<Icon icon="refresh-cw" classes="icon-refresh-cw" />
|
||||||
|
</button>
|
||||||
|
</label>
|
||||||
|
{this.showCaptcha()}
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="form-control"
|
||||||
|
id="register-captcha"
|
||||||
|
value={toUndefined(this.state.registerForm.captcha_answer)}
|
||||||
|
onInput={linkEvent(
|
||||||
|
this,
|
||||||
|
this.handleRegisterCaptchaAnswerChange
|
||||||
|
)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
{siteView.site.enable_nsfw && (
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<div class="form-check">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
id="register-show-nsfw"
|
||||||
|
type="checkbox"
|
||||||
|
checked={this.state.registerForm.show_nsfw}
|
||||||
|
onChange={linkEvent(
|
||||||
|
this,
|
||||||
|
this.handleRegisterShowNsfwChange
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<label class="form-check-label" htmlFor="register-show-nsfw">
|
||||||
|
{i18n.t("show_nsfw")}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<input
|
||||||
|
tabIndex={-1}
|
||||||
|
autoComplete="false"
|
||||||
|
name="a_password"
|
||||||
|
type="text"
|
||||||
|
class="form-control honeypot"
|
||||||
|
id="register-honey"
|
||||||
|
value={toUndefined(this.state.registerForm.honeypot)}
|
||||||
|
onInput={linkEvent(this, this.handleHoneyPotChange)}
|
||||||
|
/>
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<button type="submit" class="btn btn-secondary">
|
||||||
|
{this.state.registerLoading ? (
|
||||||
|
<Spinner />
|
||||||
|
) : (
|
||||||
|
this.titleName(siteView)
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</form>
|
||||||
<input
|
),
|
||||||
tabIndex={-1}
|
none: <></>,
|
||||||
autoComplete="false"
|
});
|
||||||
name="a_password"
|
|
||||||
type="text"
|
|
||||||
class="form-control honeypot"
|
|
||||||
id="register-honey"
|
|
||||||
value={this.state.registerForm.honeypot}
|
|
||||||
onInput={linkEvent(this, this.handleHoneyPotChange)}
|
|
||||||
/>
|
|
||||||
<div class="form-group row">
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<button type="submit" class="btn btn-secondary">
|
|
||||||
{this.state.registerLoading ? <Spinner /> : this.titleName}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showCaptcha() {
|
showCaptcha() {
|
||||||
return (
|
return this.state.captcha.match({
|
||||||
<div class="col-sm-4">
|
some: captcha => (
|
||||||
{this.state.captcha.ok && (
|
<div class="col-sm-4">
|
||||||
<>
|
{captcha.ok.match({
|
||||||
<img
|
some: res => (
|
||||||
class="rounded-top img-fluid"
|
<>
|
||||||
src={this.captchaPngSrc()}
|
<img
|
||||||
style="border-bottom-right-radius: 0; border-bottom-left-radius: 0;"
|
class="rounded-top img-fluid"
|
||||||
alt={i18n.t("captcha")}
|
src={this.captchaPngSrc(res)}
|
||||||
/>
|
style="border-bottom-right-radius: 0; border-bottom-left-radius: 0;"
|
||||||
{this.state.captcha.ok.wav && (
|
alt={i18n.t("captcha")}
|
||||||
<button
|
/>
|
||||||
class="rounded-bottom btn btn-sm btn-secondary btn-block"
|
{res.wav.isSome() && (
|
||||||
style="border-top-right-radius: 0; border-top-left-radius: 0;"
|
<button
|
||||||
title={i18n.t("play_captcha_audio")}
|
class="rounded-bottom btn btn-sm btn-secondary btn-block"
|
||||||
onClick={linkEvent(this, this.handleCaptchaPlay)}
|
style="border-top-right-radius: 0; border-top-left-radius: 0;"
|
||||||
type="button"
|
title={i18n.t("play_captcha_audio")}
|
||||||
disabled={this.state.captchaPlaying}
|
onClick={linkEvent(this, this.handleCaptchaPlay)}
|
||||||
>
|
type="button"
|
||||||
<Icon icon="play" classes="icon-play" />
|
disabled={this.state.captchaPlaying}
|
||||||
</button>
|
>
|
||||||
)}
|
<Icon icon="play" classes="icon-play" />
|
||||||
</>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
);
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get passwordStrength() {
|
get passwordStrength() {
|
||||||
|
@ -408,9 +443,9 @@ export class Signup extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegisterEmailChange(i: Signup, event: any) {
|
handleRegisterEmailChange(i: Signup, event: any) {
|
||||||
i.state.registerForm.email = event.target.value;
|
i.state.registerForm.email = Some(event.target.value);
|
||||||
if (i.state.registerForm.email == "") {
|
if (i.state.registerForm.email.unwrap() == "") {
|
||||||
i.state.registerForm.email = undefined;
|
i.state.registerForm.email = None;
|
||||||
}
|
}
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
@ -431,17 +466,17 @@ export class Signup extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleRegisterCaptchaAnswerChange(i: Signup, event: any) {
|
handleRegisterCaptchaAnswerChange(i: Signup, event: any) {
|
||||||
i.state.registerForm.captcha_answer = event.target.value;
|
i.state.registerForm.captcha_answer = Some(event.target.value);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAnswerChange(val: string) {
|
handleAnswerChange(val: string) {
|
||||||
this.state.registerForm.answer = val;
|
this.state.registerForm.answer = Some(val);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleHoneyPotChange(i: Signup, event: any) {
|
handleHoneyPotChange(i: Signup, event: any) {
|
||||||
i.state.registerForm.honeypot = event.target.value;
|
i.state.registerForm.honeypot = Some(event.target.value);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -455,25 +490,34 @@ 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.
|
||||||
if (i.audio == null) {
|
i.state.captcha.match({
|
||||||
let base64 = `data:audio/wav;base64,${i.state.captcha.ok.wav}`;
|
some: captcha =>
|
||||||
i.audio = new Audio(base64);
|
captcha.ok.match({
|
||||||
}
|
some: res => {
|
||||||
|
if (i.audio == null) {
|
||||||
|
let base64 = `data:audio/wav;base64,${res.wav}`;
|
||||||
|
i.audio = new Audio(base64);
|
||||||
|
}
|
||||||
|
|
||||||
i.audio.play();
|
i.audio.play();
|
||||||
|
|
||||||
i.state.captchaPlaying = true;
|
i.state.captchaPlaying = true;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
|
||||||
i.audio.addEventListener("ended", () => {
|
i.audio.addEventListener("ended", () => {
|
||||||
i.audio.currentTime = 0;
|
i.audio.currentTime = 0;
|
||||||
i.state.captchaPlaying = false;
|
i.state.captchaPlaying = false;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
|
}),
|
||||||
|
none: void 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
captchaPngSrc() {
|
captchaPngSrc(captcha: CaptchaResponse) {
|
||||||
return `data:image/png;base64,${this.state.captcha.ok.png}`;
|
return `data:image/png;base64,${captcha.png}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -489,7 +533,7 @@ export class Signup extends Component<any, State> {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
if (op == UserOperation.Register) {
|
if (op == UserOperation.Register) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg).data;
|
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
// Only log them in if a jwt was set
|
// Only log them in if a jwt was set
|
||||||
|
@ -497,7 +541,7 @@ export class Signup extends Component<any, State> {
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.userJoin({
|
wsClient.userJoin({
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
this.props.history.push("/communities");
|
this.props.history.push("/communities");
|
||||||
|
@ -511,17 +555,20 @@ 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).data;
|
let data = wsJsonToRes<GetCaptchaResponse>(msg, GetCaptchaResponse);
|
||||||
if (data.ok) {
|
data.ok.match({
|
||||||
this.state.captcha = data;
|
some: res => {
|
||||||
this.state.registerForm.captcha_uuid = data.ok.uuid;
|
this.state.captcha = Some(data);
|
||||||
this.setState(this.state);
|
this.state.registerForm.captcha_uuid = Some(res.uuid);
|
||||||
}
|
this.setState(this.state);
|
||||||
|
},
|
||||||
|
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).data;
|
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
||||||
this.state.site_view = data.site_view;
|
this.state.siteRes = data;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
|
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 { CreateSite, EditSite, ListingType, Site } from "lemmy-js-client";
|
import {
|
||||||
|
CreateSite,
|
||||||
|
EditSite,
|
||||||
|
ListingType,
|
||||||
|
Site,
|
||||||
|
toUndefined,
|
||||||
|
} from "lemmy-js-client";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { WebSocketService } from "../../services";
|
import { WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
fetchThemeList,
|
fetchThemeList,
|
||||||
wsClient,
|
wsClient,
|
||||||
|
@ -15,38 +22,41 @@ import { ListingTypeSelect } from "../common/listing-type-select";
|
||||||
import { MarkdownTextArea } from "../common/markdown-textarea";
|
import { MarkdownTextArea } from "../common/markdown-textarea";
|
||||||
|
|
||||||
interface SiteFormProps {
|
interface SiteFormProps {
|
||||||
site?: Site; // If a site is given, that means this is an edit
|
site: Option<Site>; // If a site is given, that means this is an edit
|
||||||
showLocal: boolean;
|
showLocal?: boolean;
|
||||||
onCancel?(): any;
|
onCancel?(): void;
|
||||||
onEdit?(): any;
|
onEdit?(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SiteFormState {
|
interface SiteFormState {
|
||||||
siteForm: EditSite;
|
siteForm: EditSite;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
themeList: string[];
|
themeList: Option<string[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
private emptyState: SiteFormState = {
|
private emptyState: SiteFormState = {
|
||||||
siteForm: {
|
siteForm: new EditSite({
|
||||||
enable_downvotes: true,
|
enable_downvotes: Some(true),
|
||||||
open_registration: true,
|
open_registration: Some(true),
|
||||||
enable_nsfw: true,
|
enable_nsfw: Some(true),
|
||||||
name: null,
|
name: None,
|
||||||
icon: null,
|
icon: None,
|
||||||
banner: null,
|
banner: None,
|
||||||
require_email_verification: null,
|
require_email_verification: None,
|
||||||
require_application: null,
|
require_application: None,
|
||||||
application_question: null,
|
application_question: None,
|
||||||
private_instance: null,
|
private_instance: None,
|
||||||
default_theme: null,
|
default_theme: None,
|
||||||
default_post_listing_type: null,
|
sidebar: None,
|
||||||
legal_information: null,
|
default_post_listing_type: None,
|
||||||
auth: authField(false),
|
legal_information: None,
|
||||||
},
|
description: None,
|
||||||
|
community_creation_admin_only: None,
|
||||||
|
auth: undefined,
|
||||||
|
}),
|
||||||
loading: false,
|
loading: false,
|
||||||
themeList: [],
|
themeList: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -67,32 +77,36 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
this.handleDefaultPostListingTypeChange =
|
this.handleDefaultPostListingTypeChange =
|
||||||
this.handleDefaultPostListingTypeChange.bind(this);
|
this.handleDefaultPostListingTypeChange.bind(this);
|
||||||
|
|
||||||
if (this.props.site) {
|
this.props.site.match({
|
||||||
let site = this.props.site;
|
some: site => {
|
||||||
this.state.siteForm = {
|
this.state.siteForm = new EditSite({
|
||||||
name: site.name,
|
name: Some(site.name),
|
||||||
sidebar: site.sidebar,
|
sidebar: site.sidebar,
|
||||||
description: site.description,
|
description: site.description,
|
||||||
enable_downvotes: site.enable_downvotes,
|
enable_downvotes: Some(site.enable_downvotes),
|
||||||
open_registration: site.open_registration,
|
open_registration: Some(site.open_registration),
|
||||||
enable_nsfw: site.enable_nsfw,
|
enable_nsfw: Some(site.enable_nsfw),
|
||||||
community_creation_admin_only: site.community_creation_admin_only,
|
community_creation_admin_only: Some(
|
||||||
icon: site.icon,
|
site.community_creation_admin_only
|
||||||
banner: site.banner,
|
),
|
||||||
require_email_verification: site.require_email_verification,
|
icon: site.icon,
|
||||||
require_application: site.require_application,
|
banner: site.banner,
|
||||||
application_question: site.application_question,
|
require_email_verification: Some(site.require_email_verification),
|
||||||
private_instance: site.private_instance,
|
require_application: Some(site.require_application),
|
||||||
default_theme: site.default_theme,
|
application_question: site.application_question,
|
||||||
default_post_listing_type: site.default_post_listing_type,
|
private_instance: Some(site.private_instance),
|
||||||
legal_information: site.legal_information,
|
default_theme: Some(site.default_theme),
|
||||||
auth: authField(false),
|
default_post_listing_type: Some(site.default_post_listing_type),
|
||||||
};
|
legal_information: site.legal_information,
|
||||||
}
|
auth: auth(false).unwrap(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
this.state.themeList = await fetchThemeList();
|
this.state.themeList = Some(await fetchThemeList());
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +119,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
if (
|
if (
|
||||||
!this.state.loading &&
|
!this.state.loading &&
|
||||||
!this.props.site &&
|
this.props.site.isNone() &&
|
||||||
(this.state.siteForm.name ||
|
(this.state.siteForm.name ||
|
||||||
this.state.siteForm.sidebar ||
|
this.state.siteForm.sidebar ||
|
||||||
this.state.siteForm.application_question ||
|
this.state.siteForm.application_question ||
|
||||||
|
@ -127,7 +141,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
<Prompt
|
<Prompt
|
||||||
when={
|
when={
|
||||||
!this.state.loading &&
|
!this.state.loading &&
|
||||||
!this.props.site &&
|
this.props.site.isNone() &&
|
||||||
(this.state.siteForm.name ||
|
(this.state.siteForm.name ||
|
||||||
this.state.siteForm.sidebar ||
|
this.state.siteForm.sidebar ||
|
||||||
this.state.siteForm.application_question ||
|
this.state.siteForm.application_question ||
|
||||||
|
@ -137,7 +151,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
/>
|
/>
|
||||||
<form onSubmit={linkEvent(this, this.handleCreateSiteSubmit)}>
|
<form onSubmit={linkEvent(this, this.handleCreateSiteSubmit)}>
|
||||||
<h5>{`${
|
<h5>{`${
|
||||||
this.props.site
|
this.props.site.isSome()
|
||||||
? capitalizeFirstLetter(i18n.t("save"))
|
? capitalizeFirstLetter(i18n.t("save"))
|
||||||
: capitalizeFirstLetter(i18n.t("name"))
|
: capitalizeFirstLetter(i18n.t("name"))
|
||||||
} ${i18n.t("your_site")}`}</h5>
|
} ${i18n.t("your_site")}`}</h5>
|
||||||
|
@ -150,7 +164,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
type="text"
|
type="text"
|
||||||
id="create-site-name"
|
id="create-site-name"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
value={this.state.siteForm.name}
|
value={toUndefined(this.state.siteForm.name)}
|
||||||
onInput={linkEvent(this, this.handleSiteNameChange)}
|
onInput={linkEvent(this, this.handleSiteNameChange)}
|
||||||
required
|
required
|
||||||
minLength={3}
|
minLength={3}
|
||||||
|
@ -186,7 +200,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="site-desc"
|
id="site-desc"
|
||||||
value={this.state.siteForm.description}
|
value={toUndefined(this.state.siteForm.description)}
|
||||||
onInput={linkEvent(this, this.handleSiteDescChange)}
|
onInput={linkEvent(this, this.handleSiteDescChange)}
|
||||||
maxLength={150}
|
maxLength={150}
|
||||||
/>
|
/>
|
||||||
|
@ -197,6 +211,9 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.siteForm.sidebar}
|
initialContent={this.state.siteForm.sidebar}
|
||||||
|
placeholder={None}
|
||||||
|
buttonTitle={None}
|
||||||
|
maxLength={None}
|
||||||
onContentChange={this.handleSiteSidebarChange}
|
onContentChange={this.handleSiteSidebarChange}
|
||||||
hideNavigationWarnings
|
hideNavigationWarnings
|
||||||
/>
|
/>
|
||||||
|
@ -209,12 +226,15 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.siteForm.legal_information}
|
initialContent={this.state.siteForm.legal_information}
|
||||||
|
placeholder={None}
|
||||||
|
buttonTitle={None}
|
||||||
|
maxLength={None}
|
||||||
onContentChange={this.handleSiteLegalInfoChange}
|
onContentChange={this.handleSiteLegalInfoChange}
|
||||||
hideNavigationWarnings
|
hideNavigationWarnings
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{this.state.siteForm.require_application && (
|
{this.state.siteForm.require_application.unwrapOr(false) && (
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-12 col-form-label">
|
<label class="col-12 col-form-label">
|
||||||
{i18n.t("application_questionnaire")}
|
{i18n.t("application_questionnaire")}
|
||||||
|
@ -222,6 +242,9 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.siteForm.application_question}
|
initialContent={this.state.siteForm.application_question}
|
||||||
|
placeholder={None}
|
||||||
|
buttonTitle={None}
|
||||||
|
maxLength={None}
|
||||||
onContentChange={this.handleSiteApplicationQuestionChange}
|
onContentChange={this.handleSiteApplicationQuestionChange}
|
||||||
hideNavigationWarnings
|
hideNavigationWarnings
|
||||||
/>
|
/>
|
||||||
|
@ -235,7 +258,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
id="create-site-downvotes"
|
id="create-site-downvotes"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.siteForm.enable_downvotes}
|
checked={toUndefined(this.state.siteForm.enable_downvotes)}
|
||||||
onChange={linkEvent(
|
onChange={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleSiteEnableDownvotesChange
|
this.handleSiteEnableDownvotesChange
|
||||||
|
@ -254,7 +277,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
id="create-site-enable-nsfw"
|
id="create-site-enable-nsfw"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.siteForm.enable_nsfw}
|
checked={toUndefined(this.state.siteForm.enable_nsfw)}
|
||||||
onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
|
onChange={linkEvent(this, this.handleSiteEnableNsfwChange)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
|
@ -273,7 +296,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
id="create-site-open-registration"
|
id="create-site-open-registration"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.siteForm.open_registration}
|
checked={toUndefined(this.state.siteForm.open_registration)}
|
||||||
onChange={linkEvent(
|
onChange={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleSiteOpenRegistrationChange
|
this.handleSiteOpenRegistrationChange
|
||||||
|
@ -295,7 +318,9 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
id="create-site-community-creation-admin-only"
|
id="create-site-community-creation-admin-only"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.siteForm.community_creation_admin_only}
|
checked={toUndefined(
|
||||||
|
this.state.siteForm.community_creation_admin_only
|
||||||
|
)}
|
||||||
onChange={linkEvent(
|
onChange={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleSiteCommunityCreationAdminOnly
|
this.handleSiteCommunityCreationAdminOnly
|
||||||
|
@ -317,7 +342,9 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
id="create-site-require-email-verification"
|
id="create-site-require-email-verification"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.siteForm.require_email_verification}
|
checked={toUndefined(
|
||||||
|
this.state.siteForm.require_email_verification
|
||||||
|
)}
|
||||||
onChange={linkEvent(
|
onChange={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleSiteRequireEmailVerification
|
this.handleSiteRequireEmailVerification
|
||||||
|
@ -339,7 +366,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
id="create-site-require-application"
|
id="create-site-require-application"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.siteForm.require_application}
|
checked={toUndefined(this.state.siteForm.require_application)}
|
||||||
onChange={linkEvent(this, this.handleSiteRequireApplication)}
|
onChange={linkEvent(this, this.handleSiteRequireApplication)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
|
@ -361,12 +388,12 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
id="create-site-default-theme"
|
id="create-site-default-theme"
|
||||||
value={this.state.siteForm.default_theme}
|
value={toUndefined(this.state.siteForm.default_theme)}
|
||||||
onChange={linkEvent(this, this.handleSiteDefaultTheme)}
|
onChange={linkEvent(this, this.handleSiteDefaultTheme)}
|
||||||
class="custom-select w-auto"
|
class="custom-select w-auto"
|
||||||
>
|
>
|
||||||
<option value="browser">{i18n.t("browser_default")}</option>
|
<option value="browser">{i18n.t("browser_default")}</option>
|
||||||
{this.state.themeList.map(theme => (
|
{this.state.themeList.unwrapOr([]).map(theme => (
|
||||||
<option value={theme}>{theme}</option>
|
<option value={theme}>{theme}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
@ -378,7 +405,11 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<ListingTypeSelect
|
<ListingTypeSelect
|
||||||
type_={
|
type_={
|
||||||
ListingType[this.state.siteForm.default_post_listing_type]
|
ListingType[
|
||||||
|
this.state.siteForm.default_post_listing_type.unwrapOr(
|
||||||
|
"Local"
|
||||||
|
)
|
||||||
|
]
|
||||||
}
|
}
|
||||||
showLocal
|
showLocal
|
||||||
showSubscribed={false}
|
showSubscribed={false}
|
||||||
|
@ -394,7 +425,7 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
id="create-site-private-instance"
|
id="create-site-private-instance"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
value={this.state.siteForm.default_theme}
|
value={toUndefined(this.state.siteForm.default_theme)}
|
||||||
onChange={linkEvent(this, this.handleSitePrivateInstance)}
|
onChange={linkEvent(this, this.handleSitePrivateInstance)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
|
@ -415,13 +446,13 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
>
|
>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : this.props.site ? (
|
) : this.props.site.isSome() ? (
|
||||||
capitalizeFirstLetter(i18n.t("save"))
|
capitalizeFirstLetter(i18n.t("save"))
|
||||||
) : (
|
) : (
|
||||||
capitalizeFirstLetter(i18n.t("create"))
|
capitalizeFirstLetter(i18n.t("create"))
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
{this.props.site && (
|
{this.props.site.isSome() && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
|
@ -440,81 +471,98 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
handleCreateSiteSubmit(i: SiteForm, event: any) {
|
handleCreateSiteSubmit(i: SiteForm, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.loading = true;
|
i.state.loading = true;
|
||||||
if (i.props.site) {
|
i.state.siteForm.auth = auth().unwrap();
|
||||||
|
|
||||||
|
if (i.props.site.isSome()) {
|
||||||
WebSocketService.Instance.send(wsClient.editSite(i.state.siteForm));
|
WebSocketService.Instance.send(wsClient.editSite(i.state.siteForm));
|
||||||
i.props.onEdit();
|
i.props.onEdit();
|
||||||
} else {
|
} else {
|
||||||
let form: CreateSite = {
|
let sForm = i.state.siteForm;
|
||||||
name: i.state.siteForm.name || "My site",
|
let form = new CreateSite({
|
||||||
...i.state.siteForm,
|
name: sForm.name.unwrapOr("My site"),
|
||||||
};
|
sidebar: sForm.sidebar,
|
||||||
|
description: sForm.description,
|
||||||
|
icon: sForm.icon,
|
||||||
|
banner: sForm.banner,
|
||||||
|
community_creation_admin_only: sForm.community_creation_admin_only,
|
||||||
|
enable_nsfw: sForm.enable_nsfw,
|
||||||
|
enable_downvotes: sForm.enable_downvotes,
|
||||||
|
require_application: sForm.require_application,
|
||||||
|
application_question: sForm.application_question,
|
||||||
|
open_registration: sForm.open_registration,
|
||||||
|
require_email_verification: sForm.require_email_verification,
|
||||||
|
private_instance: sForm.private_instance,
|
||||||
|
default_theme: sForm.default_theme,
|
||||||
|
default_post_listing_type: sForm.default_post_listing_type,
|
||||||
|
auth: auth().unwrap(),
|
||||||
|
});
|
||||||
WebSocketService.Instance.send(wsClient.createSite(form));
|
WebSocketService.Instance.send(wsClient.createSite(form));
|
||||||
}
|
}
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteNameChange(i: SiteForm, event: any) {
|
handleSiteNameChange(i: SiteForm, event: any) {
|
||||||
i.state.siteForm.name = event.target.value;
|
i.state.siteForm.name = Some(event.target.value);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteSidebarChange(val: string) {
|
handleSiteSidebarChange(val: string) {
|
||||||
this.state.siteForm.sidebar = val;
|
this.state.siteForm.sidebar = Some(val);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteLegalInfoChange(val: string) {
|
handleSiteLegalInfoChange(val: string) {
|
||||||
this.state.siteForm.legal_information = val;
|
this.state.siteForm.legal_information = Some(val);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteApplicationQuestionChange(val: string) {
|
handleSiteApplicationQuestionChange(val: string) {
|
||||||
this.state.siteForm.application_question = val;
|
this.state.siteForm.application_question = Some(val);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteDescChange(i: SiteForm, event: any) {
|
handleSiteDescChange(i: SiteForm, event: any) {
|
||||||
i.state.siteForm.description = event.target.value;
|
i.state.siteForm.description = Some(event.target.value);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteEnableNsfwChange(i: SiteForm, event: any) {
|
handleSiteEnableNsfwChange(i: SiteForm, event: any) {
|
||||||
i.state.siteForm.enable_nsfw = event.target.checked;
|
i.state.siteForm.enable_nsfw = Some(event.target.checked);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteOpenRegistrationChange(i: SiteForm, event: any) {
|
handleSiteOpenRegistrationChange(i: SiteForm, event: any) {
|
||||||
i.state.siteForm.open_registration = event.target.checked;
|
i.state.siteForm.open_registration = Some(event.target.checked);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteCommunityCreationAdminOnly(i: SiteForm, event: any) {
|
handleSiteCommunityCreationAdminOnly(i: SiteForm, event: any) {
|
||||||
i.state.siteForm.community_creation_admin_only = event.target.checked;
|
i.state.siteForm.community_creation_admin_only = Some(event.target.checked);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteEnableDownvotesChange(i: SiteForm, event: any) {
|
handleSiteEnableDownvotesChange(i: SiteForm, event: any) {
|
||||||
i.state.siteForm.enable_downvotes = event.target.checked;
|
i.state.siteForm.enable_downvotes = Some(event.target.checked);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteRequireApplication(i: SiteForm, event: any) {
|
handleSiteRequireApplication(i: SiteForm, event: any) {
|
||||||
i.state.siteForm.require_application = event.target.checked;
|
i.state.siteForm.require_application = Some(event.target.checked);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteRequireEmailVerification(i: SiteForm, event: any) {
|
handleSiteRequireEmailVerification(i: SiteForm, event: any) {
|
||||||
i.state.siteForm.require_email_verification = event.target.checked;
|
i.state.siteForm.require_email_verification = Some(event.target.checked);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSitePrivateInstance(i: SiteForm, event: any) {
|
handleSitePrivateInstance(i: SiteForm, event: any) {
|
||||||
i.state.siteForm.private_instance = event.target.checked;
|
i.state.siteForm.private_instance = Some(event.target.checked);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSiteDefaultTheme(i: SiteForm, event: any) {
|
handleSiteDefaultTheme(i: SiteForm, event: any) {
|
||||||
i.state.siteForm.default_theme = event.target.value;
|
i.state.siteForm.default_theme = Some(event.target.value);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,28 +571,29 @@ export class SiteForm extends Component<SiteFormProps, SiteFormState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleIconUpload(url: string) {
|
handleIconUpload(url: string) {
|
||||||
this.state.siteForm.icon = url;
|
this.state.siteForm.icon = Some(url);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleIconRemove() {
|
handleIconRemove() {
|
||||||
this.state.siteForm.icon = "";
|
this.state.siteForm.icon = Some("");
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerUpload(url: string) {
|
handleBannerUpload(url: string) {
|
||||||
this.state.siteForm.banner = url;
|
this.state.siteForm.banner = Some(url);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerRemove() {
|
handleBannerRemove() {
|
||||||
this.state.siteForm.banner = "";
|
this.state.siteForm.banner = Some("");
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDefaultPostListingTypeChange(val: ListingType) {
|
handleDefaultPostListingTypeChange(val: ListingType) {
|
||||||
this.state.siteForm.default_post_listing_type =
|
this.state.siteForm.default_post_listing_type = Some(
|
||||||
ListingType[ListingType[val]];
|
ListingType[ListingType[val]]
|
||||||
|
);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
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 { PersonViewSafe, Site, SiteAggregates } from "lemmy-js-client";
|
import { PersonViewSafe, Site, SiteAggregates } from "lemmy-js-client";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { UserService } from "../../services";
|
import { amAdmin, mdToHtml, numToSI } from "../../utils";
|
||||||
import { mdToHtml, numToSI } from "../../utils";
|
|
||||||
import { BannerIconHeader } from "../common/banner-icon-header";
|
import { BannerIconHeader } from "../common/banner-icon-header";
|
||||||
import { Icon } from "../common/icon";
|
import { Icon } from "../common/icon";
|
||||||
import { PersonListing } from "../person/person-listing";
|
import { PersonListing } from "../person/person-listing";
|
||||||
|
@ -12,9 +12,9 @@ import { SiteForm } from "./site-form";
|
||||||
interface SiteSidebarProps {
|
interface SiteSidebarProps {
|
||||||
site: Site;
|
site: Site;
|
||||||
showLocal: boolean;
|
showLocal: boolean;
|
||||||
counts?: SiteAggregates;
|
counts: Option<SiteAggregates>;
|
||||||
admins?: PersonViewSafe[];
|
admins: Option<PersonViewSafe[]>;
|
||||||
online?: number;
|
online: Option<number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SiteSidebarState {
|
interface SiteSidebarState {
|
||||||
|
@ -44,18 +44,18 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
<div>
|
<div>
|
||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
{this.siteName()}
|
{this.siteName()}
|
||||||
{this.props.admins && this.adminButtons()}
|
{this.props.admins.isSome() && this.adminButtons()}
|
||||||
</div>
|
</div>
|
||||||
{!this.state.collapsed && (
|
{!this.state.collapsed && (
|
||||||
<>
|
<>
|
||||||
<BannerIconHeader banner={site.banner} />
|
<BannerIconHeader banner={site.banner} icon={None} />
|
||||||
{this.siteInfo()}
|
{this.siteInfo()}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<SiteForm
|
<SiteForm
|
||||||
site={site}
|
site={Some(site)}
|
||||||
showLocal={this.props.showLocal}
|
showLocal={this.props.showLocal}
|
||||||
onEdit={this.handleEditSite}
|
onEdit={this.handleEditSite}
|
||||||
onCancel={this.handleEditCancel}
|
onCancel={this.handleEditCancel}
|
||||||
|
@ -69,23 +69,21 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
siteName() {
|
siteName() {
|
||||||
let site = this.props.site;
|
let site = this.props.site;
|
||||||
return (
|
return (
|
||||||
site.name && (
|
<h5 class="mb-0 d-inline">
|
||||||
<h5 class="mb-0 d-inline">
|
{site.name}
|
||||||
{site.name}
|
<button
|
||||||
<button
|
class="btn btn-sm text-muted"
|
||||||
class="btn btn-sm text-muted"
|
onClick={linkEvent(this, this.handleCollapseSidebar)}
|
||||||
onClick={linkEvent(this, this.handleCollapseSidebar)}
|
aria-label={i18n.t("collapse")}
|
||||||
aria-label={i18n.t("collapse")}
|
data-tippy-content={i18n.t("collapse")}
|
||||||
data-tippy-content={i18n.t("collapse")}
|
>
|
||||||
>
|
{this.state.collapsed ? (
|
||||||
{this.state.collapsed ? (
|
<Icon icon="plus-square" classes="icon-inline" />
|
||||||
<Icon icon="plus-square" classes="icon-inline" />
|
) : (
|
||||||
) : (
|
<Icon icon="minus-square" classes="icon-inline" />
|
||||||
<Icon icon="minus-square" classes="icon-inline" />
|
)}
|
||||||
)}
|
</button>
|
||||||
</button>
|
</h5>
|
||||||
</h5>
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,17 +91,29 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
let site = this.props.site;
|
let site = this.props.site;
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{site.description && <h6>{site.description}</h6>}
|
{site.description.match({
|
||||||
{site.sidebar && this.siteSidebar()}
|
some: description => <h6>{description}</h6>,
|
||||||
{this.props.counts && this.badges()}
|
none: <></>,
|
||||||
{this.props.admins && this.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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
adminButtons() {
|
adminButtons() {
|
||||||
return (
|
return (
|
||||||
this.canAdmin && (
|
amAdmin(this.props.admins) && (
|
||||||
<ul class="list-inline mb-1 text-muted font-weight-bold">
|
<ul class="list-inline mb-1 text-muted font-weight-bold">
|
||||||
<li className="list-inline-item-action">
|
<li className="list-inline-item-action">
|
||||||
<button
|
<button
|
||||||
|
@ -120,20 +130,17 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
siteSidebar() {
|
siteSidebar(sidebar: string) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="md-div" dangerouslySetInnerHTML={mdToHtml(sidebar)} />
|
||||||
className="md-div"
|
|
||||||
dangerouslySetInnerHTML={mdToHtml(this.props.site.sidebar)}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
admins() {
|
admins(admins: PersonViewSafe[]) {
|
||||||
return (
|
return (
|
||||||
<ul class="mt-1 list-inline small mb-0">
|
<ul class="mt-1 list-inline small mb-0">
|
||||||
<li class="list-inline-item">{i18n.t("admins")}:</li>
|
<li class="list-inline-item">{i18n.t("admins")}:</li>
|
||||||
{this.props.admins?.map(av => (
|
{admins.map(av => (
|
||||||
<li class="list-inline-item">
|
<li class="list-inline-item">
|
||||||
<PersonListing person={av.person} />
|
<PersonListing person={av.person} />
|
||||||
</li>
|
</li>
|
||||||
|
@ -142,9 +149,9 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
badges() {
|
badges(siteAggregates: SiteAggregates) {
|
||||||
let counts = this.props.counts;
|
let counts = siteAggregates;
|
||||||
let online = this.props.online;
|
let online = this.props.online.unwrapOr(1);
|
||||||
return (
|
return (
|
||||||
<ul class="my-2 list-inline">
|
<ul class="my-2 list-inline">
|
||||||
<li className="list-inline-item badge badge-secondary">
|
<li className="list-inline-item badge badge-secondary">
|
||||||
|
@ -238,15 +245,6 @@ export class SiteSidebar extends Component<SiteSidebarProps, SiteSidebarState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get canAdmin(): boolean {
|
|
||||||
return (
|
|
||||||
UserService.Instance.myUserInfo &&
|
|
||||||
this.props.admins
|
|
||||||
.map(a => a.person.id)
|
|
||||||
.includes(UserService.Instance.myUserInfo.local_user_view.person.id)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleCollapseSidebar(i: SiteSidebar) {
|
handleCollapseSidebar(i: SiteSidebar) {
|
||||||
i.state.collapsed = !i.state.collapsed;
|
i.state.collapsed = !i.state.collapsed;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { None, Option, Some } from "@sniptt/monads";
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import { Link } from "inferno-router";
|
import { Link } from "inferno-router";
|
||||||
import {
|
import {
|
||||||
|
@ -6,6 +7,7 @@ import {
|
||||||
GetCommunityResponse,
|
GetCommunityResponse,
|
||||||
GetModlog,
|
GetModlog,
|
||||||
GetModlogResponse,
|
GetModlogResponse,
|
||||||
|
GetSiteResponse,
|
||||||
ModAddCommunityView,
|
ModAddCommunityView,
|
||||||
ModAddView,
|
ModAddView,
|
||||||
ModBanFromCommunityView,
|
ModBanFromCommunityView,
|
||||||
|
@ -17,25 +19,25 @@ import {
|
||||||
ModStickyPostView,
|
ModStickyPostView,
|
||||||
ModTransferCommunityView,
|
ModTransferCommunityView,
|
||||||
PersonSafe,
|
PersonSafe,
|
||||||
SiteView,
|
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../i18next";
|
import { i18n } from "../i18next";
|
||||||
import { InitialFetchRequest } from "../interfaces";
|
import { InitialFetchRequest } from "../interfaces";
|
||||||
import { UserService, WebSocketService } from "../services";
|
import { WebSocketService } from "../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
amAdmin,
|
||||||
|
amMod,
|
||||||
|
auth,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
setOptionalAuth,
|
|
||||||
toast,
|
toast,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
import { HtmlTags } from "./common/html-tags";
|
import { HtmlTags } from "./common/html-tags";
|
||||||
import { Spinner } from "./common/icon";
|
import { Spinner } from "./common/icon";
|
||||||
|
@ -75,34 +77,28 @@ type ModlogType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ModlogState {
|
interface ModlogState {
|
||||||
res: GetModlogResponse;
|
res: Option<GetModlogResponse>;
|
||||||
communityId?: number;
|
communityId: Option<number>;
|
||||||
communityName?: string;
|
communityMods: Option<CommunityModeratorView[]>;
|
||||||
communityMods?: CommunityModeratorView[];
|
|
||||||
page: number;
|
page: number;
|
||||||
site_view: SiteView;
|
siteRes: GetSiteResponse;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Modlog extends Component<any, ModlogState> {
|
export class Modlog extends Component<any, ModlogState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(
|
||||||
|
this.context,
|
||||||
|
GetModlogResponse,
|
||||||
|
GetCommunityResponse
|
||||||
|
);
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: ModlogState = {
|
private emptyState: ModlogState = {
|
||||||
res: {
|
res: None,
|
||||||
removed_posts: [],
|
communityId: None,
|
||||||
locked_posts: [],
|
communityMods: None,
|
||||||
stickied_posts: [],
|
|
||||||
removed_comments: [],
|
|
||||||
removed_communities: [],
|
|
||||||
banned_from_community: [],
|
|
||||||
banned: [],
|
|
||||||
added_to_community: [],
|
|
||||||
transferred_to_community: [],
|
|
||||||
added: [],
|
|
||||||
},
|
|
||||||
page: 1,
|
page: 1,
|
||||||
loading: true,
|
loading: true,
|
||||||
site_view: this.isoData.site_res.site_view,
|
siteRes: this.isoData.site_res,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -112,22 +108,23 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
this.handlePageChange = this.handlePageChange.bind(this);
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
|
|
||||||
this.state.communityId = this.props.match.params.community_id
|
this.state.communityId = this.props.match.params.community_id
|
||||||
? Number(this.props.match.params.community_id)
|
? Some(Number(this.props.match.params.community_id))
|
||||||
: undefined;
|
: None;
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
// 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 data = this.isoData.routeData[0];
|
this.state.res = Some(this.isoData.routeData[0] as GetModlogResponse);
|
||||||
this.state.res = data;
|
|
||||||
this.state.loading = false;
|
|
||||||
|
|
||||||
// Getting the moderators
|
// Getting the moderators
|
||||||
if (this.isoData.routeData[1]) {
|
let communityRes = Some(
|
||||||
this.state.communityMods = this.isoData.routeData[1].moderators;
|
this.isoData.routeData[1] as GetCommunityResponse
|
||||||
}
|
);
|
||||||
|
this.state.communityMods = communityRes.map(c => c.moderators);
|
||||||
|
|
||||||
|
this.state.loading = false;
|
||||||
} else {
|
} else {
|
||||||
this.refetch();
|
this.refetch();
|
||||||
}
|
}
|
||||||
|
@ -226,12 +223,6 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
combined.push(...added);
|
combined.push(...added);
|
||||||
combined.push(...banned);
|
combined.push(...banned);
|
||||||
|
|
||||||
if (this.state.communityId && combined.length > 0) {
|
|
||||||
this.state.communityName = (
|
|
||||||
combined[0].view as ModRemovePostView
|
|
||||||
).community.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort them by time
|
// Sort them by time
|
||||||
combined.sort((a, b) => b.when_.localeCompare(a.when_));
|
combined.sort((a, b) => b.when_.localeCompare(a.when_));
|
||||||
|
|
||||||
|
@ -294,11 +285,11 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
<span>
|
<span>
|
||||||
Community <CommunityLink community={mrco.community} />
|
Community <CommunityLink community={mrco.community} />
|
||||||
</span>,
|
</span>,
|
||||||
mrco.mod_remove_community.reason &&
|
mrco.mod_remove_community.reason.isSome() &&
|
||||||
` reason: ${mrco.mod_remove_community.reason}`,
|
` reason: ${mrco.mod_remove_community.reason.unwrap()}`,
|
||||||
mrco.mod_remove_community.expires &&
|
mrco.mod_remove_community.expires.isSome() &&
|
||||||
` expires: ${moment
|
` expires: ${moment
|
||||||
.utc(mrco.mod_remove_community.expires)
|
.utc(mrco.mod_remove_community.expires.unwrap())
|
||||||
.fromNow()}`,
|
.fromNow()}`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -316,13 +307,13 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
<CommunityLink community={mbfc.community} />
|
<CommunityLink community={mbfc.community} />
|
||||||
</span>,
|
</span>,
|
||||||
<div>
|
<div>
|
||||||
{mbfc.mod_ban_from_community.reason &&
|
{mbfc.mod_ban_from_community.reason.isSome() &&
|
||||||
` reason: ${mbfc.mod_ban_from_community.reason}`}
|
` reason: ${mbfc.mod_ban_from_community.reason.unwrap()}`}
|
||||||
</div>,
|
</div>,
|
||||||
<div>
|
<div>
|
||||||
{mbfc.mod_ban_from_community.expires &&
|
{mbfc.mod_ban_from_community.expires.isSome() &&
|
||||||
` expires: ${moment
|
` expires: ${moment
|
||||||
.utc(mbfc.mod_ban_from_community.expires)
|
.utc(mbfc.mod_ban_from_community.expires.unwrap())
|
||||||
.fromNow()}`}
|
.fromNow()}`}
|
||||||
</div>,
|
</div>,
|
||||||
];
|
];
|
||||||
|
@ -364,17 +355,24 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
<span>
|
<span>
|
||||||
<PersonListing person={mb.banned_person} />
|
<PersonListing person={mb.banned_person} />
|
||||||
</span>,
|
</span>,
|
||||||
<div>{mb.mod_ban.reason && ` reason: ${mb.mod_ban.reason}`}</div>,
|
|
||||||
<div>
|
<div>
|
||||||
{mb.mod_ban.expires &&
|
{mb.mod_ban.reason.isSome() &&
|
||||||
` expires: ${moment.utc(mb.mod_ban.expires).fromNow()}`}
|
` reason: ${mb.mod_ban.reason.unwrap()}`}
|
||||||
|
</div>,
|
||||||
|
<div>
|
||||||
|
{mb.mod_ban.expires.isSome() &&
|
||||||
|
` expires: ${moment.utc(mb.mod_ban.expires.unwrap()).fromNow()}`}
|
||||||
</div>,
|
</div>,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
case ModlogEnum.ModAdd: {
|
case ModlogEnum.ModAdd: {
|
||||||
let ma = i.view as ModAddView;
|
let ma = i.view as ModAddView;
|
||||||
return [
|
return [
|
||||||
<span>{ma.mod_add.removed ? "Removed " : "Appointed "} </span>,
|
<span>
|
||||||
|
{ma.mod_add.removed.isSome() && ma.mod_add.removed.unwrap()
|
||||||
|
? "Removed "
|
||||||
|
: "Appointed "}{" "}
|
||||||
|
</span>,
|
||||||
<span>
|
<span>
|
||||||
<PersonListing person={ma.modded_person} />
|
<PersonListing person={ma.modded_person} />
|
||||||
</span>,
|
</span>,
|
||||||
|
@ -387,17 +385,17 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
combined() {
|
combined() {
|
||||||
let combined = this.buildCombined(this.state.res);
|
let combined = this.state.res.map(this.buildCombined).unwrapOr([]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tbody>
|
<tbody>
|
||||||
{combined.map(i => (
|
{combined.map(i => (
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<MomentTime data={i} />
|
<MomentTime published={i.when_} updated={None} />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{this.isAdminOrMod ? (
|
{this.amAdminOrMod ? (
|
||||||
<PersonListing person={i.view.moderator} />
|
<PersonListing person={i.view.moderator} />
|
||||||
) : (
|
) : (
|
||||||
<div>{this.modOrAdminText(i.view.moderator)}</div>
|
<div>{this.modOrAdminText(i.view.moderator)}</div>
|
||||||
|
@ -410,19 +408,11 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get isAdminOrMod(): boolean {
|
get amAdminOrMod(): boolean {
|
||||||
let isAdmin =
|
return (
|
||||||
UserService.Instance.myUserInfo &&
|
amAdmin(Some(this.state.siteRes.admins)) ||
|
||||||
this.isoData.site_res.admins
|
amMod(this.state.communityMods)
|
||||||
.map(a => a.person.id)
|
);
|
||||||
.includes(UserService.Instance.myUserInfo.local_user_view.person.id);
|
|
||||||
let isMod =
|
|
||||||
UserService.Instance.myUserInfo &&
|
|
||||||
this.state.communityMods &&
|
|
||||||
this.state.communityMods
|
|
||||||
.map(m => m.moderator.id)
|
|
||||||
.includes(UserService.Instance.myUserInfo.local_user_view.person.id);
|
|
||||||
return isAdmin || isMod;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
modOrAdminText(person: PersonSafe): Text {
|
modOrAdminText(person: PersonSafe): Text {
|
||||||
|
@ -436,7 +426,10 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `Modlog - ${this.state.site_view.site.name}`;
|
return this.state.siteRes.site_view.match({
|
||||||
|
some: siteView => `Modlog - ${siteView.site.name}`,
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -445,6 +438,8 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
<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>
|
||||||
|
@ -452,17 +447,6 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
</h5>
|
</h5>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
<h5>
|
|
||||||
{this.state.communityName && (
|
|
||||||
<Link
|
|
||||||
className="text-body"
|
|
||||||
to={`/c/${this.state.communityName}`}
|
|
||||||
>
|
|
||||||
/c/{this.state.communityName}{" "}
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
<span>{i18n.t("modlog")}</span>
|
|
||||||
</h5>
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table id="modlog_table" class="table table-sm table-hover">
|
<table id="modlog_table" class="table table-sm table-hover">
|
||||||
<thead class="pointer">
|
<thead class="pointer">
|
||||||
|
@ -491,46 +475,52 @@ export class Modlog extends Component<any, ModlogState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
refetch() {
|
refetch() {
|
||||||
let modlogForm: GetModlog = {
|
let modlogForm = new GetModlog({
|
||||||
community_id: this.state.communityId,
|
community_id: this.state.communityId,
|
||||||
page: this.state.page,
|
mod_person_id: None,
|
||||||
limit: fetchLimit,
|
page: Some(this.state.page),
|
||||||
auth: authField(false),
|
limit: Some(fetchLimit),
|
||||||
};
|
auth: auth(false).ok(),
|
||||||
|
});
|
||||||
WebSocketService.Instance.send(wsClient.getModlog(modlogForm));
|
WebSocketService.Instance.send(wsClient.getModlog(modlogForm));
|
||||||
|
|
||||||
if (this.state.communityId) {
|
this.state.communityId.match({
|
||||||
let communityForm: GetCommunity = {
|
some: id => {
|
||||||
id: this.state.communityId,
|
let communityForm = new GetCommunity({
|
||||||
name: this.state.communityName,
|
id: Some(id),
|
||||||
};
|
name: None,
|
||||||
WebSocketService.Instance.send(wsClient.getCommunity(communityForm));
|
auth: auth(false).ok(),
|
||||||
}
|
});
|
||||||
|
WebSocketService.Instance.send(wsClient.getCommunity(communityForm));
|
||||||
|
},
|
||||||
|
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 communityId = pathSplit[3];
|
let communityId = Some(pathSplit[3]).map(Number);
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
let modlogForm: GetModlog = {
|
let modlogForm = new GetModlog({
|
||||||
page: 1,
|
page: Some(1),
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
};
|
community_id: communityId,
|
||||||
|
mod_person_id: None,
|
||||||
if (communityId) {
|
auth: req.auth,
|
||||||
modlogForm.community_id = Number(communityId);
|
});
|
||||||
}
|
|
||||||
setOptionalAuth(modlogForm, req.auth);
|
|
||||||
|
|
||||||
promises.push(req.client.getModlog(modlogForm));
|
promises.push(req.client.getModlog(modlogForm));
|
||||||
|
|
||||||
if (communityId) {
|
if (communityId.isSome()) {
|
||||||
let communityForm: GetCommunity = {
|
let communityForm = new GetCommunity({
|
||||||
id: Number(communityId),
|
id: communityId,
|
||||||
};
|
name: None,
|
||||||
setOptionalAuth(communityForm, req.auth);
|
auth: req.auth,
|
||||||
|
});
|
||||||
promises.push(req.client.getCommunity(communityForm));
|
promises.push(req.client.getCommunity(communityForm));
|
||||||
|
} else {
|
||||||
|
promises.push(Promise.resolve());
|
||||||
}
|
}
|
||||||
return promises;
|
return promises;
|
||||||
}
|
}
|
||||||
|
@ -542,14 +532,14 @@ 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).data;
|
let data = wsJsonToRes<GetModlogResponse>(msg, GetModlogResponse);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.state.res = data;
|
this.state.res = Some(data);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.GetCommunity) {
|
} else if (op == UserOperation.GetCommunity) {
|
||||||
let data = wsJsonToRes<GetCommunityResponse>(msg).data;
|
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
||||||
this.state.communityMods = data.moderators;
|
this.state.communityMods = Some(data.moderators);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { None, Some } from "@sniptt/monads";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
BlockPersonResponse,
|
BlockPersonResponse,
|
||||||
|
@ -9,25 +10,28 @@ import {
|
||||||
GetPrivateMessages,
|
GetPrivateMessages,
|
||||||
GetReplies,
|
GetReplies,
|
||||||
GetRepliesResponse,
|
GetRepliesResponse,
|
||||||
|
GetSiteResponse,
|
||||||
PersonMentionResponse,
|
PersonMentionResponse,
|
||||||
PersonMentionView,
|
PersonMentionView,
|
||||||
PostReportResponse,
|
PostReportResponse,
|
||||||
PrivateMessageResponse,
|
PrivateMessageResponse,
|
||||||
PrivateMessagesResponse,
|
PrivateMessagesResponse,
|
||||||
PrivateMessageView,
|
PrivateMessageView,
|
||||||
SiteView,
|
|
||||||
SortType,
|
SortType,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { InitialFetchRequest } from "../../interfaces";
|
import { InitialFetchRequest } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
createCommentLikeRes,
|
createCommentLikeRes,
|
||||||
editCommentRes,
|
editCommentRes,
|
||||||
|
enableDownvotes,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
relTags,
|
relTags,
|
||||||
|
@ -37,9 +41,7 @@ import {
|
||||||
toast,
|
toast,
|
||||||
updatePersonBlock,
|
updatePersonBlock,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { CommentNodes } from "../comment/comment-nodes";
|
import { CommentNodes } from "../comment/comment-nodes";
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
|
@ -81,12 +83,17 @@ interface InboxState {
|
||||||
combined: ReplyType[];
|
combined: ReplyType[];
|
||||||
sort: SortType;
|
sort: SortType;
|
||||||
page: number;
|
page: number;
|
||||||
site_view: SiteView;
|
siteRes: GetSiteResponse;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Inbox extends Component<any, InboxState> {
|
export class Inbox extends Component<any, InboxState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(
|
||||||
|
this.context,
|
||||||
|
GetRepliesResponse,
|
||||||
|
GetPersonMentionsResponse,
|
||||||
|
PrivateMessagesResponse
|
||||||
|
);
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: InboxState = {
|
private emptyState: InboxState = {
|
||||||
unreadOrAll: UnreadOrAll.Unread,
|
unreadOrAll: UnreadOrAll.Unread,
|
||||||
|
@ -97,7 +104,7 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
combined: [],
|
combined: [],
|
||||||
sort: SortType.New,
|
sort: SortType.New,
|
||||||
page: 1,
|
page: 1,
|
||||||
site_view: this.isoData.site_res.site_view,
|
siteRes: this.isoData.site_res,
|
||||||
loading: true,
|
loading: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -108,7 +115,7 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
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 && isBrowser()) {
|
if (UserService.Instance.myUserInfo.isNone() && 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,9 +125,13 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
|
|
||||||
// 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.replies = this.isoData.routeData[0].replies || [];
|
this.state.replies =
|
||||||
this.state.mentions = this.isoData.routeData[1].mentions || [];
|
(this.isoData.routeData[0] as GetRepliesResponse).replies || [];
|
||||||
this.state.messages = this.isoData.routeData[2].messages || [];
|
this.state.mentions =
|
||||||
|
(this.isoData.routeData[1] as GetPersonMentionsResponse).mentions || [];
|
||||||
|
this.state.messages =
|
||||||
|
(this.isoData.routeData[2] as PrivateMessagesResponse)
|
||||||
|
.private_messages || [];
|
||||||
this.state.combined = this.buildCombined();
|
this.state.combined = this.buildCombined();
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -135,13 +146,23 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `@${
|
return this.state.siteRes.site_view.match({
|
||||||
UserService.Instance.myUserInfo.local_user_view.person.name
|
some: siteView =>
|
||||||
} ${i18n.t("inbox")} - ${this.state.site_view.site.name}`;
|
UserService.Instance.myUserInfo.match({
|
||||||
|
some: mui =>
|
||||||
|
`@${mui.local_user_view.person.name} ${i18n.t("inbox")} - ${
|
||||||
|
siteView.site.name
|
||||||
|
}`,
|
||||||
|
none: "",
|
||||||
|
}),
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let inboxRss = `/feeds/inbox/${UserService.Instance.auth}.xml`;
|
let inboxRss = auth()
|
||||||
|
.ok()
|
||||||
|
.map(a => `/feeds/inbox/${a}.xml`);
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
|
@ -154,19 +175,26 @@ 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 class="mb-2">
|
<h5 class="mb-2">
|
||||||
{i18n.t("inbox")}
|
{i18n.t("inbox")}
|
||||||
<small>
|
{inboxRss.match({
|
||||||
<a href={inboxRss} title="RSS" rel={relTags}>
|
some: rss => (
|
||||||
<Icon icon="rss" classes="ml-2 text-muted small" />
|
<small>
|
||||||
</a>
|
<a href={rss} title="RSS" rel={relTags}>
|
||||||
<link
|
<Icon icon="rss" classes="ml-2 text-muted small" />
|
||||||
rel="alternate"
|
</a>
|
||||||
type="application/atom+xml"
|
<link
|
||||||
href={inboxRss}
|
rel="alternate"
|
||||||
/>
|
type="application/atom+xml"
|
||||||
</small>
|
href={rss}
|
||||||
|
/>
|
||||||
|
</small>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
</h5>
|
</h5>
|
||||||
{this.state.replies.length +
|
{this.state.replies.length +
|
||||||
this.state.mentions.length +
|
this.state.mentions.length +
|
||||||
|
@ -355,11 +383,14 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
<CommentNodes
|
<CommentNodes
|
||||||
key={i.id}
|
key={i.id}
|
||||||
nodes={[{ comment_view: i.view as CommentView }]}
|
nodes={[{ comment_view: i.view as CommentView }]}
|
||||||
|
moderators={None}
|
||||||
|
admins={None}
|
||||||
|
maxCommentsShown={None}
|
||||||
noIndent
|
noIndent
|
||||||
markable
|
markable
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={this.state.site_view.site.enable_downvotes}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case ReplyEnum.Mention:
|
case ReplyEnum.Mention:
|
||||||
|
@ -367,11 +398,14 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
<CommentNodes
|
<CommentNodes
|
||||||
key={i.id}
|
key={i.id}
|
||||||
nodes={[{ comment_view: i.view as PersonMentionView }]}
|
nodes={[{ comment_view: i.view as PersonMentionView }]}
|
||||||
|
moderators={None}
|
||||||
|
admins={None}
|
||||||
|
maxCommentsShown={None}
|
||||||
noIndent
|
noIndent
|
||||||
markable
|
markable
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={this.state.site_view.site.enable_downvotes}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case ReplyEnum.Message:
|
case ReplyEnum.Message:
|
||||||
|
@ -395,11 +429,14 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
<div>
|
<div>
|
||||||
<CommentNodes
|
<CommentNodes
|
||||||
nodes={commentsToFlatNodes(this.state.replies)}
|
nodes={commentsToFlatNodes(this.state.replies)}
|
||||||
|
moderators={None}
|
||||||
|
admins={None}
|
||||||
|
maxCommentsShown={None}
|
||||||
noIndent
|
noIndent
|
||||||
markable
|
markable
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={this.state.site_view.site.enable_downvotes}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -412,11 +449,14 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
<CommentNodes
|
<CommentNodes
|
||||||
key={umv.person_mention.id}
|
key={umv.person_mention.id}
|
||||||
nodes={[{ comment_view: umv }]}
|
nodes={[{ comment_view: umv }]}
|
||||||
|
moderators={None}
|
||||||
|
admins={None}
|
||||||
|
maxCommentsShown={None}
|
||||||
noIndent
|
noIndent
|
||||||
markable
|
markable
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
enableDownvotes={this.state.site_view.site.enable_downvotes}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
@ -459,62 +499,67 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
// It can be /u/me, or /username/1
|
// It can be /u/me, or /username/1
|
||||||
let repliesForm: GetReplies = {
|
let repliesForm = new GetReplies({
|
||||||
sort: SortType.New,
|
sort: Some(SortType.New),
|
||||||
unread_only: true,
|
unread_only: Some(true),
|
||||||
page: 1,
|
page: Some(1),
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
auth: req.auth,
|
auth: req.auth.unwrap(),
|
||||||
};
|
});
|
||||||
promises.push(req.client.getReplies(repliesForm));
|
promises.push(req.client.getReplies(repliesForm));
|
||||||
|
|
||||||
let personMentionsForm: GetPersonMentions = {
|
let personMentionsForm = new GetPersonMentions({
|
||||||
sort: SortType.New,
|
sort: Some(SortType.New),
|
||||||
unread_only: true,
|
unread_only: Some(true),
|
||||||
page: 1,
|
page: Some(1),
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
auth: req.auth,
|
auth: req.auth.unwrap(),
|
||||||
};
|
});
|
||||||
promises.push(req.client.getPersonMentions(personMentionsForm));
|
promises.push(req.client.getPersonMentions(personMentionsForm));
|
||||||
|
|
||||||
let privateMessagesForm: GetPrivateMessages = {
|
let privateMessagesForm = new GetPrivateMessages({
|
||||||
unread_only: true,
|
unread_only: Some(true),
|
||||||
page: 1,
|
page: Some(1),
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
auth: req.auth,
|
auth: req.auth.unwrap(),
|
||||||
};
|
});
|
||||||
promises.push(req.client.getPrivateMessages(privateMessagesForm));
|
promises.push(req.client.getPrivateMessages(privateMessagesForm));
|
||||||
|
|
||||||
return promises;
|
return promises;
|
||||||
}
|
}
|
||||||
|
|
||||||
refetch() {
|
refetch() {
|
||||||
let repliesForm: GetReplies = {
|
let sort = Some(this.state.sort);
|
||||||
sort: this.state.sort,
|
let unread_only = Some(this.state.unreadOrAll == UnreadOrAll.Unread);
|
||||||
unread_only: this.state.unreadOrAll == UnreadOrAll.Unread,
|
let page = Some(this.state.page);
|
||||||
page: this.state.page,
|
let limit = Some(fetchLimit);
|
||||||
limit: fetchLimit,
|
|
||||||
auth: authField(),
|
let repliesForm = new GetReplies({
|
||||||
};
|
sort,
|
||||||
|
unread_only,
|
||||||
|
page,
|
||||||
|
limit,
|
||||||
|
auth: auth().unwrap(),
|
||||||
|
});
|
||||||
WebSocketService.Instance.send(wsClient.getReplies(repliesForm));
|
WebSocketService.Instance.send(wsClient.getReplies(repliesForm));
|
||||||
|
|
||||||
let personMentionsForm: GetPersonMentions = {
|
let personMentionsForm = new GetPersonMentions({
|
||||||
sort: this.state.sort,
|
sort,
|
||||||
unread_only: this.state.unreadOrAll == UnreadOrAll.Unread,
|
unread_only,
|
||||||
page: this.state.page,
|
page,
|
||||||
limit: fetchLimit,
|
limit,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.getPersonMentions(personMentionsForm)
|
wsClient.getPersonMentions(personMentionsForm)
|
||||||
);
|
);
|
||||||
|
|
||||||
let privateMessagesForm: GetPrivateMessages = {
|
let privateMessagesForm = new GetPrivateMessages({
|
||||||
unread_only: this.state.unreadOrAll == UnreadOrAll.Unread,
|
unread_only,
|
||||||
page: this.state.page,
|
page,
|
||||||
limit: fetchLimit,
|
limit,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.getPrivateMessages(privateMessagesForm)
|
wsClient.getPrivateMessages(privateMessagesForm)
|
||||||
);
|
);
|
||||||
|
@ -530,7 +575,7 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
markAllAsRead(i: Inbox) {
|
markAllAsRead(i: Inbox) {
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.markAllAsRead({
|
wsClient.markAllAsRead({
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
i.state.replies = [];
|
i.state.replies = [];
|
||||||
|
@ -559,7 +604,7 @@ 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).data;
|
let data = wsJsonToRes<GetRepliesResponse>(msg, GetRepliesResponse);
|
||||||
this.state.replies = data.replies;
|
this.state.replies = data.replies;
|
||||||
this.state.combined = this.buildCombined();
|
this.state.combined = this.buildCombined();
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
|
@ -567,21 +612,30 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.GetPersonMentions) {
|
} else if (op == UserOperation.GetPersonMentions) {
|
||||||
let data = wsJsonToRes<GetPersonMentionsResponse>(msg).data;
|
let data = wsJsonToRes<GetPersonMentionsResponse>(
|
||||||
|
msg,
|
||||||
|
GetPersonMentionsResponse
|
||||||
|
);
|
||||||
this.state.mentions = data.mentions;
|
this.state.mentions = data.mentions;
|
||||||
this.state.combined = this.buildCombined();
|
this.state.combined = this.buildCombined();
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.GetPrivateMessages) {
|
} else if (op == UserOperation.GetPrivateMessages) {
|
||||||
let data = wsJsonToRes<PrivateMessagesResponse>(msg).data;
|
let data = wsJsonToRes<PrivateMessagesResponse>(
|
||||||
|
msg,
|
||||||
|
PrivateMessagesResponse
|
||||||
|
);
|
||||||
this.state.messages = data.private_messages;
|
this.state.messages = data.private_messages;
|
||||||
this.state.combined = this.buildCombined();
|
this.state.combined = this.buildCombined();
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.EditPrivateMessage) {
|
} else if (op == UserOperation.EditPrivateMessage) {
|
||||||
let data = wsJsonToRes<PrivateMessageResponse>(msg).data;
|
let data = wsJsonToRes<PrivateMessageResponse>(
|
||||||
|
msg,
|
||||||
|
PrivateMessageResponse
|
||||||
|
);
|
||||||
let found: PrivateMessageView = this.state.messages.find(
|
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
|
||||||
|
@ -597,7 +651,10 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.DeletePrivateMessage) {
|
} else if (op == UserOperation.DeletePrivateMessage) {
|
||||||
let data = wsJsonToRes<PrivateMessageResponse>(msg).data;
|
let data = wsJsonToRes<PrivateMessageResponse>(
|
||||||
|
msg,
|
||||||
|
PrivateMessageResponse
|
||||||
|
);
|
||||||
let found: PrivateMessageView = this.state.messages.find(
|
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
|
||||||
|
@ -613,7 +670,10 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.MarkPrivateMessageAsRead) {
|
} else if (op == UserOperation.MarkPrivateMessageAsRead) {
|
||||||
let data = wsJsonToRes<PrivateMessageResponse>(msg).data;
|
let data = wsJsonToRes<PrivateMessageResponse>(
|
||||||
|
msg,
|
||||||
|
PrivateMessageResponse
|
||||||
|
);
|
||||||
let found: PrivateMessageView = this.state.messages.find(
|
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
|
||||||
|
@ -653,11 +713,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).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
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.MarkCommentAsRead) {
|
} else if (op == UserOperation.MarkCommentAsRead) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
|
|
||||||
// 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 (
|
||||||
|
@ -685,7 +745,7 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.MarkPersonMentionAsRead) {
|
} else if (op == UserOperation.MarkPersonMentionAsRead) {
|
||||||
let data = wsJsonToRes<PersonMentionResponse>(msg).data;
|
let data = wsJsonToRes<PersonMentionResponse>(msg, PersonMentionResponse);
|
||||||
|
|
||||||
// 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(
|
||||||
|
@ -732,85 +792,109 @@ export class Inbox extends Component<any, InboxState> {
|
||||||
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.CreateComment) {
|
} else if (op == UserOperation.CreateComment) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
|
|
||||||
if (
|
UserService.Instance.myUserInfo.match({
|
||||||
data.recipient_ids.includes(
|
some: mui => {
|
||||||
UserService.Instance.myUserInfo.local_user_view.local_user.id
|
if (data.recipient_ids.includes(mui.local_user_view.local_user.id)) {
|
||||||
)
|
this.state.replies.unshift(data.comment_view);
|
||||||
) {
|
this.state.combined.unshift(
|
||||||
this.state.replies.unshift(data.comment_view);
|
this.replyToReplyType(data.comment_view)
|
||||||
this.state.combined.unshift(this.replyToReplyType(data.comment_view));
|
);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (
|
} else if (
|
||||||
data.comment_view.creator.id ==
|
data.comment_view.creator.id == mui.local_user_view.person.id
|
||||||
UserService.Instance.myUserInfo.local_user_view.person.id
|
) {
|
||||||
) {
|
// 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 (this.state.unreadOrAll == UnreadOrAll.Unread) {
|
||||||
if (this.state.unreadOrAll == UnreadOrAll.Unread) {
|
this.state.replies = this.state.replies.filter(
|
||||||
this.state.replies = this.state.replies.filter(
|
r =>
|
||||||
r => r.comment.id !== data.comment_view.comment.parent_id
|
r.comment.id !==
|
||||||
);
|
data.comment_view.comment.parent_id.unwrapOr(0)
|
||||||
this.state.mentions = this.state.mentions.filter(
|
);
|
||||||
m => m.comment.id !== data.comment_view.comment.parent_id
|
this.state.mentions = this.state.mentions.filter(
|
||||||
);
|
m =>
|
||||||
this.state.combined = this.state.combined.filter(r => {
|
m.comment.id !==
|
||||||
if (this.isMention(r.view))
|
data.comment_view.comment.parent_id.unwrapOr(0)
|
||||||
return r.view.comment.id !== data.comment_view.comment.parent_id;
|
);
|
||||||
else return r.id !== data.comment_view.comment.parent_id;
|
this.state.combined = this.state.combined.filter(r => {
|
||||||
});
|
if (this.isMention(r.view))
|
||||||
} else {
|
return (
|
||||||
let mention_found = this.state.mentions.find(
|
r.view.comment.id !==
|
||||||
i => i.comment.id == data.comment_view.comment.parent_id
|
data.comment_view.comment.parent_id.unwrapOr(0)
|
||||||
);
|
);
|
||||||
if (mention_found) {
|
else
|
||||||
mention_found.person_mention.read = true;
|
return (
|
||||||
|
r.id !== data.comment_view.comment.parent_id.unwrapOr(0)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let mention_found = this.state.mentions.find(
|
||||||
|
i =>
|
||||||
|
i.comment.id ==
|
||||||
|
data.comment_view.comment.parent_id.unwrapOr(0)
|
||||||
|
);
|
||||||
|
if (mention_found) {
|
||||||
|
mention_found.person_mention.read = true;
|
||||||
|
}
|
||||||
|
let reply_found = this.state.replies.find(
|
||||||
|
i =>
|
||||||
|
i.comment.id ==
|
||||||
|
data.comment_view.comment.parent_id.unwrapOr(0)
|
||||||
|
);
|
||||||
|
if (reply_found) {
|
||||||
|
reply_found.comment.read = true;
|
||||||
|
}
|
||||||
|
this.state.combined = this.buildCombined();
|
||||||
|
}
|
||||||
|
this.sendUnreadCount(true);
|
||||||
|
this.setState(this.state);
|
||||||
|
setupTippy();
|
||||||
|
// TODO this seems wrong, you should be using form_id
|
||||||
|
toast(i18n.t("reply_sent"));
|
||||||
}
|
}
|
||||||
let reply_found = this.state.replies.find(
|
},
|
||||||
i => i.comment.id == data.comment_view.comment.parent_id
|
none: void 0,
|
||||||
);
|
});
|
||||||
if (reply_found) {
|
|
||||||
reply_found.comment.read = true;
|
|
||||||
}
|
|
||||||
this.state.combined = this.buildCombined();
|
|
||||||
}
|
|
||||||
this.sendUnreadCount(true);
|
|
||||||
this.setState(this.state);
|
|
||||||
setupTippy();
|
|
||||||
// TODO this seems wrong, you should be using form_id
|
|
||||||
toast(i18n.t("reply_sent"));
|
|
||||||
}
|
|
||||||
} else if (op == UserOperation.CreatePrivateMessage) {
|
} else if (op == UserOperation.CreatePrivateMessage) {
|
||||||
let data = wsJsonToRes<PrivateMessageResponse>(msg).data;
|
let data = wsJsonToRes<PrivateMessageResponse>(
|
||||||
if (
|
msg,
|
||||||
data.private_message_view.recipient.id ==
|
PrivateMessageResponse
|
||||||
UserService.Instance.myUserInfo.local_user_view.person.id
|
);
|
||||||
) {
|
UserService.Instance.myUserInfo.match({
|
||||||
this.state.messages.unshift(data.private_message_view);
|
some: mui => {
|
||||||
this.state.combined.unshift(
|
if (
|
||||||
this.messageToReplyType(data.private_message_view)
|
data.private_message_view.recipient.id ==
|
||||||
);
|
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).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
|
||||||
updatePersonBlock(data);
|
updatePersonBlock(data);
|
||||||
} else if (op == UserOperation.CreatePostReport) {
|
} else if (op == UserOperation.CreatePostReport) {
|
||||||
let data = wsJsonToRes<PostReportResponse>(msg).data;
|
let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
toast(i18n.t("report_created"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
import { None } from "@sniptt/monads";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
|
GetSiteResponse,
|
||||||
LoginResponse,
|
LoginResponse,
|
||||||
PasswordChange as PasswordChangeForm,
|
PasswordChange as PasswordChangeForm,
|
||||||
SiteView,
|
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
|
@ -14,9 +17,7 @@ import {
|
||||||
setIsoData,
|
setIsoData,
|
||||||
toast,
|
toast,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
|
@ -24,7 +25,7 @@ import { Spinner } from "../common/icon";
|
||||||
interface State {
|
interface State {
|
||||||
passwordChangeForm: PasswordChangeForm;
|
passwordChangeForm: PasswordChangeForm;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
site_view: SiteView;
|
siteRes: GetSiteResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PasswordChange extends Component<any, State> {
|
export class PasswordChange extends Component<any, State> {
|
||||||
|
@ -32,13 +33,13 @@ export class PasswordChange extends Component<any, State> {
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
|
|
||||||
emptyState: State = {
|
emptyState: State = {
|
||||||
passwordChangeForm: {
|
passwordChangeForm: new PasswordChangeForm({
|
||||||
token: this.props.match.params.token,
|
token: this.props.match.params.token,
|
||||||
password: undefined,
|
password: undefined,
|
||||||
password_verify: undefined,
|
password_verify: undefined,
|
||||||
},
|
}),
|
||||||
loading: false,
|
loading: false,
|
||||||
site_view: this.isoData.site_res.site_view,
|
siteRes: this.isoData.site_res,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -57,7 +58,10 @@ export class PasswordChange extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `${i18n.t("password_change")} - ${this.state.site_view.site.name}`;
|
return this.state.siteRes.site_view.match({
|
||||||
|
some: siteView => `${i18n.t("password_change")} - ${siteView.site.name}`,
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -66,6 +70,8 @@ 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 class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||||
|
@ -156,7 +162,7 @@ export class PasswordChange extends Component<any, State> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.PasswordChange) {
|
} else if (op == UserOperation.PasswordChange) {
|
||||||
let data = wsJsonToRes<LoginResponse>(msg).data;
|
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { None, Some } from "@sniptt/monads/build";
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommentView,
|
CommentView,
|
||||||
|
@ -89,7 +90,9 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
<CommentNodes
|
<CommentNodes
|
||||||
key={i.id}
|
key={i.id}
|
||||||
nodes={[{ comment_view: c }]}
|
nodes={[{ comment_view: c }]}
|
||||||
admins={this.props.admins}
|
admins={Some(this.props.admins)}
|
||||||
|
moderators={None}
|
||||||
|
maxCommentsShown={None}
|
||||||
noBorder
|
noBorder
|
||||||
noIndent
|
noIndent
|
||||||
showCommunity
|
showCommunity
|
||||||
|
@ -104,7 +107,9 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
<PostListing
|
<PostListing
|
||||||
key={i.id}
|
key={i.id}
|
||||||
post_view={p}
|
post_view={p}
|
||||||
admins={this.props.admins}
|
admins={Some(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}
|
||||||
|
@ -154,7 +159,9 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
<div>
|
<div>
|
||||||
<CommentNodes
|
<CommentNodes
|
||||||
nodes={commentsToFlatNodes(this.props.personRes.comments)}
|
nodes={commentsToFlatNodes(this.props.personRes.comments)}
|
||||||
admins={this.props.admins}
|
admins={Some(this.props.admins)}
|
||||||
|
moderators={None}
|
||||||
|
maxCommentsShown={None}
|
||||||
noIndent
|
noIndent
|
||||||
showCommunity
|
showCommunity
|
||||||
showContext
|
showContext
|
||||||
|
@ -171,8 +178,10 @@ export class PersonDetails extends Component<PersonDetailsProps, any> {
|
||||||
<>
|
<>
|
||||||
<PostListing
|
<PostListing
|
||||||
post_view={post}
|
post_view={post}
|
||||||
admins={this.props.admins}
|
admins={Some(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}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -21,7 +21,7 @@ export class PersonListing extends Component<PersonListingProps, any> {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let person = this.props.person;
|
let person = this.props.person;
|
||||||
let local = person.local == null ? true : person.local;
|
let local = person.local;
|
||||||
let apubName: string, link: string;
|
let apubName: string, link: string;
|
||||||
|
|
||||||
if (local) {
|
if (local) {
|
||||||
|
@ -37,11 +37,9 @@ export class PersonListing extends Component<PersonListingProps, any> {
|
||||||
|
|
||||||
let displayName = this.props.useApubName
|
let displayName = this.props.useApubName
|
||||||
? apubName
|
? apubName
|
||||||
: person.display_name
|
: person.display_name.unwrapOr(apubName);
|
||||||
? person.display_name
|
|
||||||
: apubName;
|
|
||||||
|
|
||||||
if (this.props.showApubName && !local && person.display_name) {
|
if (this.props.showApubName && !local && person.display_name.isSome()) {
|
||||||
displayName = `${displayName} (${apubName})`;
|
displayName = `${displayName} (${apubName})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,12 +70,14 @@ export class PersonListing extends Component<PersonListingProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
avatarAndName(displayName: string) {
|
avatarAndName(displayName: string) {
|
||||||
let person = this.props.person;
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!this.props.hideAvatar && person.avatar && showAvatars() && (
|
{this.props.person.avatar.match({
|
||||||
<PictrsImage src={person.avatar} icon />
|
some: avatar =>
|
||||||
)}
|
!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,18 +1,20 @@
|
||||||
|
import { None, Option, Some } from "@sniptt/monads";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
|
GetSiteResponse,
|
||||||
ListRegistrationApplications,
|
ListRegistrationApplications,
|
||||||
ListRegistrationApplicationsResponse,
|
ListRegistrationApplicationsResponse,
|
||||||
RegistrationApplicationResponse,
|
RegistrationApplicationResponse,
|
||||||
RegistrationApplicationView,
|
|
||||||
SiteView,
|
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { InitialFetchRequest } from "../../interfaces";
|
import { InitialFetchRequest } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
|
@ -20,9 +22,7 @@ import {
|
||||||
toast,
|
toast,
|
||||||
updateRegistrationApplicationRes,
|
updateRegistrationApplicationRes,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
|
@ -35,10 +35,10 @@ enum UnreadOrAll {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RegistrationApplicationsState {
|
interface RegistrationApplicationsState {
|
||||||
applications: RegistrationApplicationView[];
|
listRegistrationApplicationsResponse: Option<ListRegistrationApplicationsResponse>;
|
||||||
page: number;
|
siteRes: GetSiteResponse;
|
||||||
site_view: SiteView;
|
|
||||||
unreadOrAll: UnreadOrAll;
|
unreadOrAll: UnreadOrAll;
|
||||||
|
page: number;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,13 +46,16 @@ export class RegistrationApplications extends Component<
|
||||||
any,
|
any,
|
||||||
RegistrationApplicationsState
|
RegistrationApplicationsState
|
||||||
> {
|
> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(
|
||||||
|
this.context,
|
||||||
|
ListRegistrationApplicationsResponse
|
||||||
|
);
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: RegistrationApplicationsState = {
|
private emptyState: RegistrationApplicationsState = {
|
||||||
|
listRegistrationApplicationsResponse: None,
|
||||||
|
siteRes: this.isoData.site_res,
|
||||||
unreadOrAll: UnreadOrAll.Unread,
|
unreadOrAll: UnreadOrAll.Unread,
|
||||||
applications: [],
|
|
||||||
page: 1,
|
page: 1,
|
||||||
site_view: this.isoData.site_res.site_view,
|
|
||||||
loading: true,
|
loading: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -62,7 +65,7 @@ export class RegistrationApplications extends Component<
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
this.handlePageChange = this.handlePageChange.bind(this);
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
|
|
||||||
if (!UserService.Instance.myUserInfo && isBrowser()) {
|
if (UserService.Instance.myUserInfo.isNone() && 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`);
|
||||||
}
|
}
|
||||||
|
@ -72,8 +75,9 @@ export class RegistrationApplications extends Component<
|
||||||
|
|
||||||
// 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.applications =
|
this.state.listRegistrationApplicationsResponse = Some(
|
||||||
this.isoData.routeData[0].registration_applications || []; // TODO test
|
this.isoData.routeData[0] as ListRegistrationApplicationsResponse
|
||||||
|
);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
} else {
|
} else {
|
||||||
this.refetch();
|
this.refetch();
|
||||||
|
@ -91,11 +95,17 @@ export class RegistrationApplications extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `@${
|
return this.state.siteRes.site_view.match({
|
||||||
UserService.Instance.myUserInfo.local_user_view.person.name
|
some: siteView =>
|
||||||
} ${i18n.t("registration_applications")} - ${
|
UserService.Instance.myUserInfo.match({
|
||||||
this.state.site_view.site.name
|
some: mui =>
|
||||||
}`;
|
`@${mui.local_user_view.person.name} ${i18n.t(
|
||||||
|
"registration_applications"
|
||||||
|
)} - ${siteView.site.name}`,
|
||||||
|
none: "",
|
||||||
|
}),
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -111,6 +121,8 @@ 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 class="mb-2">{i18n.t("registration_applications")}</h5>
|
<h5 class="mb-2">{i18n.t("registration_applications")}</h5>
|
||||||
{this.selects()}
|
{this.selects()}
|
||||||
|
@ -168,19 +180,22 @@ export class RegistrationApplications extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
applicationList() {
|
applicationList() {
|
||||||
return (
|
return this.state.listRegistrationApplicationsResponse.match({
|
||||||
<div>
|
some: res => (
|
||||||
{this.state.applications.map(ra => (
|
<div>
|
||||||
<>
|
{res.registration_applications.map(ra => (
|
||||||
<hr />
|
<>
|
||||||
<RegistrationApplication
|
<hr />
|
||||||
key={ra.registration_application.id}
|
<RegistrationApplication
|
||||||
application={ra}
|
key={ra.registration_application.id}
|
||||||
/>
|
application={ra}
|
||||||
</>
|
/>
|
||||||
))}
|
</>
|
||||||
</div>
|
))}
|
||||||
);
|
</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnreadOrAllChange(i: RegistrationApplications, event: any) {
|
handleUnreadOrAllChange(i: RegistrationApplications, event: any) {
|
||||||
|
@ -198,12 +213,12 @@ 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: ListRegistrationApplications = {
|
let form = new ListRegistrationApplications({
|
||||||
unread_only: true,
|
unread_only: Some(true),
|
||||||
page: 1,
|
page: Some(1),
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
auth: req.auth,
|
auth: req.auth.unwrap(),
|
||||||
};
|
});
|
||||||
promises.push(req.client.listRegistrationApplications(form));
|
promises.push(req.client.listRegistrationApplications(form));
|
||||||
|
|
||||||
return promises;
|
return promises;
|
||||||
|
@ -211,12 +226,12 @@ export class RegistrationApplications extends Component<
|
||||||
|
|
||||||
refetch() {
|
refetch() {
|
||||||
let unread_only = this.state.unreadOrAll == UnreadOrAll.Unread;
|
let unread_only = this.state.unreadOrAll == UnreadOrAll.Unread;
|
||||||
let form: ListRegistrationApplications = {
|
let form = new ListRegistrationApplications({
|
||||||
unread_only: unread_only,
|
unread_only: Some(unread_only),
|
||||||
page: this.state.page,
|
page: Some(this.state.page),
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.listRegistrationApplications(form));
|
WebSocketService.Instance.send(wsClient.listRegistrationApplications(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -229,16 +244,24 @@ 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>(msg).data;
|
let data = wsJsonToRes<ListRegistrationApplicationsResponse>(
|
||||||
this.state.applications = data.registration_applications;
|
msg,
|
||||||
|
ListRegistrationApplicationsResponse
|
||||||
|
);
|
||||||
|
this.state.listRegistrationApplicationsResponse = Some(data);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.ApproveRegistrationApplication) {
|
} else if (op == UserOperation.ApproveRegistrationApplication) {
|
||||||
let data = wsJsonToRes<RegistrationApplicationResponse>(msg).data;
|
let data = wsJsonToRes<RegistrationApplicationResponse>(
|
||||||
|
msg,
|
||||||
|
RegistrationApplicationResponse
|
||||||
|
);
|
||||||
updateRegistrationApplicationRes(
|
updateRegistrationApplicationRes(
|
||||||
data.registration_application,
|
data.registration_application,
|
||||||
this.state.applications
|
this.state.listRegistrationApplicationsResponse
|
||||||
|
.map(r => r.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,22 +1,25 @@
|
||||||
|
import { None, Option, Some } from "@sniptt/monads";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommentReportResponse,
|
CommentReportResponse,
|
||||||
CommentReportView,
|
CommentReportView,
|
||||||
|
GetSiteResponse,
|
||||||
ListCommentReports,
|
ListCommentReports,
|
||||||
ListCommentReportsResponse,
|
ListCommentReportsResponse,
|
||||||
ListPostReports,
|
ListPostReports,
|
||||||
ListPostReportsResponse,
|
ListPostReportsResponse,
|
||||||
PostReportResponse,
|
PostReportResponse,
|
||||||
PostReportView,
|
PostReportView,
|
||||||
SiteView,
|
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { InitialFetchRequest } from "../../interfaces";
|
import { InitialFetchRequest } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
|
@ -25,9 +28,7 @@ import {
|
||||||
updateCommentReportRes,
|
updateCommentReportRes,
|
||||||
updatePostReportRes,
|
updatePostReportRes,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { CommentReport } from "../comment/comment-report";
|
import { CommentReport } from "../comment/comment-report";
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
|
@ -59,27 +60,31 @@ type ItemType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
interface ReportsState {
|
interface ReportsState {
|
||||||
|
listCommentReportsResponse: Option<ListCommentReportsResponse>;
|
||||||
|
listPostReportsResponse: Option<ListPostReportsResponse>;
|
||||||
unreadOrAll: UnreadOrAll;
|
unreadOrAll: UnreadOrAll;
|
||||||
messageType: MessageType;
|
messageType: MessageType;
|
||||||
commentReports: CommentReportView[];
|
|
||||||
postReports: PostReportView[];
|
|
||||||
combined: ItemType[];
|
combined: ItemType[];
|
||||||
|
siteRes: GetSiteResponse;
|
||||||
page: number;
|
page: number;
|
||||||
site_view: SiteView;
|
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Reports extends Component<any, ReportsState> {
|
export class Reports extends Component<any, ReportsState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(
|
||||||
|
this.context,
|
||||||
|
ListCommentReportsResponse,
|
||||||
|
ListPostReportsResponse
|
||||||
|
);
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: ReportsState = {
|
private emptyState: ReportsState = {
|
||||||
|
listCommentReportsResponse: None,
|
||||||
|
listPostReportsResponse: None,
|
||||||
unreadOrAll: UnreadOrAll.Unread,
|
unreadOrAll: UnreadOrAll.Unread,
|
||||||
messageType: MessageType.All,
|
messageType: MessageType.All,
|
||||||
commentReports: [],
|
|
||||||
postReports: [],
|
|
||||||
combined: [],
|
combined: [],
|
||||||
page: 1,
|
page: 1,
|
||||||
site_view: this.isoData.site_res.site_view,
|
siteRes: this.isoData.site_res,
|
||||||
loading: true,
|
loading: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -89,7 +94,7 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
this.handlePageChange = this.handlePageChange.bind(this);
|
this.handlePageChange = this.handlePageChange.bind(this);
|
||||||
|
|
||||||
if (!UserService.Instance.myUserInfo && isBrowser()) {
|
if (UserService.Instance.myUserInfo.isNone() && 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`);
|
||||||
}
|
}
|
||||||
|
@ -99,9 +104,12 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
|
|
||||||
// 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.commentReports =
|
this.state.listCommentReportsResponse = Some(
|
||||||
this.isoData.routeData[0].comment_reports || [];
|
this.isoData.routeData[0] as ListCommentReportsResponse
|
||||||
this.state.postReports = this.isoData.routeData[1].post_reports || [];
|
);
|
||||||
|
this.state.listPostReportsResponse = Some(
|
||||||
|
this.isoData.routeData[1] as ListPostReportsResponse
|
||||||
|
);
|
||||||
this.state.combined = this.buildCombined();
|
this.state.combined = this.buildCombined();
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -116,9 +124,17 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `@${
|
return this.state.siteRes.site_view.match({
|
||||||
UserService.Instance.myUserInfo.local_user_view.person.name
|
some: siteView =>
|
||||||
} ${i18n.t("reports")} - ${this.state.site_view.site.name}`;
|
UserService.Instance.myUserInfo.match({
|
||||||
|
some: mui =>
|
||||||
|
`@${mui.local_user_view.person.name} ${i18n.t("reports")} - ${
|
||||||
|
siteView.site.name
|
||||||
|
}`,
|
||||||
|
none: "",
|
||||||
|
}),
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -134,6 +150,8 @@ 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 class="mb-2">{i18n.t("reports")}</h5>
|
<h5 class="mb-2">{i18n.t("reports")}</h5>
|
||||||
{this.selects()}
|
{this.selects()}
|
||||||
|
@ -260,12 +278,14 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
buildCombined(): ItemType[] {
|
buildCombined(): ItemType[] {
|
||||||
let comments: ItemType[] = this.state.commentReports.map(r =>
|
let comments: ItemType[] = this.state.listCommentReportsResponse
|
||||||
this.replyToReplyType(r)
|
.map(r => r.comment_reports)
|
||||||
);
|
.unwrapOr([])
|
||||||
let posts: ItemType[] = this.state.postReports.map(r =>
|
.map(r => this.replyToReplyType(r));
|
||||||
this.mentionToReplyType(r)
|
let posts: ItemType[] = this.state.listPostReportsResponse
|
||||||
);
|
.map(r => r.post_reports)
|
||||||
|
.unwrapOr([])
|
||||||
|
.map(r => this.mentionToReplyType(r));
|
||||||
|
|
||||||
return [...comments, ...posts].sort((a, b) =>
|
return [...comments, ...posts].sort((a, b) =>
|
||||||
b.published.localeCompare(a.published)
|
b.published.localeCompare(a.published)
|
||||||
|
@ -299,29 +319,35 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
commentReports() {
|
commentReports() {
|
||||||
return (
|
return this.state.listCommentReportsResponse.match({
|
||||||
<div>
|
some: res => (
|
||||||
{this.state.commentReports.map(cr => (
|
<div>
|
||||||
<>
|
{res.comment_reports.map(cr => (
|
||||||
<hr />
|
<>
|
||||||
<CommentReport key={cr.comment_report.id} report={cr} />
|
<hr />
|
||||||
</>
|
<CommentReport key={cr.comment_report.id} report={cr} />
|
||||||
))}
|
</>
|
||||||
</div>
|
))}
|
||||||
);
|
</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
postReports() {
|
postReports() {
|
||||||
return (
|
return this.state.listPostReportsResponse.match({
|
||||||
<div>
|
some: res => (
|
||||||
{this.state.postReports.map(pr => (
|
<div>
|
||||||
<>
|
{res.post_reports.map(pr => (
|
||||||
<hr />
|
<>
|
||||||
<PostReport key={pr.post_report.id} report={pr} />
|
<hr />
|
||||||
</>
|
<PostReport key={pr.post_report.id} report={pr} />
|
||||||
))}
|
</>
|
||||||
</div>
|
))}
|
||||||
);
|
</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePageChange(page: number) {
|
handlePageChange(page: number) {
|
||||||
|
@ -346,47 +372,61 @@ 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 commentReportsForm: ListCommentReports = {
|
let unresolved_only = Some(true);
|
||||||
|
let page = Some(1);
|
||||||
|
let limit = Some(fetchLimit);
|
||||||
|
let community_id = None;
|
||||||
|
let auth = req.auth.unwrap();
|
||||||
|
|
||||||
|
let commentReportsForm = new ListCommentReports({
|
||||||
// TODO community_id
|
// TODO community_id
|
||||||
unresolved_only: true,
|
unresolved_only,
|
||||||
page: 1,
|
community_id,
|
||||||
limit: fetchLimit,
|
page,
|
||||||
auth: req.auth,
|
limit,
|
||||||
};
|
auth,
|
||||||
|
});
|
||||||
promises.push(req.client.listCommentReports(commentReportsForm));
|
promises.push(req.client.listCommentReports(commentReportsForm));
|
||||||
|
|
||||||
let postReportsForm: ListPostReports = {
|
let postReportsForm = new ListPostReports({
|
||||||
// TODO community_id
|
// TODO community_id
|
||||||
unresolved_only: true,
|
unresolved_only,
|
||||||
page: 1,
|
community_id,
|
||||||
limit: fetchLimit,
|
page,
|
||||||
auth: req.auth,
|
limit,
|
||||||
};
|
auth,
|
||||||
|
});
|
||||||
promises.push(req.client.listPostReports(postReportsForm));
|
promises.push(req.client.listPostReports(postReportsForm));
|
||||||
|
|
||||||
return promises;
|
return promises;
|
||||||
}
|
}
|
||||||
|
|
||||||
refetch() {
|
refetch() {
|
||||||
let unresolved_only = this.state.unreadOrAll == UnreadOrAll.Unread;
|
let unresolved_only = Some(this.state.unreadOrAll == UnreadOrAll.Unread);
|
||||||
let commentReportsForm: ListCommentReports = {
|
let community_id = None;
|
||||||
// TODO community_id
|
let page = Some(this.state.page);
|
||||||
|
let limit = Some(fetchLimit);
|
||||||
|
|
||||||
|
let commentReportsForm = new ListCommentReports({
|
||||||
unresolved_only,
|
unresolved_only,
|
||||||
page: this.state.page,
|
// TODO community_id
|
||||||
limit: fetchLimit,
|
community_id,
|
||||||
auth: authField(),
|
page,
|
||||||
};
|
limit,
|
||||||
|
auth: auth().unwrap(),
|
||||||
|
});
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.listCommentReports(commentReportsForm)
|
wsClient.listCommentReports(commentReportsForm)
|
||||||
);
|
);
|
||||||
|
|
||||||
let postReportsForm: ListPostReports = {
|
let postReportsForm = new ListPostReports({
|
||||||
// TODO community_id
|
|
||||||
unresolved_only,
|
unresolved_only,
|
||||||
page: this.state.page,
|
// TODO community_id
|
||||||
limit: fetchLimit,
|
community_id,
|
||||||
auth: authField(),
|
page,
|
||||||
};
|
limit,
|
||||||
|
auth: auth().unwrap(),
|
||||||
|
});
|
||||||
WebSocketService.Instance.send(wsClient.listPostReports(postReportsForm));
|
WebSocketService.Instance.send(wsClient.listPostReports(postReportsForm));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,8 +439,11 @@ 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>(msg).data;
|
let data = wsJsonToRes<ListCommentReportsResponse>(
|
||||||
this.state.commentReports = data.comment_reports;
|
msg,
|
||||||
|
ListCommentReportsResponse
|
||||||
|
);
|
||||||
|
this.state.listCommentReportsResponse = Some(data);
|
||||||
this.state.combined = this.buildCombined();
|
this.state.combined = this.buildCombined();
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
// this.sendUnreadCount();
|
// this.sendUnreadCount();
|
||||||
|
@ -408,8 +451,11 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.ListPostReports) {
|
} else if (op == UserOperation.ListPostReports) {
|
||||||
let data = wsJsonToRes<ListPostReportsResponse>(msg).data;
|
let data = wsJsonToRes<ListPostReportsResponse>(
|
||||||
this.state.postReports = data.post_reports;
|
msg,
|
||||||
|
ListPostReportsResponse
|
||||||
|
);
|
||||||
|
this.state.listPostReportsResponse = Some(data);
|
||||||
this.state.combined = this.buildCombined();
|
this.state.combined = this.buildCombined();
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
// this.sendUnreadCount();
|
// this.sendUnreadCount();
|
||||||
|
@ -417,8 +463,11 @@ export class Reports extends Component<any, ReportsState> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (op == UserOperation.ResolvePostReport) {
|
} else if (op == UserOperation.ResolvePostReport) {
|
||||||
let data = wsJsonToRes<PostReportResponse>(msg).data;
|
let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
|
||||||
updatePostReportRes(data.post_report_view, this.state.postReports);
|
updatePostReportRes(
|
||||||
|
data.post_report_view,
|
||||||
|
this.state.listPostReportsResponse.map(r => r.post_reports).unwrapOr([])
|
||||||
|
);
|
||||||
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) {
|
||||||
urcs.next(urcs.getValue() - 1);
|
urcs.next(urcs.getValue() - 1);
|
||||||
|
@ -427,10 +476,12 @@ 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).data;
|
let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
|
||||||
updateCommentReportRes(
|
updateCommentReportRes(
|
||||||
data.comment_report_view,
|
data.comment_report_view,
|
||||||
this.state.commentReports
|
this.state.listCommentReportsResponse
|
||||||
|
.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) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { None, Option, Some } from "@sniptt/monads";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
BlockCommunity,
|
BlockCommunity,
|
||||||
|
@ -15,19 +16,23 @@ import {
|
||||||
PersonViewSafe,
|
PersonViewSafe,
|
||||||
SaveUserSettings,
|
SaveUserSettings,
|
||||||
SortType,
|
SortType,
|
||||||
|
toUndefined,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
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 {
|
||||||
authField,
|
auth,
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
choicesConfig,
|
choicesConfig,
|
||||||
communitySelectName,
|
communitySelectName,
|
||||||
communityToChoice,
|
communityToChoice,
|
||||||
debounce,
|
debounce,
|
||||||
elementUrl,
|
elementUrl,
|
||||||
|
enableNsfw,
|
||||||
fetchCommunities,
|
fetchCommunities,
|
||||||
fetchThemeList,
|
fetchThemeList,
|
||||||
fetchUsers,
|
fetchUsers,
|
||||||
|
@ -44,9 +49,7 @@ import {
|
||||||
updateCommunityBlock,
|
updateCommunityBlock,
|
||||||
updatePersonBlock,
|
updatePersonBlock,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
import { Icon, Spinner } from "../common/icon";
|
import { Icon, Spinner } from "../common/icon";
|
||||||
|
@ -65,20 +68,19 @@ if (isBrowser()) {
|
||||||
interface SettingsState {
|
interface SettingsState {
|
||||||
saveUserSettingsForm: SaveUserSettings;
|
saveUserSettingsForm: SaveUserSettings;
|
||||||
changePasswordForm: ChangePassword;
|
changePasswordForm: ChangePassword;
|
||||||
saveUserSettingsLoading: boolean;
|
|
||||||
changePasswordLoading: boolean;
|
|
||||||
deleteAccountLoading: boolean;
|
|
||||||
deleteAccountShowConfirm: boolean;
|
|
||||||
deleteAccountForm: DeleteAccount;
|
deleteAccountForm: DeleteAccount;
|
||||||
personBlocks: PersonBlockView[];
|
personBlocks: PersonBlockView[];
|
||||||
blockPersonId: number;
|
blockPerson: Option<PersonViewSafe>;
|
||||||
blockPerson?: PersonViewSafe;
|
|
||||||
communityBlocks: CommunityBlockView[];
|
communityBlocks: CommunityBlockView[];
|
||||||
blockCommunityId: number;
|
blockCommunityId: number;
|
||||||
blockCommunity?: CommunityView;
|
blockCommunity?: CommunityView;
|
||||||
currentTab: string;
|
currentTab: string;
|
||||||
siteRes: GetSiteResponse;
|
|
||||||
themeList: string[];
|
themeList: string[];
|
||||||
|
saveUserSettingsLoading: boolean;
|
||||||
|
changePasswordLoading: boolean;
|
||||||
|
deleteAccountLoading: boolean;
|
||||||
|
deleteAccountShowConfirm: boolean;
|
||||||
|
siteRes: GetSiteResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Settings extends Component<any, SettingsState> {
|
export class Settings extends Component<any, SettingsState> {
|
||||||
|
@ -87,25 +89,43 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
private blockCommunityChoices: any;
|
private blockCommunityChoices: any;
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: SettingsState = {
|
private emptyState: SettingsState = {
|
||||||
saveUserSettingsForm: {
|
saveUserSettingsForm: new SaveUserSettings({
|
||||||
auth: authField(false),
|
show_nsfw: None,
|
||||||
},
|
show_scores: None,
|
||||||
changePasswordForm: {
|
show_avatars: None,
|
||||||
new_password: null,
|
show_read_posts: None,
|
||||||
new_password_verify: null,
|
show_bot_accounts: None,
|
||||||
old_password: null,
|
show_new_post_notifs: None,
|
||||||
auth: authField(false),
|
default_sort_type: None,
|
||||||
},
|
default_listing_type: None,
|
||||||
saveUserSettingsLoading: null,
|
theme: None,
|
||||||
|
lang: 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,
|
||||||
changePasswordLoading: false,
|
changePasswordLoading: false,
|
||||||
deleteAccountLoading: null,
|
deleteAccountLoading: false,
|
||||||
deleteAccountShowConfirm: false,
|
deleteAccountShowConfirm: false,
|
||||||
deleteAccountForm: {
|
deleteAccountForm: new DeleteAccount({
|
||||||
password: null,
|
password: undefined,
|
||||||
auth: authField(false),
|
auth: undefined,
|
||||||
},
|
}),
|
||||||
personBlocks: [],
|
personBlocks: [],
|
||||||
blockPersonId: 0,
|
blockPerson: None,
|
||||||
communityBlocks: [],
|
communityBlocks: [],
|
||||||
blockCommunityId: 0,
|
blockCommunityId: 0,
|
||||||
currentTab: "settings",
|
currentTab: "settings",
|
||||||
|
@ -154,7 +174,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={this.documentTitle}
|
description={Some(this.documentTitle)}
|
||||||
image={this.state.saveUserSettingsForm.avatar}
|
image={this.state.saveUserSettingsForm.avatar}
|
||||||
/>
|
/>
|
||||||
<ul class="nav nav-tabs mb-2">
|
<ul class="nav nav-tabs mb-2">
|
||||||
|
@ -342,14 +362,17 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
<select
|
<select
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="block-person-filter"
|
id="block-person-filter"
|
||||||
value={this.state.blockPersonId}
|
value={this.state.blockPerson.map(p => p.person.id).unwrapOr(0)}
|
||||||
>
|
>
|
||||||
<option value="0">—</option>
|
<option value="0">—</option>
|
||||||
{this.state.blockPerson && (
|
{this.state.blockPerson.match({
|
||||||
<option value={this.state.blockPerson.person.id}>
|
some: personView => (
|
||||||
{personSelectName(this.state.blockPerson)}
|
<option value={personView.person.id}>
|
||||||
</option>
|
{personSelectName(personView)}
|
||||||
)}
|
</option>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -431,7 +454,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
placeholder={i18n.t("optional")}
|
placeholder={i18n.t("optional")}
|
||||||
value={this.state.saveUserSettingsForm.display_name}
|
value={toUndefined(
|
||||||
|
this.state.saveUserSettingsForm.display_name
|
||||||
|
)}
|
||||||
onInput={linkEvent(this, this.handleDisplayNameChange)}
|
onInput={linkEvent(this, this.handleDisplayNameChange)}
|
||||||
pattern="^(?!@)(.+)$"
|
pattern="^(?!@)(.+)$"
|
||||||
minLength={3}
|
minLength={3}
|
||||||
|
@ -446,7 +471,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.saveUserSettingsForm.bio}
|
initialContent={this.state.saveUserSettingsForm.bio}
|
||||||
onContentChange={this.handleBioChange}
|
onContentChange={this.handleBioChange}
|
||||||
maxLength={300}
|
maxLength={Some(300)}
|
||||||
|
placeholder={None}
|
||||||
|
buttonTitle={None}
|
||||||
hideNavigationWarnings
|
hideNavigationWarnings
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -461,7 +488,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
id="user-email"
|
id="user-email"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
placeholder={i18n.t("optional")}
|
placeholder={i18n.t("optional")}
|
||||||
value={this.state.saveUserSettingsForm.email}
|
value={toUndefined(this.state.saveUserSettingsForm.email)}
|
||||||
onInput={linkEvent(this, this.handleEmailChange)}
|
onInput={linkEvent(this, this.handleEmailChange)}
|
||||||
minLength={3}
|
minLength={3}
|
||||||
/>
|
/>
|
||||||
|
@ -479,7 +506,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
placeholder="@user:example.com"
|
placeholder="@user:example.com"
|
||||||
value={this.state.saveUserSettingsForm.matrix_user_id}
|
value={toUndefined(
|
||||||
|
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,}$"
|
||||||
/>
|
/>
|
||||||
|
@ -515,7 +544,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select
|
<select
|
||||||
id="user-language"
|
id="user-language"
|
||||||
value={this.state.saveUserSettingsForm.lang}
|
value={toUndefined(this.state.saveUserSettingsForm.lang)}
|
||||||
onChange={linkEvent(this, this.handleLangChange)}
|
onChange={linkEvent(this, this.handleLangChange)}
|
||||||
class="custom-select w-auto"
|
class="custom-select w-auto"
|
||||||
>
|
>
|
||||||
|
@ -541,7 +570,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<select
|
<select
|
||||||
id="user-theme"
|
id="user-theme"
|
||||||
value={this.state.saveUserSettingsForm.theme}
|
value={toUndefined(this.state.saveUserSettingsForm.theme)}
|
||||||
onChange={linkEvent(this, this.handleThemeChange)}
|
onChange={linkEvent(this, this.handleThemeChange)}
|
||||||
class="custom-select w-auto"
|
class="custom-select w-auto"
|
||||||
>
|
>
|
||||||
|
@ -561,7 +590,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
<ListingTypeSelect
|
<ListingTypeSelect
|
||||||
type_={
|
type_={
|
||||||
Object.values(ListingType)[
|
Object.values(ListingType)[
|
||||||
this.state.saveUserSettingsForm.default_listing_type
|
this.state.saveUserSettingsForm.default_listing_type.unwrapOr(
|
||||||
|
1
|
||||||
|
)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
showLocal={showLocal(this.isoData)}
|
showLocal={showLocal(this.isoData)}
|
||||||
|
@ -576,21 +607,25 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
<SortSelect
|
<SortSelect
|
||||||
sort={
|
sort={
|
||||||
Object.values(SortType)[
|
Object.values(SortType)[
|
||||||
this.state.saveUserSettingsForm.default_sort_type
|
this.state.saveUserSettingsForm.default_sort_type.unwrapOr(
|
||||||
|
0
|
||||||
|
)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
onChange={this.handleSortTypeChange}
|
onChange={this.handleSortTypeChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{this.state.siteRes.site_view.site.enable_nsfw && (
|
{enableNsfw(this.state.siteRes) && (
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input
|
<input
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
id="user-show-nsfw"
|
id="user-show-nsfw"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.saveUserSettingsForm.show_nsfw}
|
checked={toUndefined(
|
||||||
|
this.state.saveUserSettingsForm.show_nsfw
|
||||||
|
)}
|
||||||
onChange={linkEvent(this, this.handleShowNsfwChange)}
|
onChange={linkEvent(this, this.handleShowNsfwChange)}
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" htmlFor="user-show-nsfw">
|
<label class="form-check-label" htmlFor="user-show-nsfw">
|
||||||
|
@ -605,7 +640,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
id="user-show-scores"
|
id="user-show-scores"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.saveUserSettingsForm.show_scores}
|
checked={toUndefined(
|
||||||
|
this.state.saveUserSettingsForm.show_scores
|
||||||
|
)}
|
||||||
onChange={linkEvent(this, this.handleShowScoresChange)}
|
onChange={linkEvent(this, this.handleShowScoresChange)}
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" htmlFor="user-show-scores">
|
<label class="form-check-label" htmlFor="user-show-scores">
|
||||||
|
@ -619,7 +656,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
id="user-show-avatars"
|
id="user-show-avatars"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.saveUserSettingsForm.show_avatars}
|
checked={toUndefined(
|
||||||
|
this.state.saveUserSettingsForm.show_avatars
|
||||||
|
)}
|
||||||
onChange={linkEvent(this, this.handleShowAvatarsChange)}
|
onChange={linkEvent(this, this.handleShowAvatarsChange)}
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" htmlFor="user-show-avatars">
|
<label class="form-check-label" htmlFor="user-show-avatars">
|
||||||
|
@ -633,7 +672,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
id="user-bot-account"
|
id="user-bot-account"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.saveUserSettingsForm.bot_account}
|
checked={toUndefined(
|
||||||
|
this.state.saveUserSettingsForm.bot_account
|
||||||
|
)}
|
||||||
onChange={linkEvent(this, this.handleBotAccount)}
|
onChange={linkEvent(this, this.handleBotAccount)}
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" htmlFor="user-bot-account">
|
<label class="form-check-label" htmlFor="user-bot-account">
|
||||||
|
@ -647,7 +688,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
id="user-show-bot-accounts"
|
id="user-show-bot-accounts"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.saveUserSettingsForm.show_bot_accounts}
|
checked={toUndefined(
|
||||||
|
this.state.saveUserSettingsForm.show_bot_accounts
|
||||||
|
)}
|
||||||
onChange={linkEvent(this, this.handleShowBotAccounts)}
|
onChange={linkEvent(this, this.handleShowBotAccounts)}
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" htmlFor="user-show-bot-accounts">
|
<label class="form-check-label" htmlFor="user-show-bot-accounts">
|
||||||
|
@ -661,7 +704,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
id="user-show-read-posts"
|
id="user-show-read-posts"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.saveUserSettingsForm.show_read_posts}
|
checked={toUndefined(
|
||||||
|
this.state.saveUserSettingsForm.show_read_posts
|
||||||
|
)}
|
||||||
onChange={linkEvent(this, this.handleReadPosts)}
|
onChange={linkEvent(this, this.handleReadPosts)}
|
||||||
/>
|
/>
|
||||||
<label class="form-check-label" htmlFor="user-show-read-posts">
|
<label class="form-check-label" htmlFor="user-show-read-posts">
|
||||||
|
@ -675,7 +720,9 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
class="form-check-input"
|
class="form-check-input"
|
||||||
id="user-show-new-post-notifs"
|
id="user-show-new-post-notifs"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.saveUserSettingsForm.show_new_post_notifs}
|
checked={toUndefined(
|
||||||
|
this.state.saveUserSettingsForm.show_new_post_notifs
|
||||||
|
)}
|
||||||
onChange={linkEvent(this, this.handleShowNewPostNotifs)}
|
onChange={linkEvent(this, this.handleShowNewPostNotifs)}
|
||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
|
@ -693,9 +740,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={
|
checked={toUndefined(
|
||||||
this.state.saveUserSettingsForm.send_notifications_to_email
|
this.state.saveUserSettingsForm.send_notifications_to_email
|
||||||
}
|
)}
|
||||||
onChange={linkEvent(
|
onChange={linkEvent(
|
||||||
this,
|
this,
|
||||||
this.handleSendNotificationsToEmailChange
|
this.handleSendNotificationsToEmailChange
|
||||||
|
@ -844,31 +891,31 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
|
|
||||||
handleBlockPerson(personId: number) {
|
handleBlockPerson(personId: number) {
|
||||||
if (personId != 0) {
|
if (personId != 0) {
|
||||||
let blockUserForm: BlockPerson = {
|
let blockUserForm = new BlockPerson({
|
||||||
person_id: personId,
|
person_id: personId,
|
||||||
block: true,
|
block: true,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
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: BlockPerson = {
|
let blockUserForm = new BlockPerson({
|
||||||
person_id: i.recipientId,
|
person_id: i.recipientId,
|
||||||
block: false,
|
block: false,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
|
WebSocketService.Instance.send(wsClient.blockPerson(blockUserForm));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBlockCommunity(community_id: number) {
|
handleBlockCommunity(community_id: number) {
|
||||||
if (community_id != 0) {
|
if (community_id != 0) {
|
||||||
let blockCommunityForm: BlockCommunity = {
|
let blockCommunityForm = new BlockCommunity({
|
||||||
community_id,
|
community_id,
|
||||||
block: true,
|
block: true,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.blockCommunity(blockCommunityForm)
|
wsClient.blockCommunity(blockCommunityForm)
|
||||||
);
|
);
|
||||||
|
@ -876,126 +923,133 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleUnblockCommunity(i: { ctx: Settings; communityId: number }) {
|
handleUnblockCommunity(i: { ctx: Settings; communityId: number }) {
|
||||||
let blockCommunityForm: BlockCommunity = {
|
let blockCommunityForm = new BlockCommunity({
|
||||||
community_id: i.communityId,
|
community_id: i.communityId,
|
||||||
block: false,
|
block: false,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.blockCommunity(blockCommunityForm));
|
WebSocketService.Instance.send(wsClient.blockCommunity(blockCommunityForm));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleShowNsfwChange(i: Settings, event: any) {
|
handleShowNsfwChange(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.show_nsfw = event.target.checked;
|
i.state.saveUserSettingsForm.show_nsfw = Some(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 = event.target.checked;
|
i.state.saveUserSettingsForm.show_avatars = Some(event.target.checked);
|
||||||
UserService.Instance.myUserInfo.local_user_view.local_user.show_avatars =
|
UserService.Instance.myUserInfo.match({
|
||||||
event.target.checked; // Just for instant updates
|
some: mui =>
|
||||||
|
(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 = event.target.checked;
|
i.state.saveUserSettingsForm.bot_account = Some(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 = event.target.checked;
|
i.state.saveUserSettingsForm.show_bot_accounts = Some(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 = event.target.checked;
|
i.state.saveUserSettingsForm.show_read_posts = Some(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 = event.target.checked;
|
i.state.saveUserSettingsForm.show_new_post_notifs = Some(
|
||||||
|
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 = event.target.checked;
|
i.state.saveUserSettingsForm.show_scores = Some(event.target.checked);
|
||||||
UserService.Instance.myUserInfo.local_user_view.local_user.show_scores =
|
UserService.Instance.myUserInfo.match({
|
||||||
event.target.checked; // Just for instant updates
|
some: mui =>
|
||||||
|
(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 =
|
i.state.saveUserSettingsForm.send_notifications_to_email = Some(
|
||||||
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 = event.target.value;
|
i.state.saveUserSettingsForm.theme = Some(event.target.value);
|
||||||
setTheme(event.target.value, true);
|
setTheme(event.target.value, true);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLangChange(i: Settings, event: any) {
|
handleLangChange(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.lang = event.target.value;
|
i.state.saveUserSettingsForm.lang = Some(event.target.value);
|
||||||
i18n.changeLanguage(getLanguages(i.state.saveUserSettingsForm.lang)[0]);
|
i18n.changeLanguage(
|
||||||
|
getLanguages(i.state.saveUserSettingsForm.lang.unwrap())[0]
|
||||||
|
);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSortTypeChange(val: SortType) {
|
handleSortTypeChange(val: SortType) {
|
||||||
this.state.saveUserSettingsForm.default_sort_type =
|
this.state.saveUserSettingsForm.default_sort_type = Some(
|
||||||
Object.keys(SortType).indexOf(val);
|
Object.keys(SortType).indexOf(val)
|
||||||
|
);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleListingTypeChange(val: ListingType) {
|
handleListingTypeChange(val: ListingType) {
|
||||||
this.state.saveUserSettingsForm.default_listing_type =
|
this.state.saveUserSettingsForm.default_listing_type = Some(
|
||||||
Object.keys(ListingType).indexOf(val);
|
Object.keys(ListingType).indexOf(val)
|
||||||
|
);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEmailChange(i: Settings, event: any) {
|
handleEmailChange(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.email = event.target.value;
|
i.state.saveUserSettingsForm.email = Some(event.target.value);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBioChange(val: string) {
|
handleBioChange(val: string) {
|
||||||
this.state.saveUserSettingsForm.bio = val;
|
this.state.saveUserSettingsForm.bio = Some(val);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAvatarUpload(url: string) {
|
handleAvatarUpload(url: string) {
|
||||||
this.state.saveUserSettingsForm.avatar = url;
|
this.state.saveUserSettingsForm.avatar = Some(url);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleAvatarRemove() {
|
handleAvatarRemove() {
|
||||||
this.state.saveUserSettingsForm.avatar = "";
|
this.state.saveUserSettingsForm.avatar = Some("");
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerUpload(url: string) {
|
handleBannerUpload(url: string) {
|
||||||
this.state.saveUserSettingsForm.banner = url;
|
this.state.saveUserSettingsForm.banner = Some(url);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleBannerRemove() {
|
handleBannerRemove() {
|
||||||
this.state.saveUserSettingsForm.banner = "";
|
this.state.saveUserSettingsForm.banner = Some("");
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDisplayNameChange(i: Settings, event: any) {
|
handleDisplayNameChange(i: Settings, event: any) {
|
||||||
i.state.saveUserSettingsForm.display_name = event.target.value;
|
i.state.saveUserSettingsForm.display_name = Some(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 = event.target.value;
|
i.state.saveUserSettingsForm.matrix_user_id = Some(event.target.value);
|
||||||
if (
|
|
||||||
i.state.saveUserSettingsForm.matrix_user_id == "" &&
|
|
||||||
!UserService.Instance.myUserInfo.local_user_view.person.matrix_user_id
|
|
||||||
) {
|
|
||||||
i.state.saveUserSettingsForm.matrix_user_id = undefined;
|
|
||||||
}
|
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1026,6 +1080,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
handleSaveSettingsSubmit(i: Settings, event: any) {
|
handleSaveSettingsSubmit(i: Settings, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.saveUserSettingsLoading = true;
|
i.state.saveUserSettingsLoading = true;
|
||||||
|
i.state.saveUserSettingsForm.auth = auth().unwrap();
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
|
@ -1036,6 +1091,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
handleChangePasswordSubmit(i: Settings, event: any) {
|
handleChangePasswordSubmit(i: Settings, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.changePasswordLoading = true;
|
i.state.changePasswordLoading = true;
|
||||||
|
i.state.changePasswordForm.auth = auth().unwrap();
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
|
@ -1057,6 +1113,7 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
handleDeleteAccount(i: Settings, event: any) {
|
handleDeleteAccount(i: Settings, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
i.state.deleteAccountLoading = true;
|
i.state.deleteAccountLoading = true;
|
||||||
|
i.state.deleteAccountForm.auth = auth().unwrap();
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
|
@ -1074,36 +1131,55 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
setUserInfo() {
|
setUserInfo() {
|
||||||
let luv = UserService.Instance.myUserInfo.local_user_view;
|
UserService.Instance.myUserInfo.match({
|
||||||
this.state.saveUserSettingsForm.show_nsfw = luv.local_user.show_nsfw;
|
some: mui => {
|
||||||
this.state.saveUserSettingsForm.theme = luv.local_user.theme
|
let luv = mui.local_user_view;
|
||||||
? luv.local_user.theme
|
this.state.saveUserSettingsForm.show_nsfw = Some(
|
||||||
: "browser";
|
luv.local_user.show_nsfw
|
||||||
this.state.saveUserSettingsForm.default_sort_type =
|
);
|
||||||
luv.local_user.default_sort_type;
|
this.state.saveUserSettingsForm.theme = Some(
|
||||||
this.state.saveUserSettingsForm.default_listing_type =
|
luv.local_user.theme ? luv.local_user.theme : "browser"
|
||||||
luv.local_user.default_listing_type;
|
);
|
||||||
this.state.saveUserSettingsForm.lang = luv.local_user.lang;
|
this.state.saveUserSettingsForm.default_sort_type = Some(
|
||||||
this.state.saveUserSettingsForm.avatar = luv.person.avatar;
|
luv.local_user.default_sort_type
|
||||||
this.state.saveUserSettingsForm.banner = luv.person.banner;
|
);
|
||||||
this.state.saveUserSettingsForm.display_name = luv.person.display_name;
|
this.state.saveUserSettingsForm.default_listing_type = Some(
|
||||||
this.state.saveUserSettingsForm.show_avatars = luv.local_user.show_avatars;
|
luv.local_user.default_listing_type
|
||||||
this.state.saveUserSettingsForm.bot_account = luv.person.bot_account;
|
);
|
||||||
this.state.saveUserSettingsForm.show_bot_accounts =
|
this.state.saveUserSettingsForm.lang = Some(luv.local_user.lang);
|
||||||
luv.local_user.show_bot_accounts;
|
this.state.saveUserSettingsForm.avatar = luv.person.avatar;
|
||||||
this.state.saveUserSettingsForm.show_scores = luv.local_user.show_scores;
|
this.state.saveUserSettingsForm.banner = luv.person.banner;
|
||||||
this.state.saveUserSettingsForm.show_read_posts =
|
this.state.saveUserSettingsForm.display_name = luv.person.display_name;
|
||||||
luv.local_user.show_read_posts;
|
this.state.saveUserSettingsForm.show_avatars = Some(
|
||||||
this.state.saveUserSettingsForm.show_new_post_notifs =
|
luv.local_user.show_avatars
|
||||||
luv.local_user.show_new_post_notifs;
|
);
|
||||||
this.state.saveUserSettingsForm.email = luv.local_user.email;
|
this.state.saveUserSettingsForm.bot_account = Some(
|
||||||
this.state.saveUserSettingsForm.bio = luv.person.bio;
|
luv.person.bot_account
|
||||||
this.state.saveUserSettingsForm.send_notifications_to_email =
|
);
|
||||||
luv.local_user.send_notifications_to_email;
|
this.state.saveUserSettingsForm.show_bot_accounts = Some(
|
||||||
this.state.saveUserSettingsForm.matrix_user_id = luv.person.matrix_user_id;
|
luv.local_user.show_bot_accounts
|
||||||
this.state.personBlocks = UserService.Instance.myUserInfo.person_blocks;
|
);
|
||||||
this.state.communityBlocks =
|
this.state.saveUserSettingsForm.show_scores = Some(
|
||||||
UserService.Instance.myUserInfo.community_blocks;
|
luv.local_user.show_scores
|
||||||
|
);
|
||||||
|
this.state.saveUserSettingsForm.show_read_posts = Some(
|
||||||
|
luv.local_user.show_read_posts
|
||||||
|
);
|
||||||
|
this.state.saveUserSettingsForm.show_new_post_notifs = Some(
|
||||||
|
luv.local_user.show_new_post_notifs
|
||||||
|
);
|
||||||
|
this.state.saveUserSettingsForm.email = luv.local_user.email;
|
||||||
|
this.state.saveUserSettingsForm.bio = luv.person.bio;
|
||||||
|
this.state.saveUserSettingsForm.send_notifications_to_email = Some(
|
||||||
|
luv.local_user.send_notifications_to_email
|
||||||
|
);
|
||||||
|
this.state.saveUserSettingsForm.matrix_user_id =
|
||||||
|
luv.person.matrix_user_id;
|
||||||
|
this.state.personBlocks = mui.person_blocks;
|
||||||
|
this.state.communityBlocks = mui.community_blocks;
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -1118,14 +1194,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).data;
|
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
this.state.saveUserSettingsLoading = false;
|
this.state.saveUserSettingsLoading = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
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).data;
|
let data = wsJsonToRes<LoginResponse>(msg, LoginResponse);
|
||||||
UserService.Instance.login(data);
|
UserService.Instance.login(data);
|
||||||
this.state.changePasswordLoading = false;
|
this.state.changePasswordLoading = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
@ -1138,13 +1214,21 @@ export class Settings extends Component<any, SettingsState> {
|
||||||
});
|
});
|
||||||
UserService.Instance.logout();
|
UserService.Instance.logout();
|
||||||
window.location.href = "/";
|
window.location.href = "/";
|
||||||
location.reload();
|
|
||||||
} else if (op == UserOperation.BlockPerson) {
|
} else if (op == UserOperation.BlockPerson) {
|
||||||
let data = wsJsonToRes<BlockPersonResponse>(msg).data;
|
let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
|
||||||
this.setState({ personBlocks: updatePersonBlock(data) });
|
updatePersonBlock(data).match({
|
||||||
|
some: blocks => this.setState({ personBlocks: blocks }),
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
} else if (op == UserOperation.BlockCommunity) {
|
} else if (op == UserOperation.BlockCommunity) {
|
||||||
let data = wsJsonToRes<BlockCommunityResponse>(msg).data;
|
let data = wsJsonToRes<BlockCommunityResponse>(
|
||||||
this.setState({ communityBlocks: updateCommunityBlock(data) });
|
msg,
|
||||||
|
BlockCommunityResponse
|
||||||
|
);
|
||||||
|
updateCommunityBlock(data).match({
|
||||||
|
some: blocks => this.setState({ communityBlocks: blocks }),
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
import { None } from "@sniptt/monads/build";
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import {
|
import {
|
||||||
SiteView,
|
GetSiteResponse,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
VerifyEmail as VerifyEmailForm,
|
VerifyEmail as VerifyEmailForm,
|
||||||
VerifyEmailResponse,
|
VerifyEmailResponse,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
|
@ -13,15 +16,13 @@ import {
|
||||||
setIsoData,
|
setIsoData,
|
||||||
toast,
|
toast,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
verifyEmailForm: VerifyEmailForm;
|
verifyEmailForm: VerifyEmailForm;
|
||||||
site_view: SiteView;
|
siteRes: GetSiteResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class VerifyEmail extends Component<any, State> {
|
export class VerifyEmail extends Component<any, State> {
|
||||||
|
@ -29,10 +30,10 @@ export class VerifyEmail extends Component<any, State> {
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
|
|
||||||
emptyState: State = {
|
emptyState: State = {
|
||||||
verifyEmailForm: {
|
verifyEmailForm: new VerifyEmailForm({
|
||||||
token: this.props.match.params.token,
|
token: this.props.match.params.token,
|
||||||
},
|
}),
|
||||||
site_view: this.isoData.site_res.site_view,
|
siteRes: this.isoData.site_res,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -57,7 +58,10 @@ export class VerifyEmail extends Component<any, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `${i18n.t("verify_email")} - ${this.state.site_view.site.name}`;
|
return this.state.siteRes.site_view.match({
|
||||||
|
some: siteView => `${i18n.t("verify_email")} - ${siteView.site.name}`,
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -66,6 +70,8 @@ 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 class="row">
|
<div class="row">
|
||||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||||
|
@ -85,7 +91,7 @@ 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).data;
|
let data = wsJsonToRes<VerifyEmailResponse>(msg, VerifyEmailResponse);
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("email_verified"));
|
toast(i18n.t("email_verified"));
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
|
|
|
@ -1,48 +1,50 @@
|
||||||
|
import { Either, Left, None, Option, Right, Some } from "@sniptt/monads";
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommunityView,
|
|
||||||
GetCommunity,
|
GetCommunity,
|
||||||
GetCommunityResponse,
|
GetCommunityResponse,
|
||||||
|
GetSiteResponse,
|
||||||
ListCommunities,
|
ListCommunities,
|
||||||
ListCommunitiesResponse,
|
ListCommunitiesResponse,
|
||||||
ListingType,
|
ListingType,
|
||||||
PostView,
|
PostView,
|
||||||
SiteView,
|
|
||||||
SortType,
|
SortType,
|
||||||
|
toOption,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { InitialFetchRequest, PostFormParams } from "shared/interfaces";
|
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 {
|
||||||
authField,
|
auth,
|
||||||
|
enableDownvotes,
|
||||||
|
enableNsfw,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
setOptionalAuth,
|
|
||||||
toast,
|
toast,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
import { PostForm } from "./post-form";
|
import { PostForm } from "./post-form";
|
||||||
|
|
||||||
interface CreatePostState {
|
interface CreatePostState {
|
||||||
site_view: SiteView;
|
listCommunitiesResponse: Option<ListCommunitiesResponse>;
|
||||||
communities: CommunityView[];
|
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);
|
private isoData = setIsoData(this.context, ListCommunitiesResponse);
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: CreatePostState = {
|
private emptyState: CreatePostState = {
|
||||||
site_view: this.isoData.site_res.site_view,
|
siteRes: this.isoData.site_res,
|
||||||
communities: [],
|
listCommunitiesResponse: None,
|
||||||
loading: true,
|
loading: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -51,7 +53,7 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
this.handlePostCreate = this.handlePostCreate.bind(this);
|
this.handlePostCreate = this.handlePostCreate.bind(this);
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
|
|
||||||
if (!UserService.Instance.myUserInfo && isBrowser()) {
|
if (UserService.Instance.myUserInfo.isNone() && 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`);
|
||||||
}
|
}
|
||||||
|
@ -61,7 +63,9 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
|
|
||||||
// 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.communities = this.isoData.routeData[0].communities;
|
this.state.listCommunitiesResponse = Some(
|
||||||
|
this.isoData.routeData[0] as ListCommunitiesResponse
|
||||||
|
);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
} else {
|
} else {
|
||||||
this.refetch();
|
this.refetch();
|
||||||
|
@ -69,27 +73,39 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
refetch() {
|
refetch() {
|
||||||
if (this.params.community_id) {
|
this.params.nameOrId.match({
|
||||||
let form: GetCommunity = {
|
some: opt =>
|
||||||
id: this.params.community_id,
|
opt.match({
|
||||||
};
|
left: name => {
|
||||||
WebSocketService.Instance.send(wsClient.getCommunity(form));
|
let form = new GetCommunity({
|
||||||
} else if (this.params.community_name) {
|
name: Some(name),
|
||||||
let form: GetCommunity = {
|
id: None,
|
||||||
name: this.params.community_name,
|
auth: auth(false).ok(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.getCommunity(form));
|
WebSocketService.Instance.send(wsClient.getCommunity(form));
|
||||||
} else {
|
},
|
||||||
let listCommunitiesForm: ListCommunities = {
|
right: id => {
|
||||||
type_: ListingType.All,
|
let form = new GetCommunity({
|
||||||
sort: SortType.TopAll,
|
id: Some(id),
|
||||||
limit: fetchLimit,
|
name: None,
|
||||||
auth: authField(false),
|
auth: auth(false).ok(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(wsClient.getCommunity(form));
|
||||||
wsClient.listCommunities(listCommunitiesForm)
|
},
|
||||||
);
|
}),
|
||||||
}
|
none: () => {
|
||||||
|
let listCommunitiesForm = new ListCommunities({
|
||||||
|
type_: Some(ListingType.All),
|
||||||
|
sort: Some(SortType.TopAll),
|
||||||
|
limit: Some(fetchLimit),
|
||||||
|
page: None,
|
||||||
|
auth: auth(false).ok(),
|
||||||
|
});
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.listCommunities(listCommunitiesForm)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -99,7 +115,10 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `${i18n.t("create_post")} - ${this.state.site_view.site.name}`;
|
return this.state.siteRes.site_view.match({
|
||||||
|
some: siteView => `${i18n.t("create_post")} - ${siteView.site.name}`,
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -108,24 +127,32 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
<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>
|
||||||
) : (
|
) : (
|
||||||
<div class="row">
|
this.state.listCommunitiesResponse.match({
|
||||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
some: res => (
|
||||||
<h5>{i18n.t("create_post")}</h5>
|
<div class="row">
|
||||||
<PostForm
|
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||||
communities={this.state.communities}
|
<h5>{i18n.t("create_post")}</h5>
|
||||||
onCreate={this.handlePostCreate}
|
<PostForm
|
||||||
params={this.params}
|
post_view={None}
|
||||||
enableDownvotes={this.state.site_view.site.enable_downvotes}
|
communities={Some(res.communities)}
|
||||||
enableNsfw={this.state.site_view.site.enable_nsfw}
|
onCreate={this.handlePostCreate}
|
||||||
/>
|
params={Some(this.params)}
|
||||||
</div>
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
</div>
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -133,36 +160,48 @@ 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(
|
||||||
|
this.prevCommunityName
|
||||||
|
);
|
||||||
|
let id = toOption(urlParams.get("community_id"))
|
||||||
|
.map(Number)
|
||||||
|
.or(this.prevCommunityId);
|
||||||
|
let nameOrId: Option<Either<string, number>>;
|
||||||
|
if (name.isSome()) {
|
||||||
|
nameOrId = Some(Left(name.unwrap()));
|
||||||
|
} else if (id.isSome()) {
|
||||||
|
nameOrId = Some(Right(id.unwrap()));
|
||||||
|
} else {
|
||||||
|
nameOrId = None;
|
||||||
|
}
|
||||||
|
|
||||||
let params: PostFormParams = {
|
let params: PostFormParams = {
|
||||||
name: urlParams.get("title"),
|
name: toOption(urlParams.get("title")),
|
||||||
community_name: urlParams.get("community_name") || this.prevCommunityName,
|
nameOrId,
|
||||||
community_id: urlParams.get("community_id")
|
body: toOption(urlParams.get("body")),
|
||||||
? Number(urlParams.get("community_id")) || this.prevCommunityId
|
url: toOption(urlParams.get("url")),
|
||||||
: null,
|
|
||||||
body: urlParams.get("body"),
|
|
||||||
url: urlParams.get("url"),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
get prevCommunityName(): string {
|
get prevCommunityName(): Option<string> {
|
||||||
if (this.props.match.params.name) {
|
if (this.props.match.params.name) {
|
||||||
return this.props.match.params.name;
|
return toOption(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 lastLocation.split("/c/")[1];
|
return toOption(lastLocation.split("/c/")[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
get prevCommunityId(): number {
|
get prevCommunityId(): Option<number> {
|
||||||
if (this.props.match.params.id) {
|
if (this.props.match.params.id) {
|
||||||
return this.props.match.params.id;
|
return toOption(this.props.match.params.id);
|
||||||
}
|
}
|
||||||
return null;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostCreate(post_view: PostView) {
|
handlePostCreate(post_view: PostView) {
|
||||||
|
@ -170,12 +209,13 @@ export class CreatePost extends Component<any, CreatePostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
static fetchInitialData(req: InitialFetchRequest): Promise<any>[] {
|
||||||
let listCommunitiesForm: ListCommunities = {
|
let listCommunitiesForm = new ListCommunities({
|
||||||
type_: ListingType.All,
|
type_: Some(ListingType.All),
|
||||||
sort: SortType.TopAll,
|
sort: Some(SortType.TopAll),
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
};
|
page: None,
|
||||||
setOptionalAuth(listCommunitiesForm, req.auth);
|
auth: req.auth,
|
||||||
|
});
|
||||||
return [req.client.listCommunities(listCommunitiesForm)];
|
return [req.client.listCommunities(listCommunitiesForm)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,13 +226,18 @@ 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>(msg).data;
|
let data = wsJsonToRes<ListCommunitiesResponse>(
|
||||||
this.state.communities = data.communities;
|
msg,
|
||||||
|
ListCommunitiesResponse
|
||||||
|
);
|
||||||
|
this.state.listCommunitiesResponse = Some(data);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.GetCommunity) {
|
} else if (op == UserOperation.GetCommunity) {
|
||||||
let data = wsJsonToRes<GetCommunityResponse>(msg).data;
|
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
||||||
this.state.communities = [data.community_view];
|
this.state.listCommunitiesResponse = Some({
|
||||||
|
communities: [data.community_view],
|
||||||
|
});
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,56 +29,71 @@ export class MetadataCard extends Component<
|
||||||
let post = this.props.post;
|
let post = this.props.post;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{post.embed_title && !this.state.expanded && (
|
{!this.state.expanded &&
|
||||||
<div class="card border-secondary mt-3 mb-2">
|
post.embed_title.match({
|
||||||
<div class="row">
|
some: embedTitle =>
|
||||||
<div class="col-12">
|
post.url.match({
|
||||||
<div class="card-body">
|
some: url => (
|
||||||
{post.name !== post.embed_title && [
|
<div class="card border-secondary mt-3 mb-2">
|
||||||
<h5 class="card-title d-inline">
|
<div class="row">
|
||||||
<a class="text-body" href={post.url} rel={relTags}>
|
<div class="col-12">
|
||||||
{post.embed_title}
|
<div class="card-body">
|
||||||
</a>
|
{post.name !== embedTitle && [
|
||||||
</h5>,
|
<h5 class="card-title d-inline">
|
||||||
<span class="d-inline-block ml-2 mb-2 small text-muted">
|
<a class="text-body" href={url} rel={relTags}>
|
||||||
<a
|
{embedTitle}
|
||||||
class="text-muted font-italic"
|
</a>
|
||||||
href={post.url}
|
</h5>,
|
||||||
rel={relTags}
|
<span class="d-inline-block ml-2 mb-2 small text-muted">
|
||||||
>
|
<a
|
||||||
{new URL(post.url).hostname}
|
class="text-muted font-italic"
|
||||||
<Icon icon="external-link" classes="ml-1" />
|
href={url}
|
||||||
</a>
|
rel={relTags}
|
||||||
</span>,
|
>
|
||||||
]}
|
{new URL(url).hostname}
|
||||||
{post.embed_description && (
|
<Icon icon="external-link" classes="ml-1" />
|
||||||
<div
|
</a>
|
||||||
className="card-text small text-muted md-div"
|
</span>,
|
||||||
dangerouslySetInnerHTML={{
|
]}
|
||||||
__html: post.embed_description,
|
{post.embed_description.match({
|
||||||
}}
|
some: desc => (
|
||||||
/>
|
<div
|
||||||
)}
|
className="card-text small text-muted md-div"
|
||||||
{post.embed_html && (
|
dangerouslySetInnerHTML={{
|
||||||
<button
|
__html: desc,
|
||||||
class="mt-2 btn btn-secondary text-monospace"
|
}}
|
||||||
onClick={linkEvent(this, this.handleIframeExpand)}
|
/>
|
||||||
data-tippy-content={i18n.t("expand_here")}
|
),
|
||||||
>
|
none: <></>,
|
||||||
{this.state.expanded ? "-" : "+"}
|
})}
|
||||||
</button>
|
{post.embed_html.isSome() && (
|
||||||
)}
|
<button
|
||||||
</div>
|
class="mt-2 btn btn-secondary text-monospace"
|
||||||
</div>
|
onClick={linkEvent(this, this.handleIframeExpand)}
|
||||||
</div>
|
data-tippy-content={i18n.t("expand_here")}
|
||||||
</div>
|
>
|
||||||
)}
|
{this.state.expanded ? "-" : "+"}
|
||||||
{this.state.expanded && (
|
</button>
|
||||||
<div
|
)}
|
||||||
class="mt-3 mb-2"
|
</div>
|
||||||
dangerouslySetInnerHTML={{ __html: post.embed_html }}
|
</div>
|
||||||
/>
|
</div>
|
||||||
)}
|
</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
}),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
|
{this.state.expanded &&
|
||||||
|
post.embed_html.match({
|
||||||
|
some: html => (
|
||||||
|
<div
|
||||||
|
class="mt-3 mb-2"
|
||||||
|
dangerouslySetInnerHTML={{ __html: html }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
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";
|
||||||
|
@ -12,7 +13,10 @@ import {
|
||||||
SearchResponse,
|
SearchResponse,
|
||||||
SearchType,
|
SearchType,
|
||||||
SortType,
|
SortType,
|
||||||
|
toUndefined,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { pictrsUri } from "../../env";
|
import { pictrsUri } from "../../env";
|
||||||
|
@ -21,7 +25,7 @@ import { PostFormParams } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
archiveTodayUrl,
|
archiveTodayUrl,
|
||||||
authField,
|
auth,
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
choicesConfig,
|
choicesConfig,
|
||||||
communitySelectName,
|
communitySelectName,
|
||||||
|
@ -36,13 +40,12 @@ import {
|
||||||
relTags,
|
relTags,
|
||||||
setupTippy,
|
setupTippy,
|
||||||
toast,
|
toast,
|
||||||
|
trendingFetchLimit,
|
||||||
validTitle,
|
validTitle,
|
||||||
validURL,
|
validURL,
|
||||||
webArchiveUrl,
|
webArchiveUrl,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { Icon, Spinner } from "../common/icon";
|
import { Icon, Spinner } from "../common/icon";
|
||||||
import { MarkdownTextArea } from "../common/markdown-textarea";
|
import { MarkdownTextArea } from "../common/markdown-textarea";
|
||||||
|
@ -56,42 +59,45 @@ if (isBrowser()) {
|
||||||
const MAX_POST_TITLE_LENGTH = 200;
|
const MAX_POST_TITLE_LENGTH = 200;
|
||||||
|
|
||||||
interface PostFormProps {
|
interface PostFormProps {
|
||||||
post_view?: PostView; // If a post is given, that means this is an edit
|
post_view: Option<PostView>; // If a post is given, that means this is an edit
|
||||||
communities?: CommunityView[];
|
communities: Option<CommunityView[]>;
|
||||||
params?: PostFormParams;
|
params: Option<PostFormParams>;
|
||||||
onCancel?(): any;
|
onCancel?(): any;
|
||||||
onCreate?(post: PostView): any;
|
onCreate?(post: PostView): any;
|
||||||
onEdit?(post: PostView): any;
|
onEdit?(post: PostView): any;
|
||||||
enableNsfw: boolean;
|
enableNsfw?: boolean;
|
||||||
enableDownvotes: boolean;
|
enableDownvotes?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PostFormState {
|
interface PostFormState {
|
||||||
postForm: CreatePost;
|
postForm: CreatePost;
|
||||||
|
suggestedTitle: Option<string>;
|
||||||
|
suggestedPosts: Option<PostView[]>;
|
||||||
|
crossPosts: Option<PostView[]>;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
imageLoading: boolean;
|
imageLoading: boolean;
|
||||||
previewMode: boolean;
|
previewMode: boolean;
|
||||||
suggestedTitle: string;
|
|
||||||
suggestedPosts: PostView[];
|
|
||||||
crossPosts: PostView[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = {
|
private emptyState: PostFormState = {
|
||||||
postForm: {
|
postForm: new CreatePost({
|
||||||
community_id: null,
|
community_id: undefined,
|
||||||
name: null,
|
name: undefined,
|
||||||
nsfw: false,
|
nsfw: Some(false),
|
||||||
auth: authField(false),
|
url: None,
|
||||||
},
|
body: None,
|
||||||
|
honeypot: None,
|
||||||
|
auth: undefined,
|
||||||
|
}),
|
||||||
loading: false,
|
loading: false,
|
||||||
imageLoading: false,
|
imageLoading: false,
|
||||||
previewMode: false,
|
previewMode: false,
|
||||||
suggestedTitle: undefined,
|
suggestedTitle: None,
|
||||||
suggestedPosts: [],
|
suggestedPosts: None,
|
||||||
crossPosts: [],
|
crossPosts: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
|
@ -103,26 +109,28 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
this.state = this.emptyState;
|
this.state = this.emptyState;
|
||||||
|
|
||||||
// Means its an edit
|
// Means its an edit
|
||||||
if (this.props.post_view) {
|
this.props.post_view.match({
|
||||||
this.state.postForm = {
|
some: pv =>
|
||||||
body: this.props.post_view.post.body,
|
(this.state.postForm = new CreatePost({
|
||||||
name: this.props.post_view.post.name,
|
body: pv.post.body,
|
||||||
community_id: this.props.post_view.community.id,
|
name: pv.post.name,
|
||||||
url: this.props.post_view.post.url,
|
community_id: pv.community.id,
|
||||||
nsfw: this.props.post_view.post.nsfw,
|
url: pv.post.url,
|
||||||
auth: authField(),
|
nsfw: Some(pv.post.nsfw),
|
||||||
};
|
honeypot: None,
|
||||||
}
|
auth: auth().unwrap(),
|
||||||
|
})),
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
|
|
||||||
if (this.props.params) {
|
this.props.params.match({
|
||||||
this.state.postForm.name = this.props.params.name;
|
some: params => {
|
||||||
if (this.props.params.url) {
|
this.state.postForm.name = toUndefined(params.name);
|
||||||
this.state.postForm.url = this.props.params.url;
|
this.state.postForm.url = params.url;
|
||||||
}
|
this.state.postForm.body = params.body;
|
||||||
if (this.props.params.body) {
|
},
|
||||||
this.state.postForm.body = this.props.params.body;
|
none: void 0,
|
||||||
}
|
});
|
||||||
}
|
|
||||||
|
|
||||||
this.parseMessage = this.parseMessage.bind(this);
|
this.parseMessage = this.parseMessage.bind(this);
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
@ -141,8 +149,8 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
if (
|
if (
|
||||||
!this.state.loading &&
|
!this.state.loading &&
|
||||||
(this.state.postForm.name ||
|
(this.state.postForm.name ||
|
||||||
this.state.postForm.url ||
|
this.state.postForm.url.isSome() ||
|
||||||
this.state.postForm.body)
|
this.state.postForm.body.isSome())
|
||||||
) {
|
) {
|
||||||
window.onbeforeunload = () => true;
|
window.onbeforeunload = () => true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -163,8 +171,8 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
when={
|
when={
|
||||||
!this.state.loading &&
|
!this.state.loading &&
|
||||||
(this.state.postForm.name ||
|
(this.state.postForm.name ||
|
||||||
this.state.postForm.url ||
|
this.state.postForm.url.isSome() ||
|
||||||
this.state.postForm.body)
|
this.state.postForm.body.isSome())
|
||||||
}
|
}
|
||||||
message={i18n.t("block_leaving")}
|
message={i18n.t("block_leaving")}
|
||||||
/>
|
/>
|
||||||
|
@ -178,26 +186,29 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
type="url"
|
type="url"
|
||||||
id="post-url"
|
id="post-url"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
value={this.state.postForm.url}
|
value={toUndefined(this.state.postForm.url)}
|
||||||
onInput={linkEvent(this, this.handlePostUrlChange)}
|
onInput={linkEvent(this, this.handlePostUrlChange)}
|
||||||
onPaste={linkEvent(this, this.handleImageUploadPaste)}
|
onPaste={linkEvent(this, this.handleImageUploadPaste)}
|
||||||
/>
|
/>
|
||||||
{this.state.suggestedTitle && (
|
{this.state.suggestedTitle.match({
|
||||||
<div
|
some: title => (
|
||||||
class="mt-1 text-muted small font-weight-bold pointer"
|
<div
|
||||||
role="button"
|
class="mt-1 text-muted small font-weight-bold pointer"
|
||||||
onClick={linkEvent(this, this.copySuggestedTitle)}
|
role="button"
|
||||||
>
|
onClick={linkEvent(this, this.copySuggestedTitle)}
|
||||||
{i18n.t("copy_suggested_title", {
|
>
|
||||||
title: this.state.suggestedTitle,
|
{i18n.t("copy_suggested_title", {
|
||||||
})}
|
title,
|
||||||
</div>
|
})}
|
||||||
)}
|
</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
<form>
|
<form>
|
||||||
<label
|
<label
|
||||||
htmlFor="file-upload"
|
htmlFor="file-upload"
|
||||||
className={`${
|
className={`${
|
||||||
UserService.Instance.myUserInfo && "pointer"
|
UserService.Instance.myUserInfo.isSome() && "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")}
|
||||||
>
|
>
|
||||||
|
@ -209,58 +220,68 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
accept="image/*,video/*"
|
accept="image/*,video/*"
|
||||||
name="file"
|
name="file"
|
||||||
class="d-none"
|
class="d-none"
|
||||||
disabled={!UserService.Instance.myUserInfo}
|
disabled={UserService.Instance.myUserInfo.isNone()}
|
||||||
onChange={linkEvent(this, this.handleImageUpload)}
|
onChange={linkEvent(this, this.handleImageUpload)}
|
||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
{this.state.postForm.url && validURL(this.state.postForm.url) && (
|
{this.state.postForm.url.match({
|
||||||
<div>
|
some: url =>
|
||||||
<a
|
validURL(url) && (
|
||||||
href={`${webArchiveUrl}/save/${encodeURIComponent(
|
<div>
|
||||||
this.state.postForm.url
|
<a
|
||||||
)}`}
|
href={`${webArchiveUrl}/save/${encodeURIComponent(
|
||||||
class="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
url
|
||||||
rel={relTags}
|
)}`}
|
||||||
>
|
class="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
||||||
archive.org {i18n.t("archive_link")}
|
rel={relTags}
|
||||||
</a>
|
>
|
||||||
<a
|
archive.org {i18n.t("archive_link")}
|
||||||
href={`${ghostArchiveUrl}/search?term=${encodeURIComponent(
|
</a>
|
||||||
this.state.postForm.url
|
<a
|
||||||
)}`}
|
href={`${ghostArchiveUrl}/search?term=${encodeURIComponent(
|
||||||
class="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
url
|
||||||
rel={relTags}
|
)}`}
|
||||||
>
|
class="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
||||||
ghostarchive.org {i18n.t("archive_link")}
|
rel={relTags}
|
||||||
</a>
|
>
|
||||||
<a
|
ghostarchive.org {i18n.t("archive_link")}
|
||||||
href={`${archiveTodayUrl}/?run=1&url=${encodeURIComponent(
|
</a>
|
||||||
this.state.postForm.url
|
<a
|
||||||
)}`}
|
href={`${archiveTodayUrl}/?run=1&url=${encodeURIComponent(
|
||||||
class="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
url
|
||||||
rel={relTags}
|
)}`}
|
||||||
>
|
class="mr-2 d-inline-block float-right text-muted small font-weight-bold"
|
||||||
archive.today {i18n.t("archive_link")}
|
rel={relTags}
|
||||||
</a>
|
>
|
||||||
</div>
|
archive.today {i18n.t("archive_link")}
|
||||||
)}
|
</a>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
{this.state.imageLoading && <Spinner />}
|
{this.state.imageLoading && <Spinner />}
|
||||||
{isImage(this.state.postForm.url) && (
|
{this.state.postForm.url.match({
|
||||||
<img src={this.state.postForm.url} class="img-fluid" alt="" />
|
some: url =>
|
||||||
)}
|
isImage(url) && <img src={url} class="img-fluid" alt="" />,
|
||||||
{this.state.crossPosts.length > 0 && (
|
none: <></>,
|
||||||
<>
|
})}
|
||||||
<div class="my-1 text-muted small font-weight-bold">
|
{this.state.crossPosts.match({
|
||||||
{i18n.t("cross_posts")}
|
some: xPosts =>
|
||||||
</div>
|
xPosts.length > 0 && (
|
||||||
<PostListings
|
<>
|
||||||
showCommunity
|
<div class="my-1 text-muted small font-weight-bold">
|
||||||
posts={this.state.crossPosts}
|
{i18n.t("cross_posts")}
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
</div>
|
||||||
enableNsfw={this.props.enableNsfw}
|
<PostListings
|
||||||
/>
|
showCommunity
|
||||||
</>
|
posts={xPosts}
|
||||||
)}
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
|
enableNsfw={this.props.enableNsfw}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
|
@ -285,18 +306,22 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
{i18n.t("invalid_post_title")}
|
{i18n.t("invalid_post_title")}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{this.state.suggestedPosts.length > 0 && (
|
{this.state.suggestedPosts.match({
|
||||||
<>
|
some: sPosts =>
|
||||||
<div class="my-1 text-muted small font-weight-bold">
|
sPosts.length > 0 && (
|
||||||
{i18n.t("related_posts")}
|
<>
|
||||||
</div>
|
<div class="my-1 text-muted small font-weight-bold">
|
||||||
<PostListings
|
{i18n.t("related_posts")}
|
||||||
posts={this.state.suggestedPosts}
|
</div>
|
||||||
enableDownvotes={this.props.enableDownvotes}
|
<PostListings
|
||||||
enableNsfw={this.props.enableNsfw}
|
posts={sPosts}
|
||||||
/>
|
enableDownvotes={this.props.enableDownvotes}
|
||||||
</>
|
enableNsfw={this.props.enableNsfw}
|
||||||
)}
|
/>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -306,10 +331,13 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.postForm.body}
|
initialContent={this.state.postForm.body}
|
||||||
onContentChange={this.handlePostBodyChange}
|
onContentChange={this.handlePostBodyChange}
|
||||||
|
placeholder={None}
|
||||||
|
buttonTitle={None}
|
||||||
|
maxLength={None}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!this.props.post_view && (
|
{this.props.post_view.isNone() && (
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-2 col-form-label" htmlFor="post-community">
|
<label class="col-sm-2 col-form-label" htmlFor="post-community">
|
||||||
{i18n.t("community")}
|
{i18n.t("community")}
|
||||||
|
@ -322,7 +350,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
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.map(cv => (
|
{this.props.communities.unwrapOr([]).map(cv => (
|
||||||
<option value={cv.community.id}>
|
<option value={cv.community.id}>
|
||||||
{communitySelectName(cv)}
|
{communitySelectName(cv)}
|
||||||
</option>
|
</option>
|
||||||
|
@ -342,7 +370,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
class="form-check-input position-static"
|
class="form-check-input position-static"
|
||||||
id="post-nsfw"
|
id="post-nsfw"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={this.state.postForm.nsfw}
|
checked={toUndefined(this.state.postForm.nsfw)}
|
||||||
onChange={linkEvent(this, this.handlePostNsfwChange)}
|
onChange={linkEvent(this, this.handlePostNsfwChange)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -356,7 +384,7 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
type="text"
|
type="text"
|
||||||
class="form-control honeypot"
|
class="form-control honeypot"
|
||||||
id="register-honey"
|
id="register-honey"
|
||||||
value={this.state.postForm.honeypot}
|
value={toUndefined(this.state.postForm.honeypot)}
|
||||||
onInput={linkEvent(this, this.handleHoneyPotChange)}
|
onInput={linkEvent(this, this.handleHoneyPotChange)}
|
||||||
/>
|
/>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
|
@ -370,13 +398,13 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
>
|
>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : this.props.post_view ? (
|
) : this.props.post_view.isSome() ? (
|
||||||
capitalizeFirstLetter(i18n.t("save"))
|
capitalizeFirstLetter(i18n.t("save"))
|
||||||
) : (
|
) : (
|
||||||
capitalizeFirstLetter(i18n.t("create"))
|
capitalizeFirstLetter(i18n.t("create"))
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
{this.props.post_view && (
|
{this.props.post_view.isSome() && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
|
@ -396,65 +424,87 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// Coerce empty url string to undefined
|
// Coerce empty url string to undefined
|
||||||
if (i.state.postForm.url !== undefined && i.state.postForm.url === "") {
|
if (
|
||||||
i.state.postForm.url = undefined;
|
i.state.postForm.url.isSome() &&
|
||||||
|
i.state.postForm.url.unwrapOr("blank") === ""
|
||||||
|
) {
|
||||||
|
i.state.postForm.url = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i.props.post_view) {
|
let pForm = i.state.postForm;
|
||||||
let form: EditPost = {
|
i.props.post_view.match({
|
||||||
...i.state.postForm,
|
some: pv => {
|
||||||
post_id: i.props.post_view.post.id,
|
let form = new EditPost({
|
||||||
};
|
name: Some(pForm.name),
|
||||||
WebSocketService.Instance.send(wsClient.editPost(form));
|
url: pForm.url,
|
||||||
} else {
|
body: pForm.body,
|
||||||
WebSocketService.Instance.send(wsClient.createPost(i.state.postForm));
|
nsfw: pForm.nsfw,
|
||||||
}
|
post_id: pv.post.id,
|
||||||
|
auth: auth().unwrap(),
|
||||||
|
});
|
||||||
|
WebSocketService.Instance.send(wsClient.editPost(form));
|
||||||
|
},
|
||||||
|
none: () => {
|
||||||
|
i.state.postForm.auth = auth().unwrap();
|
||||||
|
WebSocketService.Instance.send(wsClient.createPost(i.state.postForm));
|
||||||
|
},
|
||||||
|
});
|
||||||
i.state.loading = true;
|
i.state.loading = true;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
copySuggestedTitle(i: PostForm) {
|
copySuggestedTitle(i: PostForm) {
|
||||||
i.state.postForm.name = i.state.suggestedTitle.substring(
|
i.state.suggestedTitle.match({
|
||||||
0,
|
some: sTitle => {
|
||||||
MAX_POST_TITLE_LENGTH
|
i.state.postForm.name = sTitle.substring(0, MAX_POST_TITLE_LENGTH);
|
||||||
);
|
i.state.suggestedTitle = None;
|
||||||
i.state.suggestedTitle = undefined;
|
setTimeout(() => {
|
||||||
setTimeout(() => {
|
let textarea: any = document.getElementById("post-title");
|
||||||
let textarea: any = document.getElementById("post-title");
|
autosize.update(textarea);
|
||||||
autosize.update(textarea);
|
}, 10);
|
||||||
}, 10);
|
i.setState(i.state);
|
||||||
i.setState(i.state);
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostUrlChange(i: PostForm, event: any) {
|
handlePostUrlChange(i: PostForm, event: any) {
|
||||||
i.state.postForm.url = event.target.value;
|
i.state.postForm.url = Some(event.target.value);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
i.fetchPageTitle();
|
i.fetchPageTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchPageTitle() {
|
fetchPageTitle() {
|
||||||
if (validURL(this.state.postForm.url)) {
|
this.state.postForm.url.match({
|
||||||
let form: Search = {
|
some: url => {
|
||||||
q: this.state.postForm.url,
|
if (validURL(url)) {
|
||||||
type_: SearchType.Url,
|
let form = new Search({
|
||||||
sort: SortType.TopAll,
|
q: url,
|
||||||
listing_type: ListingType.All,
|
community_id: None,
|
||||||
page: 1,
|
community_name: None,
|
||||||
limit: 6,
|
creator_id: None,
|
||||||
auth: authField(false),
|
type_: Some(SearchType.Url),
|
||||||
};
|
sort: Some(SortType.TopAll),
|
||||||
|
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(this.state.postForm.url).then(d => {
|
getSiteMetadata(url).then(d => {
|
||||||
this.state.suggestedTitle = d.metadata.title;
|
this.state.suggestedTitle = d.metadata.title;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.state.suggestedTitle = undefined;
|
this.state.suggestedTitle = None;
|
||||||
this.state.crossPosts = [];
|
this.state.crossPosts = None;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostNameChange(i: PostForm, event: any) {
|
handlePostNameChange(i: PostForm, event: any) {
|
||||||
|
@ -464,28 +514,30 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchSimilarPosts() {
|
fetchSimilarPosts() {
|
||||||
let form: Search = {
|
let form = new Search({
|
||||||
q: this.state.postForm.name,
|
q: this.state.postForm.name,
|
||||||
type_: SearchType.Posts,
|
type_: Some(SearchType.Posts),
|
||||||
sort: SortType.TopAll,
|
sort: Some(SortType.TopAll),
|
||||||
listing_type: ListingType.All,
|
listing_type: Some(ListingType.All),
|
||||||
community_id: this.state.postForm.community_id,
|
community_id: Some(this.state.postForm.community_id),
|
||||||
page: 1,
|
community_name: None,
|
||||||
limit: 6,
|
creator_id: None,
|
||||||
auth: authField(false),
|
page: Some(1),
|
||||||
};
|
limit: Some(trendingFetchLimit),
|
||||||
|
auth: auth(false).ok(),
|
||||||
|
});
|
||||||
|
|
||||||
if (this.state.postForm.name !== "") {
|
if (this.state.postForm.name !== "") {
|
||||||
WebSocketService.Instance.send(wsClient.search(form));
|
WebSocketService.Instance.send(wsClient.search(form));
|
||||||
} else {
|
} else {
|
||||||
this.state.suggestedPosts = [];
|
this.state.suggestedPosts = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostBodyChange(val: string) {
|
handlePostBodyChange(val: string) {
|
||||||
this.state.postForm.body = val;
|
this.state.postForm.body = Some(val);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -495,12 +547,12 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePostNsfwChange(i: PostForm, event: any) {
|
handlePostNsfwChange(i: PostForm, event: any) {
|
||||||
i.state.postForm.nsfw = event.target.checked;
|
i.state.postForm.nsfw = Some(event.target.checked);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleHoneyPotChange(i: PostForm, event: any) {
|
handleHoneyPotChange(i: PostForm, event: any) {
|
||||||
i.state.postForm.honeypot = event.target.value;
|
i.state.postForm.honeypot = Some(event.target.value);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,7 +601,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 = url;
|
i.state.postForm.url = Some(url);
|
||||||
i.state.imageLoading = false;
|
i.state.imageLoading = false;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
pictrsDeleteToast(
|
pictrsDeleteToast(
|
||||||
|
@ -606,30 +658,34 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.props.post_view) {
|
this.props.post_view.match({
|
||||||
this.state.postForm.community_id = this.props.post_view.community.id;
|
some: pv => (this.state.postForm.community_id = pv.community.id),
|
||||||
} else if (
|
none: void 0,
|
||||||
this.props.params &&
|
});
|
||||||
(this.props.params.community_id || this.props.params.community_name)
|
this.props.params.match({
|
||||||
) {
|
some: params =>
|
||||||
if (this.props.params.community_name) {
|
params.nameOrId.match({
|
||||||
let foundCommunityId = this.props.communities.find(
|
some: nameOrId =>
|
||||||
r => r.community.name == this.props.params.community_name
|
nameOrId.match({
|
||||||
).community.id;
|
left: name => {
|
||||||
this.state.postForm.community_id = foundCommunityId;
|
let foundCommunityId = this.props.communities
|
||||||
} else if (this.props.params.community_id) {
|
.unwrapOr([])
|
||||||
this.state.postForm.community_id = this.props.params.community_id;
|
.find(r => r.community.name == name).community.id;
|
||||||
}
|
this.state.postForm.community_id = foundCommunityId;
|
||||||
|
},
|
||||||
|
right: id => (this.state.postForm.community_id = id),
|
||||||
|
}),
|
||||||
|
none: void 0,
|
||||||
|
}),
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
|
|
||||||
if (isBrowser()) {
|
if (isBrowser() && this.state.postForm.community_id) {
|
||||||
this.choices.setChoiceByValue(
|
this.choices.setChoiceByValue(
|
||||||
this.state.postForm.community_id.toString()
|
this.state.postForm.community_id.toString()
|
||||||
);
|
);
|
||||||
}
|
|
||||||
this.setState(this.state);
|
|
||||||
} else {
|
|
||||||
// By default, the null valued 'Select a Community'
|
|
||||||
}
|
}
|
||||||
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -642,30 +698,34 @@ export class PostForm extends Component<PostFormProps, PostFormState> {
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.CreatePost) {
|
} else if (op == UserOperation.CreatePost) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg).data;
|
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
||||||
if (
|
UserService.Instance.myUserInfo.match({
|
||||||
data.post_view.creator.id ==
|
some: mui => {
|
||||||
UserService.Instance.myUserInfo.local_user_view.person.id
|
if (data.post_view.creator.id == mui.local_user_view.person.id) {
|
||||||
) {
|
this.state.loading = false;
|
||||||
this.state.loading = false;
|
this.props.onCreate(data.post_view);
|
||||||
this.props.onCreate(data.post_view);
|
}
|
||||||
}
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
} else if (op == UserOperation.EditPost) {
|
} else if (op == UserOperation.EditPost) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg).data;
|
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
||||||
if (
|
UserService.Instance.myUserInfo.match({
|
||||||
data.post_view.creator.id ==
|
some: mui => {
|
||||||
UserService.Instance.myUserInfo.local_user_view.person.id
|
if (data.post_view.creator.id == mui.local_user_view.person.id) {
|
||||||
) {
|
this.state.loading = false;
|
||||||
this.state.loading = false;
|
this.props.onEdit(data.post_view);
|
||||||
this.props.onEdit(data.post_view);
|
}
|
||||||
}
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
} else if (op == UserOperation.Search) {
|
} else if (op == UserOperation.Search) {
|
||||||
let data = wsJsonToRes<SearchResponse>(msg).data;
|
let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
|
||||||
|
|
||||||
if (data.type_ == SearchType[SearchType.Posts]) {
|
if (data.type_ == SearchType[SearchType.Posts]) {
|
||||||
this.state.suggestedPosts = data.posts;
|
this.state.suggestedPosts = Some(data.posts);
|
||||||
} else if (data.type_ == SearchType[SearchType.Url]) {
|
} else if (data.type_ == SearchType[SearchType.Url]) {
|
||||||
this.state.crossPosts = data.posts;
|
this.state.crossPosts = Some(data.posts);
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,4 @@
|
||||||
|
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";
|
||||||
|
@ -13,39 +14,30 @@ interface PostListingsProps {
|
||||||
enableNsfw: boolean;
|
enableNsfw: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PostListingsState {
|
export class PostListings extends Component<PostListingsProps, any> {
|
||||||
posts: PostView[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class PostListings extends Component<
|
|
||||||
PostListingsProps,
|
|
||||||
PostListingsState
|
|
||||||
> {
|
|
||||||
duplicatesMap = new Map<number, PostView[]>();
|
duplicatesMap = new Map<number, PostView[]>();
|
||||||
|
|
||||||
private emptyState: PostListingsState = {
|
|
||||||
posts: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props: any, context: any) {
|
constructor(props: any, context: any) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
this.state = this.emptyState;
|
}
|
||||||
if (this.props.removeDuplicates) {
|
|
||||||
this.state.posts = this.removeDuplicates();
|
get posts() {
|
||||||
} else {
|
return this.props.removeDuplicates
|
||||||
this.state.posts = this.props.posts;
|
? this.removeDuplicates()
|
||||||
}
|
: this.props.posts;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{this.state.posts.length > 0 ? (
|
{this.posts.length > 0 ? (
|
||||||
this.state.posts.map(post_view => (
|
this.posts.map(post_view => (
|
||||||
<>
|
<>
|
||||||
<PostListing
|
<PostListing
|
||||||
post_view={post_view}
|
post_view={post_view}
|
||||||
duplicates={this.duplicatesMap.get(post_view.post.id)}
|
duplicates={Some(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}
|
||||||
|
@ -56,7 +48,7 @@ export class PostListings extends Component<
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<div>{i18n.t("no_posts")}</div>
|
<div>{i18n.t("no_posts")}</div>
|
||||||
{this.props.showCommunity !== undefined && (
|
{this.props.showCommunity && (
|
||||||
<T i18nKey="subscribe_to_communities">
|
<T i18nKey="subscribe_to_communities">
|
||||||
#<Link to="/communities">#</Link>
|
#<Link to="/communities">#</Link>
|
||||||
</T>
|
</T>
|
||||||
|
@ -76,19 +68,20 @@ export class PostListings extends Component<
|
||||||
|
|
||||||
// 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) {
|
||||||
if (
|
!pv.post.deleted &&
|
||||||
pv.post.url &&
|
|
||||||
!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({
|
||||||
if (!urlMap.get(pv.post.url)) {
|
some: url => {
|
||||||
urlMap.set(pv.post.url, [pv]);
|
if (!urlMap.get(url)) {
|
||||||
} else {
|
urlMap.set(url, [pv]);
|
||||||
urlMap.get(pv.post.url).push(pv);
|
} else {
|
||||||
}
|
urlMap.get(url).push(pv);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by oldest
|
// Sort by oldest
|
||||||
|
@ -103,19 +96,22 @@ export class PostListings extends Component<
|
||||||
|
|
||||||
for (let i = 0; i < posts.length; i++) {
|
for (let i = 0; i < posts.length; i++) {
|
||||||
let pv = posts[i];
|
let pv = posts[i];
|
||||||
if (pv.post.url) {
|
pv.post.url.match({
|
||||||
let found = urlMap.get(pv.post.url);
|
some: url => {
|
||||||
if (found) {
|
let found = urlMap.get(url);
|
||||||
// If its the oldest, add
|
if (found) {
|
||||||
if (pv.post.id == found[0].post.id) {
|
// If its the oldest, add
|
||||||
this.duplicatesMap.set(pv.post.id, found.slice(1));
|
if (pv.post.id == found[0].post.id) {
|
||||||
|
this.duplicatesMap.set(pv.post.id, found.slice(1));
|
||||||
|
}
|
||||||
|
// Otherwise, delete it
|
||||||
|
else {
|
||||||
|
posts.splice(i--, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Otherwise, delete it
|
},
|
||||||
else {
|
none: void 0,
|
||||||
posts.splice(i--, 1);
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return posts;
|
return posts;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
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 { PostReportView, PostView, ResolvePostReport } from "lemmy-js-client";
|
import { PostReportView, PostView, ResolvePostReport } from "lemmy-js-client";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { WebSocketService } from "../../services";
|
import { WebSocketService } from "../../services";
|
||||||
import { authField, wsClient } from "../../utils";
|
import { auth, 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";
|
||||||
|
@ -45,6 +46,9 @@ 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}
|
||||||
|
@ -56,21 +60,24 @@ 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 && (
|
{r.resolver.match({
|
||||||
<div>
|
some: resolver => (
|
||||||
{r.post_report.resolved ? (
|
<div>
|
||||||
<T i18nKey="resolved_by">
|
{r.post_report.resolved ? (
|
||||||
#
|
<T i18nKey="resolved_by">
|
||||||
<PersonListing person={r.resolver} />
|
#
|
||||||
</T>
|
<PersonListing person={resolver} />
|
||||||
) : (
|
</T>
|
||||||
<T i18nKey="unresolved_by">
|
) : (
|
||||||
#
|
<T i18nKey="unresolved_by">
|
||||||
<PersonListing person={r.resolver} />
|
#
|
||||||
</T>
|
<PersonListing person={resolver} />
|
||||||
)}
|
</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)}
|
||||||
|
@ -89,11 +96,11 @@ export class PostReport extends Component<PostReportProps, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleResolveReport(i: PostReport) {
|
handleResolveReport(i: PostReport) {
|
||||||
let form: ResolvePostReport = {
|
let form = new ResolvePostReport({
|
||||||
report_id: i.props.report.post_report.id,
|
report_id: i.props.report.post_report.id,
|
||||||
resolved: !i.props.report.post_report.resolved,
|
resolved: !i.props.report.post_report.resolved,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.resolvePostReport(form));
|
WebSocketService.Instance.send(wsClient.resolvePostReport(form));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
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 {
|
||||||
|
@ -23,6 +24,8 @@ import {
|
||||||
SearchType,
|
SearchType,
|
||||||
SortType,
|
SortType,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
|
@ -34,13 +37,15 @@ import {
|
||||||
} from "../../interfaces";
|
} from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
buildCommentsTree,
|
buildCommentsTree,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
createCommentLikeRes,
|
createCommentLikeRes,
|
||||||
createPostLikeRes,
|
createPostLikeRes,
|
||||||
debounce,
|
debounce,
|
||||||
editCommentRes,
|
editCommentRes,
|
||||||
|
enableDownvotes,
|
||||||
|
enableNsfw,
|
||||||
getCommentIdFromProps,
|
getCommentIdFromProps,
|
||||||
getIdFromProps,
|
getIdFromProps,
|
||||||
insertCommentIntoTree,
|
insertCommentIntoTree,
|
||||||
|
@ -50,14 +55,12 @@ import {
|
||||||
saveCommentRes,
|
saveCommentRes,
|
||||||
saveScrollPosition,
|
saveScrollPosition,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
setOptionalAuth,
|
|
||||||
setupTippy,
|
setupTippy,
|
||||||
toast,
|
toast,
|
||||||
|
trendingFetchLimit,
|
||||||
updatePersonBlock,
|
updatePersonBlock,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { CommentForm } from "../comment/comment-form";
|
import { CommentForm } from "../comment/comment-form";
|
||||||
import { CommentNodes } from "../comment/comment-nodes";
|
import { CommentNodes } from "../comment/comment-nodes";
|
||||||
|
@ -69,7 +72,7 @@ import { PostListing } from "./post-listing";
|
||||||
const commentsShownInterval = 15;
|
const commentsShownInterval = 15;
|
||||||
|
|
||||||
interface PostState {
|
interface PostState {
|
||||||
postRes: GetPostResponse;
|
postRes: Option<GetPostResponse>;
|
||||||
postId: number;
|
postId: number;
|
||||||
commentTree: CommentNodeI[];
|
commentTree: CommentNodeI[];
|
||||||
commentId?: number;
|
commentId?: number;
|
||||||
|
@ -77,7 +80,7 @@ interface PostState {
|
||||||
commentViewType: CommentViewType;
|
commentViewType: CommentViewType;
|
||||||
scrolled?: boolean;
|
scrolled?: boolean;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
crossPosts: PostView[];
|
crossPosts: Option<PostView[]>;
|
||||||
siteRes: GetSiteResponse;
|
siteRes: GetSiteResponse;
|
||||||
commentSectionRef?: RefObject<HTMLDivElement>;
|
commentSectionRef?: RefObject<HTMLDivElement>;
|
||||||
showSidebarMobile: boolean;
|
showSidebarMobile: boolean;
|
||||||
|
@ -86,10 +89,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(this.context);
|
private isoData = setIsoData(this.context, GetPostResponse);
|
||||||
private commentScrollDebounced: () => void;
|
private commentScrollDebounced: () => void;
|
||||||
private emptyState: PostState = {
|
private emptyState: PostState = {
|
||||||
postRes: null,
|
postRes: None,
|
||||||
postId: getIdFromProps(this.props),
|
postId: getIdFromProps(this.props),
|
||||||
commentTree: [],
|
commentTree: [],
|
||||||
commentId: getCommentIdFromProps(this.props),
|
commentId: getCommentIdFromProps(this.props),
|
||||||
|
@ -97,7 +100,7 @@ export class Post extends Component<any, PostState> {
|
||||||
commentViewType: CommentViewType.Tree,
|
commentViewType: CommentViewType.Tree,
|
||||||
scrolled: false,
|
scrolled: false,
|
||||||
loading: true,
|
loading: true,
|
||||||
crossPosts: [],
|
crossPosts: None,
|
||||||
siteRes: this.isoData.site_res,
|
siteRes: this.isoData.site_res,
|
||||||
commentSectionRef: null,
|
commentSectionRef: null,
|
||||||
showSidebarMobile: false,
|
showSidebarMobile: false,
|
||||||
|
@ -115,14 +118,24 @@ export class Post extends Component<any, PostState> {
|
||||||
|
|
||||||
// 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.postRes = this.isoData.routeData[0];
|
this.state.postRes = Some(this.isoData.routeData[0] as GetPostResponse);
|
||||||
this.state.commentTree = buildCommentsTree(
|
this.state.commentTree = buildCommentsTree(
|
||||||
this.state.postRes.comments,
|
this.state.postRes.unwrap().comments,
|
||||||
this.state.commentSort
|
this.state.commentSort
|
||||||
);
|
);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
|
|
||||||
if (isBrowser()) {
|
if (isBrowser()) {
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.communityJoin({
|
||||||
|
community_id:
|
||||||
|
this.state.postRes.unwrap().community_view.community.id,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.postJoin({ post_id: this.state.postId })
|
||||||
|
);
|
||||||
|
|
||||||
this.fetchCrossPosts();
|
this.fetchCrossPosts();
|
||||||
if (this.state.commentId) {
|
if (this.state.commentId) {
|
||||||
this.scrollCommentIntoView();
|
this.scrollCommentIntoView();
|
||||||
|
@ -138,42 +151,47 @@ export class Post extends Component<any, PostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchPost() {
|
fetchPost() {
|
||||||
let form: GetPost = {
|
let form = new GetPost({
|
||||||
id: this.state.postId,
|
id: this.state.postId,
|
||||||
auth: authField(false),
|
auth: auth(false).ok(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.getPost(form));
|
WebSocketService.Instance.send(wsClient.getPost(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchCrossPosts() {
|
fetchCrossPosts() {
|
||||||
if (this.state.postRes.post_view.post.url) {
|
this.state.postRes
|
||||||
let form: Search = {
|
.andThen(r => r.post_view.post.url)
|
||||||
q: this.state.postRes.post_view.post.url,
|
.match({
|
||||||
type_: SearchType.Url,
|
some: url => {
|
||||||
sort: SortType.TopAll,
|
let form = new Search({
|
||||||
listing_type: ListingType.All,
|
q: url,
|
||||||
page: 1,
|
type_: Some(SearchType.Url),
|
||||||
limit: 6,
|
sort: Some(SortType.TopAll),
|
||||||
auth: authField(false),
|
listing_type: Some(ListingType.All),
|
||||||
};
|
page: Some(1),
|
||||||
WebSocketService.Instance.send(wsClient.search(form));
|
limit: Some(trendingFetchLimit),
|
||||||
}
|
community_id: None,
|
||||||
|
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 id = Number(pathSplit[2]);
|
let id = Number(pathSplit[2]);
|
||||||
|
|
||||||
let postForm: GetPost = {
|
let postForm = new GetPost({
|
||||||
id,
|
id,
|
||||||
};
|
auth: req.auth,
|
||||||
setOptionalAuth(postForm, req.auth);
|
});
|
||||||
|
|
||||||
promises.push(req.client.getPost(postForm));
|
return [req.client.getPost(postForm)];
|
||||||
|
|
||||||
return promises;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -185,9 +203,6 @@ export class Post extends Component<any, PostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
WebSocketService.Instance.send(
|
|
||||||
wsClient.postJoin({ post_id: this.state.postId })
|
|
||||||
);
|
|
||||||
autosize(document.querySelectorAll("textarea"));
|
autosize(document.querySelectorAll("textarea"));
|
||||||
|
|
||||||
this.commentScrollDebounced = debounce(this.trackCommentsBoxScrolling, 100);
|
this.commentScrollDebounced = debounce(this.trackCommentsBoxScrolling, 100);
|
||||||
|
@ -231,34 +246,38 @@ export class Post extends Component<any, PostState> {
|
||||||
|
|
||||||
// TODO this needs some re-work
|
// TODO this needs some re-work
|
||||||
markScrolledAsRead(commentId: number) {
|
markScrolledAsRead(commentId: number) {
|
||||||
let found = this.state.postRes.comments.find(
|
this.state.postRes.match({
|
||||||
c => c.comment.id == commentId
|
some: res => {
|
||||||
);
|
let found = res.comments.find(c => c.comment.id == commentId);
|
||||||
let parent = this.state.postRes.comments.find(
|
let parent = res.comments.find(
|
||||||
c => found.comment.parent_id == c.comment.id
|
c => found.comment.parent_id.unwrapOr(0) == c.comment.id
|
||||||
);
|
);
|
||||||
let parent_person_id = parent
|
let parent_person_id = parent
|
||||||
? parent.creator.id
|
? parent.creator.id
|
||||||
: this.state.postRes.post_view.creator.id;
|
: res.post_view.creator.id;
|
||||||
|
|
||||||
if (
|
UserService.Instance.myUserInfo.match({
|
||||||
UserService.Instance.myUserInfo &&
|
some: mui => {
|
||||||
UserService.Instance.myUserInfo.local_user_view.person.id ==
|
if (mui.local_user_view.person.id == parent_person_id) {
|
||||||
parent_person_id
|
let form = new MarkCommentAsRead({
|
||||||
) {
|
comment_id: found.comment.id,
|
||||||
let form: MarkCommentAsRead = {
|
read: true,
|
||||||
comment_id: found.comment.id,
|
auth: auth().unwrap(),
|
||||||
read: true,
|
});
|
||||||
auth: authField(),
|
WebSocketService.Instance.send(wsClient.markCommentAsRead(form));
|
||||||
};
|
UserService.Instance.unreadInboxCountSub.next(
|
||||||
WebSocketService.Instance.send(wsClient.markCommentAsRead(form));
|
UserService.Instance.unreadInboxCountSub.value - 1
|
||||||
UserService.Instance.unreadInboxCountSub.next(
|
);
|
||||||
UserService.Instance.unreadInboxCountSub.value - 1
|
}
|
||||||
);
|
},
|
||||||
}
|
none: void 0,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isBottom(el: Element) {
|
isBottom(el: Element): boolean {
|
||||||
return el?.getBoundingClientRect().bottom <= window.innerHeight;
|
return el?.getBoundingClientRect().bottom <= window.innerHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,23 +293,35 @@ export class Post extends Component<any, PostState> {
|
||||||
};
|
};
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
return `${this.state.postRes.post_view.post.name} - ${this.state.siteRes.site_view.site.name}`;
|
return this.state.postRes.match({
|
||||||
|
some: res =>
|
||||||
|
this.state.siteRes.site_view.match({
|
||||||
|
some: siteView =>
|
||||||
|
`${res.post_view.post.name} - ${siteView.site.name}`,
|
||||||
|
none: "",
|
||||||
|
}),
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get imageTag(): string {
|
get imageTag(): Option<string> {
|
||||||
let post = this.state.postRes.post_view.post;
|
return this.state.postRes.match({
|
||||||
return (
|
some: res =>
|
||||||
post.thumbnail_url ||
|
res.post_view.post.thumbnail_url.or(
|
||||||
(post.url ? (isImage(post.url) ? post.url : undefined) : undefined)
|
res.post_view.post.url.match({
|
||||||
);
|
some: url => (isImage(url) ? Some(url) : None),
|
||||||
|
none: None,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
none: None,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get descriptionTag(): string {
|
get descriptionTag(): Option<string> {
|
||||||
return this.state.postRes.post_view.post.body;
|
return this.state.postRes.andThen(r => r.post_view.post.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let pv = this.state.postRes?.post_view;
|
|
||||||
return (
|
return (
|
||||||
<div class="container">
|
<div class="container">
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
|
@ -298,56 +329,59 @@ export class Post extends Component<any, PostState> {
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
</h5>
|
</h5>
|
||||||
) : (
|
) : (
|
||||||
<div class="row">
|
this.state.postRes.match({
|
||||||
<div class="col-12 col-md-8 mb-3">
|
some: res => (
|
||||||
<HtmlTags
|
<div class="row">
|
||||||
title={this.documentTitle}
|
<div class="col-12 col-md-8 mb-3">
|
||||||
path={this.context.router.route.match.url}
|
<HtmlTags
|
||||||
image={this.imageTag}
|
title={this.documentTitle}
|
||||||
description={this.descriptionTag}
|
path={this.context.router.route.match.url}
|
||||||
/>
|
image={this.imageTag}
|
||||||
<PostListing
|
description={this.descriptionTag}
|
||||||
post_view={pv}
|
|
||||||
duplicates={this.state.crossPosts}
|
|
||||||
showBody
|
|
||||||
showCommunity
|
|
||||||
moderators={this.state.postRes.moderators}
|
|
||||||
admins={this.state.siteRes.admins}
|
|
||||||
enableDownvotes={
|
|
||||||
this.state.siteRes.site_view.site.enable_downvotes
|
|
||||||
}
|
|
||||||
enableNsfw={this.state.siteRes.site_view.site.enable_nsfw}
|
|
||||||
/>
|
|
||||||
<div ref={this.state.commentSectionRef} className="mb-2" />
|
|
||||||
<CommentForm
|
|
||||||
postId={this.state.postId}
|
|
||||||
disabled={pv.post.locked}
|
|
||||||
/>
|
|
||||||
<div class="d-block d-md-none">
|
|
||||||
<button
|
|
||||||
class="btn btn-secondary d-inline-block mb-2 mr-3"
|
|
||||||
onClick={linkEvent(this, this.handleShowSidebarMobile)}
|
|
||||||
>
|
|
||||||
{i18n.t("sidebar")}{" "}
|
|
||||||
<Icon
|
|
||||||
icon={
|
|
||||||
this.state.showSidebarMobile
|
|
||||||
? `minus-square`
|
|
||||||
: `plus-square`
|
|
||||||
}
|
|
||||||
classes="icon-inline"
|
|
||||||
/>
|
/>
|
||||||
</button>
|
<PostListing
|
||||||
{this.state.showSidebarMobile && this.sidebar()}
|
post_view={res.post_view}
|
||||||
|
duplicates={this.state.crossPosts}
|
||||||
|
showBody
|
||||||
|
showCommunity
|
||||||
|
moderators={Some(res.moderators)}
|
||||||
|
admins={Some(this.state.siteRes.admins)}
|
||||||
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
|
/>
|
||||||
|
<div ref={this.state.commentSectionRef} className="mb-2" />
|
||||||
|
<CommentForm
|
||||||
|
node={Right(this.state.postId)}
|
||||||
|
disabled={res.post_view.post.locked}
|
||||||
|
/>
|
||||||
|
<div class="d-block d-md-none">
|
||||||
|
<button
|
||||||
|
class="btn btn-secondary d-inline-block mb-2 mr-3"
|
||||||
|
onClick={linkEvent(this, this.handleShowSidebarMobile)}
|
||||||
|
>
|
||||||
|
{i18n.t("sidebar")}{" "}
|
||||||
|
<Icon
|
||||||
|
icon={
|
||||||
|
this.state.showSidebarMobile
|
||||||
|
? `minus-square`
|
||||||
|
: `plus-square`
|
||||||
|
}
|
||||||
|
classes="icon-inline"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
{this.state.showSidebarMobile && this.sidebar()}
|
||||||
|
</div>
|
||||||
|
{res.comments.length > 0 && this.sortRadios()}
|
||||||
|
{this.state.commentViewType == CommentViewType.Tree &&
|
||||||
|
this.commentsTree()}
|
||||||
|
{this.state.commentViewType == CommentViewType.Chat &&
|
||||||
|
this.commentsFlat()}
|
||||||
|
</div>
|
||||||
|
<div class="d-none d-md-block col-md-4">{this.sidebar()}</div>
|
||||||
</div>
|
</div>
|
||||||
{this.state.postRes.comments.length > 0 && this.sortRadios()}
|
),
|
||||||
{this.state.commentViewType == CommentViewType.Tree &&
|
none: <></>,
|
||||||
this.commentsTree()}
|
})
|
||||||
{this.state.commentViewType == CommentViewType.Chat &&
|
|
||||||
this.commentsFlat()}
|
|
||||||
</div>
|
|
||||||
<div class="d-none d-md-block col-md-4">{this.sidebar()}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -431,43 +465,48 @@ export class Post extends Component<any, PostState> {
|
||||||
|
|
||||||
commentsFlat() {
|
commentsFlat() {
|
||||||
// These are already sorted by new
|
// These are already sorted by new
|
||||||
return (
|
return this.state.postRes.match({
|
||||||
<div>
|
some: res => (
|
||||||
<CommentNodes
|
<div>
|
||||||
nodes={commentsToFlatNodes(this.state.postRes.comments)}
|
<CommentNodes
|
||||||
maxCommentsShown={this.state.maxCommentsShown}
|
nodes={commentsToFlatNodes(res.comments)}
|
||||||
noIndent
|
maxCommentsShown={Some(this.state.maxCommentsShown)}
|
||||||
locked={this.state.postRes.post_view.post.locked}
|
noIndent
|
||||||
moderators={this.state.postRes.moderators}
|
locked={res.post_view.post.locked}
|
||||||
admins={this.state.siteRes.admins}
|
moderators={Some(res.moderators)}
|
||||||
postCreatorId={this.state.postRes.post_view.creator.id}
|
admins={Some(this.state.siteRes.admins)}
|
||||||
showContext
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
enableDownvotes={this.state.siteRes.site_view.site.enable_downvotes}
|
showContext
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
),
|
||||||
|
none: <></>,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sidebar() {
|
sidebar() {
|
||||||
return (
|
return this.state.postRes.match({
|
||||||
<div class="mb-3">
|
some: res => (
|
||||||
<Sidebar
|
<div class="mb-3">
|
||||||
community_view={this.state.postRes.community_view}
|
<Sidebar
|
||||||
moderators={this.state.postRes.moderators}
|
community_view={res.community_view}
|
||||||
admins={this.state.siteRes.admins}
|
moderators={res.moderators}
|
||||||
online={this.state.postRes.online}
|
admins={this.state.siteRes.admins}
|
||||||
enableNsfw={this.state.siteRes.site_view.site.enable_nsfw}
|
online={res.online}
|
||||||
showIcon
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
/>
|
showIcon
|
||||||
</div>
|
/>
|
||||||
);
|
</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCommentSortChange(i: Post, event: any) {
|
handleCommentSortChange(i: Post, event: any) {
|
||||||
i.state.commentSort = Number(event.target.value);
|
i.state.commentSort = Number(event.target.value);
|
||||||
i.state.commentViewType = CommentViewType.Tree;
|
i.state.commentViewType = CommentViewType.Tree;
|
||||||
i.state.commentTree = buildCommentsTree(
|
i.state.commentTree = buildCommentsTree(
|
||||||
i.state.postRes.comments,
|
i.state.postRes.map(r => r.comments).unwrapOr([]),
|
||||||
i.state.commentSort
|
i.state.commentSort
|
||||||
);
|
);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
@ -477,7 +516,7 @@ export class Post extends Component<any, PostState> {
|
||||||
i.state.commentViewType = Number(event.target.value);
|
i.state.commentViewType = Number(event.target.value);
|
||||||
i.state.commentSort = CommentSortType.New;
|
i.state.commentSort = CommentSortType.New;
|
||||||
i.state.commentTree = buildCommentsTree(
|
i.state.commentTree = buildCommentsTree(
|
||||||
i.state.postRes.comments,
|
i.state.postRes.map(r => r.comments).unwrapOr([]),
|
||||||
i.state.commentSort
|
i.state.commentSort
|
||||||
);
|
);
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
|
@ -489,19 +528,21 @@ export class Post extends Component<any, PostState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
commentsTree() {
|
commentsTree() {
|
||||||
return (
|
return this.state.postRes.match({
|
||||||
<div>
|
some: res => (
|
||||||
<CommentNodes
|
<div>
|
||||||
nodes={this.state.commentTree}
|
<CommentNodes
|
||||||
maxCommentsShown={this.state.maxCommentsShown}
|
nodes={this.state.commentTree}
|
||||||
locked={this.state.postRes.post_view.post.locked}
|
maxCommentsShown={Some(this.state.maxCommentsShown)}
|
||||||
moderators={this.state.postRes.moderators}
|
locked={res.post_view.post.locked}
|
||||||
admins={this.state.siteRes.admins}
|
moderators={Some(res.moderators)}
|
||||||
postCreatorId={this.state.postRes.post_view.creator.id}
|
admins={Some(this.state.siteRes.admins)}
|
||||||
enableDownvotes={this.state.siteRes.site_view.site.enable_downvotes}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
),
|
||||||
|
none: <></>,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
parseMessage(msg: any) {
|
parseMessage(msg: any) {
|
||||||
|
@ -516,18 +557,29 @@ export class Post extends Component<any, PostState> {
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.getPost({
|
wsClient.getPost({
|
||||||
id: postId,
|
id: postId,
|
||||||
auth: authField(false),
|
auth: auth(false).ok(),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} else if (op == UserOperation.GetPost) {
|
} else if (op == UserOperation.GetPost) {
|
||||||
let data = wsJsonToRes<GetPostResponse>(msg).data;
|
let data = wsJsonToRes<GetPostResponse>(msg, GetPostResponse);
|
||||||
this.state.postRes = data;
|
this.state.postRes = Some(data);
|
||||||
|
|
||||||
this.state.commentTree = buildCommentsTree(
|
this.state.commentTree = buildCommentsTree(
|
||||||
this.state.postRes.comments,
|
this.state.postRes.map(r => r.comments).unwrapOr([]),
|
||||||
this.state.commentSort
|
this.state.commentSort
|
||||||
);
|
);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
|
|
||||||
|
// join the rooms
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.postJoin({ post_id: this.state.postId })
|
||||||
|
);
|
||||||
|
WebSocketService.Instance.send(
|
||||||
|
wsClient.communityJoin({
|
||||||
|
community_id: data.community_view.community.id,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
// Get cross-posts
|
// Get cross-posts
|
||||||
this.fetchCrossPosts();
|
this.fetchCrossPosts();
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
@ -542,18 +594,25 @@ export class Post extends Component<any, PostState> {
|
||||||
this.scrollCommentIntoView();
|
this.scrollCommentIntoView();
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.CreateComment) {
|
} else if (op == UserOperation.CreateComment) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
|
|
||||||
// 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?.person_blocks
|
let creatorBlocked = UserService.Instance.myUserInfo
|
||||||
|
.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) {
|
if (data.recipient_ids.length == 0 && !creatorBlocked) {
|
||||||
this.state.postRes.comments.unshift(data.comment_view);
|
this.state.postRes.match({
|
||||||
insertCommentIntoTree(this.state.commentTree, data.comment_view);
|
some: res => {
|
||||||
this.state.postRes.post_view.counts.comments++;
|
res.comments.unshift(data.comment_view);
|
||||||
|
insertCommentIntoTree(this.state.commentTree, data.comment_view);
|
||||||
|
res.post_view.counts.comments++;
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
}
|
}
|
||||||
|
@ -562,21 +621,33 @@ export class Post extends Component<any, PostState> {
|
||||||
op == UserOperation.DeleteComment ||
|
op == UserOperation.DeleteComment ||
|
||||||
op == UserOperation.RemoveComment
|
op == UserOperation.RemoveComment
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
editCommentRes(data.comment_view, this.state.postRes.comments);
|
editCommentRes(
|
||||||
|
data.comment_view,
|
||||||
|
this.state.postRes.map(r => r.comments).unwrapOr([])
|
||||||
|
);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.SaveComment) {
|
} else if (op == UserOperation.SaveComment) {
|
||||||
let data = wsJsonToRes<CommentResponse>(msg).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
saveCommentRes(data.comment_view, this.state.postRes.comments);
|
saveCommentRes(
|
||||||
|
data.comment_view,
|
||||||
|
this.state.postRes.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).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
createCommentLikeRes(data.comment_view, this.state.postRes.comments);
|
createCommentLikeRes(
|
||||||
|
data.comment_view,
|
||||||
|
this.state.postRes.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).data;
|
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
||||||
createPostLikeRes(data.post_view, this.state.postRes.post_view);
|
this.state.postRes.match({
|
||||||
|
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 ||
|
||||||
|
@ -586,8 +657,11 @@ export class Post extends Component<any, PostState> {
|
||||||
op == UserOperation.StickyPost ||
|
op == UserOperation.StickyPost ||
|
||||||
op == UserOperation.SavePost
|
op == UserOperation.SavePost
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<PostResponse>(msg).data;
|
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
||||||
this.state.postRes.post_view = data.post_view;
|
this.state.postRes.match({
|
||||||
|
some: res => (res.post_view = data.post_view),
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
setupTippy();
|
setupTippy();
|
||||||
} else if (
|
} else if (
|
||||||
|
@ -596,68 +670,94 @@ export class Post extends Component<any, PostState> {
|
||||||
op == UserOperation.RemoveCommunity ||
|
op == UserOperation.RemoveCommunity ||
|
||||||
op == UserOperation.FollowCommunity
|
op == UserOperation.FollowCommunity
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<CommunityResponse>(msg).data;
|
let data = wsJsonToRes<CommunityResponse>(msg, CommunityResponse);
|
||||||
this.state.postRes.community_view = data.community_view;
|
this.state.postRes.match({
|
||||||
this.state.postRes.post_view.community = data.community_view.community;
|
some: res => {
|
||||||
this.setState(this.state);
|
res.community_view = data.community_view;
|
||||||
this.setState(this.state);
|
res.post_view.community = data.community_view.community;
|
||||||
|
this.setState(this.state);
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
} else if (op == UserOperation.BanFromCommunity) {
|
} else if (op == UserOperation.BanFromCommunity) {
|
||||||
let data = wsJsonToRes<BanFromCommunityResponse>(msg).data;
|
let data = wsJsonToRes<BanFromCommunityResponse>(
|
||||||
this.state.postRes.comments
|
msg,
|
||||||
.filter(c => c.creator.id == data.person_view.person.id)
|
BanFromCommunityResponse
|
||||||
.forEach(c => (c.creator_banned_from_community = data.banned));
|
);
|
||||||
if (
|
this.state.postRes.match({
|
||||||
this.state.postRes.post_view.creator.id == data.person_view.person.id
|
some: res => {
|
||||||
) {
|
res.comments
|
||||||
this.state.postRes.post_view.creator_banned_from_community =
|
.filter(c => c.creator.id == data.person_view.person.id)
|
||||||
data.banned;
|
.forEach(c => (c.creator_banned_from_community = data.banned));
|
||||||
}
|
if (res.post_view.creator.id == data.person_view.person.id) {
|
||||||
this.setState(this.state);
|
res.post_view.creator_banned_from_community = data.banned;
|
||||||
|
}
|
||||||
|
this.setState(this.state);
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
} else if (op == UserOperation.AddModToCommunity) {
|
} else if (op == UserOperation.AddModToCommunity) {
|
||||||
let data = wsJsonToRes<AddModToCommunityResponse>(msg).data;
|
let data = wsJsonToRes<AddModToCommunityResponse>(
|
||||||
this.state.postRes.moderators = data.moderators;
|
msg,
|
||||||
this.setState(this.state);
|
AddModToCommunityResponse
|
||||||
|
);
|
||||||
|
this.state.postRes.match({
|
||||||
|
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).data;
|
let data = wsJsonToRes<BanPersonResponse>(msg, BanPersonResponse);
|
||||||
this.state.postRes.comments
|
this.state.postRes.match({
|
||||||
.filter(c => c.creator.id == data.person_view.person.id)
|
some: res => {
|
||||||
.forEach(c => (c.creator.banned = data.banned));
|
res.comments
|
||||||
if (
|
.filter(c => c.creator.id == data.person_view.person.id)
|
||||||
this.state.postRes.post_view.creator.id == data.person_view.person.id
|
.forEach(c => (c.creator.banned = data.banned));
|
||||||
) {
|
if (res.post_view.creator.id == data.person_view.person.id) {
|
||||||
this.state.postRes.post_view.creator.banned = data.banned;
|
res.post_view.creator.banned = data.banned;
|
||||||
}
|
}
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
} else if (op == UserOperation.AddAdmin) {
|
} else if (op == UserOperation.AddAdmin) {
|
||||||
let data = wsJsonToRes<AddAdminResponse>(msg).data;
|
let data = wsJsonToRes<AddAdminResponse>(msg, AddAdminResponse);
|
||||||
this.state.siteRes.admins = data.admins;
|
this.state.siteRes.admins = data.admins;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.Search) {
|
} else if (op == UserOperation.Search) {
|
||||||
let data = wsJsonToRes<SearchResponse>(msg).data;
|
let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
|
||||||
this.state.crossPosts = 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.state.crossPosts = xPosts.length > 0 ? Some(xPosts) : None;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.LeaveAdmin) {
|
} else if (op == UserOperation.LeaveAdmin) {
|
||||||
let data = wsJsonToRes<GetSiteResponse>(msg).data;
|
let data = wsJsonToRes<GetSiteResponse>(msg, GetSiteResponse);
|
||||||
this.state.siteRes = data;
|
this.state.siteRes = data;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
} else if (op == UserOperation.TransferCommunity) {
|
} else if (op == UserOperation.TransferCommunity) {
|
||||||
let data = wsJsonToRes<GetCommunityResponse>(msg).data;
|
let data = wsJsonToRes<GetCommunityResponse>(msg, GetCommunityResponse);
|
||||||
this.state.postRes.community_view = data.community_view;
|
this.state.postRes.match({
|
||||||
this.state.postRes.post_view.community = data.community_view.community;
|
some: res => {
|
||||||
this.state.postRes.moderators = data.moderators;
|
res.community_view = data.community_view;
|
||||||
this.setState(this.state);
|
res.post_view.community = data.community_view.community;
|
||||||
|
res.moderators = data.moderators;
|
||||||
|
this.setState(this.state);
|
||||||
|
},
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
} else if (op == UserOperation.BlockPerson) {
|
} else if (op == UserOperation.BlockPerson) {
|
||||||
let data = wsJsonToRes<BlockPersonResponse>(msg).data;
|
let data = wsJsonToRes<BlockPersonResponse>(msg, BlockPersonResponse);
|
||||||
updatePersonBlock(data);
|
updatePersonBlock(data);
|
||||||
} else if (op == UserOperation.CreatePostReport) {
|
} else if (op == UserOperation.CreatePostReport) {
|
||||||
let data = wsJsonToRes<PostReportResponse>(msg).data;
|
let data = wsJsonToRes<PostReportResponse>(msg, PostReportResponse);
|
||||||
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).data;
|
let data = wsJsonToRes<CommentReportResponse>(msg, CommentReportResponse);
|
||||||
if (data) {
|
if (data) {
|
||||||
toast(i18n.t("report_created"));
|
toast(i18n.t("report_created"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,34 @@
|
||||||
|
import { None, Option, Some } from "@sniptt/monads";
|
||||||
import { Component } from "inferno";
|
import { Component } from "inferno";
|
||||||
import {
|
import {
|
||||||
GetPersonDetails,
|
GetPersonDetails,
|
||||||
GetPersonDetailsResponse,
|
GetPersonDetailsResponse,
|
||||||
PersonViewSafe,
|
GetSiteResponse,
|
||||||
SiteView,
|
|
||||||
SortType,
|
SortType,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { InitialFetchRequest } from "../../interfaces";
|
import { InitialFetchRequest } from "../../interfaces";
|
||||||
import { UserService, WebSocketService } from "../../services";
|
import { UserService, WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
getRecipientIdFromProps,
|
getRecipientIdFromProps,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
toast,
|
toast,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { HtmlTags } from "../common/html-tags";
|
import { HtmlTags } from "../common/html-tags";
|
||||||
import { Spinner } from "../common/icon";
|
import { Spinner } from "../common/icon";
|
||||||
import { PrivateMessageForm } from "./private-message-form";
|
import { PrivateMessageForm } from "./private-message-form";
|
||||||
|
|
||||||
interface CreatePrivateMessageState {
|
interface CreatePrivateMessageState {
|
||||||
site_view: SiteView;
|
siteRes: GetSiteResponse;
|
||||||
recipient: PersonViewSafe;
|
recipientDetailsRes: Option<GetPersonDetailsResponse>;
|
||||||
recipient_id: number;
|
recipient_id: number;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
}
|
}
|
||||||
|
@ -37,11 +37,11 @@ export class CreatePrivateMessage extends Component<
|
||||||
any,
|
any,
|
||||||
CreatePrivateMessageState
|
CreatePrivateMessageState
|
||||||
> {
|
> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(this.context, GetPersonDetailsResponse);
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: CreatePrivateMessageState = {
|
private emptyState: CreatePrivateMessageState = {
|
||||||
site_view: this.isoData.site_res.site_view,
|
siteRes: this.isoData.site_res,
|
||||||
recipient: undefined,
|
recipientDetailsRes: None,
|
||||||
recipient_id: getRecipientIdFromProps(this.props),
|
recipient_id: getRecipientIdFromProps(this.props),
|
||||||
loading: true,
|
loading: true,
|
||||||
};
|
};
|
||||||
|
@ -54,14 +54,16 @@ export class CreatePrivateMessage extends Component<
|
||||||
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) {
|
if (UserService.Instance.myUserInfo.isNone() && 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`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.recipient = this.isoData.routeData[0].user;
|
this.state.recipientDetailsRes = Some(
|
||||||
|
this.isoData.routeData[0] as GetPersonDetailsResponse
|
||||||
|
);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
} else {
|
} else {
|
||||||
this.fetchPersonDetails();
|
this.fetchPersonDetails();
|
||||||
|
@ -69,30 +71,40 @@ export class CreatePrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchPersonDetails() {
|
fetchPersonDetails() {
|
||||||
let form: GetPersonDetails = {
|
let form = new GetPersonDetails({
|
||||||
person_id: this.state.recipient_id,
|
person_id: Some(this.state.recipient_id),
|
||||||
sort: SortType.New,
|
sort: Some(SortType.New),
|
||||||
saved_only: false,
|
saved_only: Some(false),
|
||||||
auth: authField(false),
|
username: None,
|
||||||
};
|
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 = Number(req.path.split("/").pop());
|
let person_id = Some(Number(req.path.split("/").pop()));
|
||||||
let form: GetPersonDetails = {
|
let form = new GetPersonDetails({
|
||||||
person_id,
|
person_id,
|
||||||
sort: SortType.New,
|
sort: Some(SortType.New),
|
||||||
saved_only: false,
|
saved_only: Some(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 `${i18n.t("create_private_message")} - ${
|
return this.state.recipientDetailsRes.match({
|
||||||
this.state.site_view.site.name
|
some: res =>
|
||||||
}`;
|
`${i18n.t("create_private_message")} - ${res.person_view.person.name}`,
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -107,21 +119,29 @@ export class CreatePrivateMessage 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}
|
||||||
/>
|
/>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<h5>
|
<h5>
|
||||||
<Spinner large />
|
<Spinner large />
|
||||||
</h5>
|
</h5>
|
||||||
) : (
|
) : (
|
||||||
<div class="row">
|
this.state.recipientDetailsRes.match({
|
||||||
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
some: res => (
|
||||||
<h5>{i18n.t("create_private_message")}</h5>
|
<div class="row">
|
||||||
<PrivateMessageForm
|
<div class="col-12 col-lg-6 offset-lg-3 mb-4">
|
||||||
onCreate={this.handlePrivateMessageCreate}
|
<h5>{i18n.t("create_private_message")}</h5>
|
||||||
recipient={this.state.recipient.person}
|
<PrivateMessageForm
|
||||||
/>
|
privateMessageView={None}
|
||||||
</div>
|
onCreate={this.handlePrivateMessageCreate}
|
||||||
</div>
|
recipient={res.person_view.person}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -143,8 +163,11 @@ export class CreatePrivateMessage extends Component<
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
return;
|
return;
|
||||||
} else if (op == UserOperation.GetPersonDetails) {
|
} else if (op == UserOperation.GetPersonDetails) {
|
||||||
let data = wsJsonToRes<GetPersonDetailsResponse>(msg).data;
|
let data = wsJsonToRes<GetPersonDetailsResponse>(
|
||||||
this.state.recipient = data.person_view;
|
msg,
|
||||||
|
GetPersonDetailsResponse
|
||||||
|
);
|
||||||
|
this.state.recipientDetailsRes = Some(data);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
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";
|
||||||
|
@ -8,21 +9,21 @@ import {
|
||||||
PrivateMessageResponse,
|
PrivateMessageResponse,
|
||||||
PrivateMessageView,
|
PrivateMessageView,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { i18n } from "../../i18next";
|
import { i18n } from "../../i18next";
|
||||||
import { WebSocketService } from "../../services";
|
import { WebSocketService } from "../../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
isBrowser,
|
isBrowser,
|
||||||
relTags,
|
relTags,
|
||||||
setupTippy,
|
setupTippy,
|
||||||
toast,
|
toast,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../../utils";
|
} from "../../utils";
|
||||||
import { Icon, Spinner } from "../common/icon";
|
import { Icon, Spinner } from "../common/icon";
|
||||||
import { MarkdownTextArea } from "../common/markdown-textarea";
|
import { MarkdownTextArea } from "../common/markdown-textarea";
|
||||||
|
@ -30,7 +31,7 @@ import { PersonListing } from "../person/person-listing";
|
||||||
|
|
||||||
interface PrivateMessageFormProps {
|
interface PrivateMessageFormProps {
|
||||||
recipient: PersonSafe;
|
recipient: PersonSafe;
|
||||||
privateMessage?: PrivateMessageView; // If a pm is given, that means this is an edit
|
privateMessageView: Option<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;
|
||||||
|
@ -49,11 +50,11 @@ export class PrivateMessageForm extends Component<
|
||||||
> {
|
> {
|
||||||
private subscription: Subscription;
|
private subscription: Subscription;
|
||||||
private emptyState: PrivateMessageFormState = {
|
private emptyState: PrivateMessageFormState = {
|
||||||
privateMessageForm: {
|
privateMessageForm: new CreatePrivateMessage({
|
||||||
content: null,
|
content: null,
|
||||||
recipient_id: this.props.recipient.id,
|
recipient_id: this.props.recipient.id,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
},
|
}),
|
||||||
loading: false,
|
loading: false,
|
||||||
previewMode: false,
|
previewMode: false,
|
||||||
showDisclaimer: false,
|
showDisclaimer: false,
|
||||||
|
@ -70,10 +71,11 @@ export class PrivateMessageForm extends Component<
|
||||||
this.subscription = wsSubscribe(this.parseMessage);
|
this.subscription = wsSubscribe(this.parseMessage);
|
||||||
|
|
||||||
// Its an edit
|
// Its an edit
|
||||||
if (this.props.privateMessage) {
|
this.props.privateMessageView.match({
|
||||||
this.state.privateMessageForm.content =
|
some: pm =>
|
||||||
this.props.privateMessage.private_message.content;
|
(this.state.privateMessageForm.content = pm.private_message.content),
|
||||||
}
|
none: void 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -103,7 +105,7 @@ export class PrivateMessageForm extends Component<
|
||||||
message={i18n.t("block_leaving")}
|
message={i18n.t("block_leaving")}
|
||||||
/>
|
/>
|
||||||
<form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
|
<form onSubmit={linkEvent(this, this.handlePrivateMessageSubmit)}>
|
||||||
{!this.props.privateMessage && (
|
{this.props.privateMessageView.isNone() && (
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-2 col-form-label">
|
<label class="col-sm-2 col-form-label">
|
||||||
{capitalizeFirstLetter(i18n.t("to"))}
|
{capitalizeFirstLetter(i18n.t("to"))}
|
||||||
|
@ -128,7 +130,10 @@ export class PrivateMessageForm extends Component<
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<MarkdownTextArea
|
<MarkdownTextArea
|
||||||
initialContent={this.state.privateMessageForm.content}
|
initialContent={Some(this.state.privateMessageForm.content)}
|
||||||
|
placeholder={None}
|
||||||
|
buttonTitle={None}
|
||||||
|
maxLength={None}
|
||||||
onContentChange={this.handleContentChange}
|
onContentChange={this.handleContentChange}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -161,13 +166,13 @@ export class PrivateMessageForm extends Component<
|
||||||
>
|
>
|
||||||
{this.state.loading ? (
|
{this.state.loading ? (
|
||||||
<Spinner />
|
<Spinner />
|
||||||
) : this.props.privateMessage ? (
|
) : this.props.privateMessageView.isSome() ? (
|
||||||
capitalizeFirstLetter(i18n.t("save"))
|
capitalizeFirstLetter(i18n.t("save"))
|
||||||
) : (
|
) : (
|
||||||
capitalizeFirstLetter(i18n.t("send_message"))
|
capitalizeFirstLetter(i18n.t("send_message"))
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
{this.props.privateMessage && (
|
{this.props.privateMessageView.isSome() && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-secondary"
|
class="btn btn-secondary"
|
||||||
|
@ -188,18 +193,19 @@ export class PrivateMessageForm extends Component<
|
||||||
|
|
||||||
handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) {
|
handlePrivateMessageSubmit(i: PrivateMessageForm, event: any) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (i.props.privateMessage) {
|
i.props.privateMessageView.match({
|
||||||
let form: EditPrivateMessage = {
|
some: pm => {
|
||||||
private_message_id: i.props.privateMessage.private_message.id,
|
let form = new EditPrivateMessage({
|
||||||
content: i.state.privateMessageForm.content,
|
private_message_id: pm.private_message.id,
|
||||||
auth: authField(),
|
content: i.state.privateMessageForm.content,
|
||||||
};
|
auth: auth().unwrap(),
|
||||||
WebSocketService.Instance.send(wsClient.editPrivateMessage(form));
|
});
|
||||||
} else {
|
WebSocketService.Instance.send(wsClient.editPrivateMessage(form));
|
||||||
WebSocketService.Instance.send(
|
},
|
||||||
|
none: WebSocketService.Instance.send(
|
||||||
wsClient.createPrivateMessage(i.state.privateMessageForm)
|
wsClient.createPrivateMessage(i.state.privateMessageForm)
|
||||||
);
|
),
|
||||||
}
|
});
|
||||||
i.state.loading = true;
|
i.state.loading = true;
|
||||||
i.setState(i.state);
|
i.setState(i.state);
|
||||||
}
|
}
|
||||||
|
@ -237,11 +243,17 @@ export class PrivateMessageForm extends Component<
|
||||||
op == UserOperation.DeletePrivateMessage ||
|
op == UserOperation.DeletePrivateMessage ||
|
||||||
op == UserOperation.MarkPrivateMessageAsRead
|
op == UserOperation.MarkPrivateMessageAsRead
|
||||||
) {
|
) {
|
||||||
let data = wsJsonToRes<PrivateMessageResponse>(msg).data;
|
let data = wsJsonToRes<PrivateMessageResponse>(
|
||||||
|
msg,
|
||||||
|
PrivateMessageResponse
|
||||||
|
);
|
||||||
this.state.loading = false;
|
this.state.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>(msg).data;
|
let data = wsJsonToRes<PrivateMessageResponse>(
|
||||||
|
msg,
|
||||||
|
PrivateMessageResponse
|
||||||
|
);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
this.props.onCreate(data.private_message_view);
|
this.props.onCreate(data.private_message_view);
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { None, Some } from "@sniptt/monads/build";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
DeletePrivateMessage,
|
DeletePrivateMessage,
|
||||||
|
@ -7,7 +8,7 @@ import {
|
||||||
} 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 { authField, mdToHtml, toast, wsClient } from "../../utils";
|
import { auth, mdToHtml, 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";
|
||||||
|
@ -46,16 +47,17 @@ export class PrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
get mine(): boolean {
|
get mine(): boolean {
|
||||||
return (
|
return UserService.Instance.myUserInfo
|
||||||
UserService.Instance.myUserInfo &&
|
.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() {
|
||||||
let message_view = this.props.private_message_view;
|
let message_view = this.props.private_message_view;
|
||||||
// TODO check this again
|
|
||||||
let otherPerson: PersonSafe = this.mine
|
let otherPerson: PersonSafe = this.mine
|
||||||
? message_view.recipient
|
? message_view.recipient
|
||||||
: message_view.creator;
|
: message_view.creator;
|
||||||
|
@ -73,7 +75,10 @@ export class PrivateMessage extends Component<
|
||||||
</li>
|
</li>
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
<span>
|
<span>
|
||||||
<MomentTime data={message_view.private_message} />
|
<MomentTime
|
||||||
|
published={message_view.private_message.published}
|
||||||
|
updated={message_view.private_message.updated}
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="list-inline-item">
|
<li className="list-inline-item">
|
||||||
|
@ -93,7 +98,7 @@ export class PrivateMessage extends Component<
|
||||||
{this.state.showEdit && (
|
{this.state.showEdit && (
|
||||||
<PrivateMessageForm
|
<PrivateMessageForm
|
||||||
recipient={otherPerson}
|
recipient={otherPerson}
|
||||||
privateMessage={message_view}
|
privateMessageView={Some(message_view)}
|
||||||
onEdit={this.handlePrivateMessageEdit}
|
onEdit={this.handlePrivateMessageEdit}
|
||||||
onCreate={this.handlePrivateMessageCreate}
|
onCreate={this.handlePrivateMessageCreate}
|
||||||
onCancel={this.handleReplyCancel}
|
onCancel={this.handleReplyCancel}
|
||||||
|
@ -207,6 +212,7 @@ 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}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@ -232,11 +238,11 @@ export class PrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDeleteClick(i: PrivateMessage) {
|
handleDeleteClick(i: PrivateMessage) {
|
||||||
let form: DeletePrivateMessage = {
|
let form = new DeletePrivateMessage({
|
||||||
private_message_id: i.props.private_message_view.private_message.id,
|
private_message_id: i.props.private_message_view.private_message.id,
|
||||||
deleted: !i.props.private_message_view.private_message.deleted,
|
deleted: !i.props.private_message_view.private_message.deleted,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.deletePrivateMessage(form));
|
WebSocketService.Instance.send(wsClient.deletePrivateMessage(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,11 +253,11 @@ export class PrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMarkRead(i: PrivateMessage) {
|
handleMarkRead(i: PrivateMessage) {
|
||||||
let form: MarkPrivateMessageAsRead = {
|
let form = new MarkPrivateMessageAsRead({
|
||||||
private_message_id: i.props.private_message_view.private_message.id,
|
private_message_id: i.props.private_message_view.private_message.id,
|
||||||
read: !i.props.private_message_view.private_message.read,
|
read: !i.props.private_message_view.private_message.read,
|
||||||
auth: authField(),
|
auth: auth().unwrap(),
|
||||||
};
|
});
|
||||||
WebSocketService.Instance.send(wsClient.markPrivateMessageAsRead(form));
|
WebSocketService.Instance.send(wsClient.markPrivateMessageAsRead(form));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,14 +277,15 @@ export class PrivateMessage extends Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePrivateMessageCreate(message: PrivateMessageView) {
|
handlePrivateMessageCreate(message: PrivateMessageView) {
|
||||||
if (
|
UserService.Instance.myUserInfo.match({
|
||||||
UserService.Instance.myUserInfo &&
|
some: mui => {
|
||||||
message.creator.id ==
|
if (message.creator.id == mui.local_user_view.person.id) {
|
||||||
UserService.Instance.myUserInfo.local_user_view.person.id
|
this.state.showReply = false;
|
||||||
) {
|
this.setState(this.state);
|
||||||
this.state.showReply = false;
|
toast(i18n.t("message_sent"));
|
||||||
this.setState(this.state);
|
}
|
||||||
toast(i18n.t("message_sent"));
|
},
|
||||||
}
|
none: void 0,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
|
import { None, Option, Some } from "@sniptt/monads";
|
||||||
import { Component, linkEvent } from "inferno";
|
import { Component, linkEvent } from "inferno";
|
||||||
import {
|
import {
|
||||||
CommentResponse,
|
CommentResponse,
|
||||||
CommentView,
|
CommentView,
|
||||||
CommunityView,
|
CommunityView,
|
||||||
GetCommunity,
|
GetCommunity,
|
||||||
|
GetCommunityResponse,
|
||||||
GetPersonDetails,
|
GetPersonDetails,
|
||||||
|
GetPersonDetailsResponse,
|
||||||
|
GetSiteResponse,
|
||||||
ListCommunities,
|
ListCommunities,
|
||||||
ListCommunitiesResponse,
|
ListCommunitiesResponse,
|
||||||
ListingType,
|
ListingType,
|
||||||
|
@ -16,16 +20,17 @@ import {
|
||||||
Search as SearchForm,
|
Search as SearchForm,
|
||||||
SearchResponse,
|
SearchResponse,
|
||||||
SearchType,
|
SearchType,
|
||||||
Site,
|
|
||||||
SortType,
|
SortType,
|
||||||
UserOperation,
|
UserOperation,
|
||||||
|
wsJsonToRes,
|
||||||
|
wsUserOp,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import { Subscription } from "rxjs";
|
import { Subscription } from "rxjs";
|
||||||
import { InitialFetchRequest } from "shared/interfaces";
|
import { InitialFetchRequest } from "shared/interfaces";
|
||||||
import { i18n } from "../i18next";
|
import { i18n } from "../i18next";
|
||||||
import { WebSocketService } from "../services";
|
import { WebSocketService } from "../services";
|
||||||
import {
|
import {
|
||||||
authField,
|
auth,
|
||||||
capitalizeFirstLetter,
|
capitalizeFirstLetter,
|
||||||
choicesConfig,
|
choicesConfig,
|
||||||
commentsToFlatNodes,
|
commentsToFlatNodes,
|
||||||
|
@ -34,6 +39,8 @@ import {
|
||||||
createCommentLikeRes,
|
createCommentLikeRes,
|
||||||
createPostLikeFindRes,
|
createPostLikeFindRes,
|
||||||
debounce,
|
debounce,
|
||||||
|
enableDownvotes,
|
||||||
|
enableNsfw,
|
||||||
fetchCommunities,
|
fetchCommunities,
|
||||||
fetchLimit,
|
fetchLimit,
|
||||||
fetchUsers,
|
fetchUsers,
|
||||||
|
@ -48,13 +55,10 @@ import {
|
||||||
routeSortTypeToEnum,
|
routeSortTypeToEnum,
|
||||||
saveScrollPosition,
|
saveScrollPosition,
|
||||||
setIsoData,
|
setIsoData,
|
||||||
setOptionalAuth,
|
|
||||||
showLocal,
|
showLocal,
|
||||||
toast,
|
toast,
|
||||||
wsClient,
|
wsClient,
|
||||||
wsJsonToRes,
|
|
||||||
wsSubscribe,
|
wsSubscribe,
|
||||||
wsUserOp,
|
|
||||||
} from "../utils";
|
} from "../utils";
|
||||||
import { CommentNodes } from "./comment/comment-nodes";
|
import { CommentNodes } from "./comment/comment-nodes";
|
||||||
import { HtmlTags } from "./common/html-tags";
|
import { HtmlTags } from "./common/html-tags";
|
||||||
|
@ -89,13 +93,13 @@ interface SearchState {
|
||||||
communityId: number;
|
communityId: number;
|
||||||
creatorId: number;
|
creatorId: number;
|
||||||
page: number;
|
page: number;
|
||||||
searchResponse?: SearchResponse;
|
searchResponse: Option<SearchResponse>;
|
||||||
communities: CommunityView[];
|
communities: CommunityView[];
|
||||||
creator?: PersonViewSafe;
|
creatorDetails: Option<GetPersonDetailsResponse>;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
site: Site;
|
siteRes: GetSiteResponse;
|
||||||
searchText: string;
|
searchText: string;
|
||||||
resolveObjectResponse?: ResolveObjectResponse;
|
resolveObjectResponse: Option<ResolveObjectResponse>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UrlParams {
|
interface UrlParams {
|
||||||
|
@ -115,7 +119,14 @@ interface Combined {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Search extends Component<any, SearchState> {
|
export class Search extends Component<any, SearchState> {
|
||||||
private isoData = setIsoData(this.context);
|
private isoData = setIsoData(
|
||||||
|
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;
|
||||||
|
@ -132,10 +143,11 @@ 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: null,
|
searchResponse: None,
|
||||||
resolveObjectResponse: null,
|
resolveObjectResponse: None,
|
||||||
|
creatorDetails: None,
|
||||||
loading: true,
|
loading: true,
|
||||||
site: this.isoData.site_res.site_view.site,
|
siteRes: this.isoData.site_res,
|
||||||
communities: [],
|
communities: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -180,20 +192,29 @@ 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 singleOrMultipleCommunities = this.isoData.routeData[0];
|
let communityRes = Some(
|
||||||
if (singleOrMultipleCommunities.communities) {
|
this.isoData.routeData[0] as GetCommunityResponse
|
||||||
this.state.communities = this.isoData.routeData[0].communities;
|
);
|
||||||
} else {
|
let communitiesRes = Some(
|
||||||
this.state.communities = [this.isoData.routeData[0].community_view];
|
this.isoData.routeData[1] as ListCommunitiesResponse
|
||||||
}
|
);
|
||||||
|
|
||||||
|
// This can be single or multiple communities given
|
||||||
|
this.state.communities = communitiesRes
|
||||||
|
.map(c => c.communities)
|
||||||
|
.unwrapOr([communityRes.map(c => c.community_view).unwrap()]);
|
||||||
|
|
||||||
|
this.state.creatorDetails = Some(
|
||||||
|
this.isoData.routeData[2] as GetPersonDetailsResponse
|
||||||
|
);
|
||||||
|
|
||||||
let creator = this.isoData.routeData[1];
|
|
||||||
if (creator?.person_view) {
|
|
||||||
this.state.creator = this.isoData.routeData[1].person_view;
|
|
||||||
}
|
|
||||||
if (this.state.q != "") {
|
if (this.state.q != "") {
|
||||||
this.state.searchResponse = this.isoData.routeData[2];
|
this.state.searchResponse = Some(
|
||||||
this.state.resolveObjectResponse = this.isoData.routeData[3];
|
this.isoData.routeData[3] as SearchResponse
|
||||||
|
);
|
||||||
|
this.state.resolveObjectResponse = Some(
|
||||||
|
this.isoData.routeData[4] as ResolveObjectResponse
|
||||||
|
);
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
} else {
|
} else {
|
||||||
this.search();
|
this.search();
|
||||||
|
@ -231,12 +252,13 @@ export class Search extends Component<any, SearchState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchCommunities() {
|
fetchCommunities() {
|
||||||
let listCommunitiesForm: ListCommunities = {
|
let listCommunitiesForm = new ListCommunities({
|
||||||
type_: ListingType.All,
|
type_: Some(ListingType.All),
|
||||||
sort: SortType.TopAll,
|
sort: Some(SortType.TopAll),
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
auth: authField(false),
|
page: None,
|
||||||
};
|
auth: auth(false).ok(),
|
||||||
|
});
|
||||||
WebSocketService.Instance.send(
|
WebSocketService.Instance.send(
|
||||||
wsClient.listCommunities(listCommunitiesForm)
|
wsClient.listCommunities(listCommunitiesForm)
|
||||||
);
|
);
|
||||||
|
@ -247,59 +269,76 @@ export class Search extends Component<any, SearchState> {
|
||||||
let promises: Promise<any>[] = [];
|
let promises: Promise<any>[] = [];
|
||||||
|
|
||||||
let communityId = this.getCommunityIdFromProps(pathSplit[11]);
|
let communityId = this.getCommunityIdFromProps(pathSplit[11]);
|
||||||
if (communityId !== 0) {
|
let community_id: Option<number> =
|
||||||
let getCommunityForm: GetCommunity = {
|
communityId == 0 ? None : Some(communityId);
|
||||||
id: communityId,
|
community_id.match({
|
||||||
};
|
some: id => {
|
||||||
setOptionalAuth(getCommunityForm, req.auth);
|
let getCommunityForm = new GetCommunity({
|
||||||
promises.push(req.client.getCommunity(getCommunityForm));
|
id: Some(id),
|
||||||
} else {
|
name: None,
|
||||||
let listCommunitiesForm: ListCommunities = {
|
auth: req.auth,
|
||||||
type_: ListingType.All,
|
});
|
||||||
sort: SortType.TopAll,
|
promises.push(req.client.getCommunity(getCommunityForm));
|
||||||
limit: fetchLimit,
|
promises.push(Promise.resolve());
|
||||||
};
|
},
|
||||||
setOptionalAuth(listCommunitiesForm, req.auth);
|
none: () => {
|
||||||
promises.push(req.client.listCommunities(listCommunitiesForm));
|
let listCommunitiesForm = new ListCommunities({
|
||||||
}
|
type_: Some(ListingType.All),
|
||||||
|
sort: Some(SortType.TopAll),
|
||||||
|
limit: Some(fetchLimit),
|
||||||
|
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]);
|
||||||
if (creatorId !== 0) {
|
let creator_id: Option<number> = creatorId == 0 ? None : Some(creatorId);
|
||||||
let getCreatorForm: GetPersonDetails = {
|
creator_id.match({
|
||||||
person_id: creatorId,
|
some: id => {
|
||||||
};
|
let getCreatorForm = new GetPersonDetails({
|
||||||
setOptionalAuth(getCreatorForm, req.auth);
|
person_id: Some(id),
|
||||||
promises.push(req.client.getPersonDetails(getCreatorForm));
|
username: None,
|
||||||
} else {
|
sort: None,
|
||||||
promises.push(Promise.resolve());
|
page: None,
|
||||||
}
|
limit: None,
|
||||||
|
community_id: None,
|
||||||
|
saved_only: None,
|
||||||
|
auth: req.auth,
|
||||||
|
});
|
||||||
|
promises.push(req.client.getPersonDetails(getCreatorForm));
|
||||||
|
},
|
||||||
|
none: () => {
|
||||||
|
promises.push(Promise.resolve());
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
let form: SearchForm = {
|
let form = new SearchForm({
|
||||||
q: this.getSearchQueryFromProps(pathSplit[3]),
|
q: this.getSearchQueryFromProps(pathSplit[3]),
|
||||||
type_: this.getSearchTypeFromProps(pathSplit[5]),
|
community_id,
|
||||||
sort: this.getSortTypeFromProps(pathSplit[7]),
|
community_name: None,
|
||||||
listing_type: this.getListingTypeFromProps(pathSplit[9]),
|
creator_id,
|
||||||
page: this.getPageFromProps(pathSplit[15]),
|
type_: Some(this.getSearchTypeFromProps(pathSplit[5])),
|
||||||
limit: fetchLimit,
|
sort: Some(this.getSortTypeFromProps(pathSplit[7])),
|
||||||
};
|
listing_type: Some(this.getListingTypeFromProps(pathSplit[9])),
|
||||||
if (communityId !== 0) {
|
page: Some(this.getPageFromProps(pathSplit[15])),
|
||||||
form.community_id = communityId;
|
limit: Some(fetchLimit),
|
||||||
}
|
auth: req.auth,
|
||||||
if (creatorId !== 0) {
|
});
|
||||||
form.creator_id = creatorId;
|
|
||||||
}
|
|
||||||
setOptionalAuth(form, req.auth);
|
|
||||||
|
|
||||||
let resolveObjectForm: ResolveObject = {
|
let resolveObjectForm = new ResolveObject({
|
||||||
q: this.getSearchQueryFromProps(pathSplit[3]),
|
q: this.getSearchQueryFromProps(pathSplit[3]),
|
||||||
};
|
auth: req.auth,
|
||||||
setOptionalAuth(resolveObjectForm, req.auth);
|
});
|
||||||
|
|
||||||
if (form.q != "") {
|
if (form.q != "") {
|
||||||
//this.state.loading = false;
|
|
||||||
//this.setState(this.state);
|
|
||||||
promises.push(req.client.search(form));
|
promises.push(req.client.search(form));
|
||||||
promises.push(req.client.resolveObject(resolveObjectForm));
|
promises.push(req.client.resolveObject(resolveObjectForm));
|
||||||
|
} else {
|
||||||
|
promises.push(Promise.resolve());
|
||||||
|
promises.push(Promise.resolve());
|
||||||
}
|
}
|
||||||
|
|
||||||
return promises;
|
return promises;
|
||||||
|
@ -318,19 +357,21 @@ export class Search extends Component<any, SearchState> {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: true,
|
loading: true,
|
||||||
searchText: this.state.q,
|
searchText: this.state.q,
|
||||||
searchResponse: null,
|
searchResponse: None,
|
||||||
resolveObjectResponse: null,
|
resolveObjectResponse: None,
|
||||||
});
|
});
|
||||||
this.search();
|
this.search();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get documentTitle(): string {
|
get documentTitle(): string {
|
||||||
if (this.state.q) {
|
return this.state.siteRes.site_view.match({
|
||||||
return `${i18n.t("search")} - ${this.state.q} - ${this.state.site.name}`;
|
some: siteView =>
|
||||||
} else {
|
this.state.q
|
||||||
return `${i18n.t("search")} - ${this.state.site.name}`;
|
? `${i18n.t("search")} - ${this.state.q} - ${siteView.site.name}`
|
||||||
}
|
: `${i18n.t("search")} - ${siteView.site.name}`,
|
||||||
|
none: "",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -339,6 +380,8 @@ 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()}
|
||||||
|
@ -459,46 +502,52 @@ export class Search extends Component<any, SearchState> {
|
||||||
let combined: Combined[] = [];
|
let combined: Combined[] = [];
|
||||||
|
|
||||||
// Push the possible resolve / federated objects first
|
// Push the possible resolve / federated objects first
|
||||||
let resolveComment = this.state.resolveObjectResponse?.comment;
|
this.state.resolveObjectResponse.match({
|
||||||
if (resolveComment) {
|
some: res => {
|
||||||
combined.push(this.commentViewToCombined(resolveComment));
|
let resolveComment = res.comment;
|
||||||
}
|
if (resolveComment.isSome()) {
|
||||||
let resolvePost = this.state.resolveObjectResponse?.post;
|
combined.push(this.commentViewToCombined(resolveComment.unwrap()));
|
||||||
if (resolvePost) {
|
}
|
||||||
combined.push(this.postViewToCombined(resolvePost));
|
let resolvePost = res.post;
|
||||||
}
|
if (resolvePost.isSome()) {
|
||||||
let resolveCommunity = this.state.resolveObjectResponse?.community;
|
combined.push(this.postViewToCombined(resolvePost.unwrap()));
|
||||||
if (resolveCommunity) {
|
}
|
||||||
combined.push(this.communityViewToCombined(resolveCommunity));
|
let resolveCommunity = res.community;
|
||||||
}
|
if (resolveCommunity.isSome()) {
|
||||||
let resolveUser = this.state.resolveObjectResponse?.person;
|
combined.push(
|
||||||
if (resolveUser) {
|
this.communityViewToCombined(resolveCommunity.unwrap())
|
||||||
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
|
||||||
pushNotNull(
|
this.state.searchResponse.match({
|
||||||
combined,
|
some: res => {
|
||||||
this.state.searchResponse?.comments?.map(e =>
|
pushNotNull(
|
||||||
this.commentViewToCombined(e)
|
combined,
|
||||||
)
|
res.comments?.map(e => this.commentViewToCombined(e))
|
||||||
);
|
);
|
||||||
pushNotNull(
|
pushNotNull(
|
||||||
combined,
|
combined,
|
||||||
this.state.searchResponse?.posts?.map(e => this.postViewToCombined(e))
|
res.posts?.map(e => this.postViewToCombined(e))
|
||||||
);
|
);
|
||||||
pushNotNull(
|
pushNotNull(
|
||||||
combined,
|
combined,
|
||||||
this.state.searchResponse?.communities?.map(e =>
|
res.communities?.map(e => this.communityViewToCombined(e))
|
||||||
this.communityViewToCombined(e)
|
);
|
||||||
)
|
pushNotNull(
|
||||||
);
|
combined,
|
||||||
pushNotNull(
|
res.users?.map(e => this.personViewSafeToCombined(e))
|
||||||
combined,
|
);
|
||||||
this.state.searchResponse?.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) {
|
||||||
|
@ -528,18 +577,24 @@ 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={this.state.site.enable_downvotes}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
enableNsfw={this.state.site.enable_nsfw}
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{i.type_ == "comments" && (
|
{i.type_ == "comments" && (
|
||||||
<CommentNodes
|
<CommentNodes
|
||||||
key={(i.data as CommentView).comment.id}
|
key={(i.data as CommentView).comment.id}
|
||||||
nodes={[{ comment_view: i.data as CommentView }]}
|
nodes={[{ comment_view: i.data as CommentView }]}
|
||||||
|
moderators={None}
|
||||||
|
admins={None}
|
||||||
|
maxCommentsShown={None}
|
||||||
locked
|
locked
|
||||||
noIndent
|
noIndent
|
||||||
enableDownvotes={this.state.site.enable_downvotes}
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{i.type_ == "communities" && (
|
{i.type_ == "communities" && (
|
||||||
|
@ -558,15 +613,24 @@ 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
|
||||||
nodes={commentsToFlatNodes(comments)}
|
nodes={commentsToFlatNodes(comments)}
|
||||||
locked
|
locked
|
||||||
noIndent
|
noIndent
|
||||||
enableDownvotes={this.state.site.enable_downvotes}
|
moderators={None}
|
||||||
|
admins={None}
|
||||||
|
maxCommentsShown={None}
|
||||||
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -574,8 +638,14 @@ export class Search extends Component<any, SearchState> {
|
||||||
posts() {
|
posts() {
|
||||||
let posts: PostView[] = [];
|
let posts: PostView[] = [];
|
||||||
|
|
||||||
pushNotNull(posts, this.state.resolveObjectResponse?.post);
|
this.state.resolveObjectResponse.match({
|
||||||
pushNotNull(posts, this.state.searchResponse?.posts);
|
some: res => pushNotNull(posts, res.post),
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
|
this.state.searchResponse.match({
|
||||||
|
some: res => pushNotNull(posts, res.posts),
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -585,8 +655,11 @@ export class Search extends Component<any, SearchState> {
|
||||||
<PostListing
|
<PostListing
|
||||||
post_view={post}
|
post_view={post}
|
||||||
showCommunity
|
showCommunity
|
||||||
enableDownvotes={this.state.site.enable_downvotes}
|
duplicates={None}
|
||||||
enableNsfw={this.state.site.enable_nsfw}
|
moderators={None}
|
||||||
|
admins={None}
|
||||||
|
enableDownvotes={enableDownvotes(this.state.siteRes)}
|
||||||
|
enableNsfw={enableNsfw(this.state.siteRes)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -598,8 +671,14 @@ export class Search extends Component<any, SearchState> {
|
||||||
communities() {
|
communities() {
|
||||||
let communities: CommunityView[] = [];
|
let communities: CommunityView[] = [];
|
||||||
|
|
||||||
pushNotNull(communities, this.state.resolveObjectResponse?.community);
|
this.state.resolveObjectResponse.match({
|
||||||
pushNotNull(communities, this.state.searchResponse?.communities);
|
some: res => pushNotNull(communities, res.community),
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
|
this.state.searchResponse.match({
|
||||||
|
some: res => pushNotNull(communities, res.communities),
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -615,8 +694,14 @@ export class Search extends Component<any, SearchState> {
|
||||||
users() {
|
users() {
|
||||||
let users: PersonViewSafe[] = [];
|
let users: PersonViewSafe[] = [];
|
||||||
|
|
||||||
pushNotNull(users, this.state.resolveObjectResponse?.person);
|
this.state.resolveObjectResponse.match({
|
||||||
pushNotNull(users, this.state.searchResponse?.users);
|
some: res => pushNotNull(users, res.person),
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
|
this.state.searchResponse.match({
|
||||||
|
some: res => pushNotNull(users, res.users),
|
||||||
|
none: void 0,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -692,11 +777,14 @@ 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.creator && (
|
{this.state.creatorDetails.match({
|
||||||
<option value={this.state.creator.person.id}>
|
some: creator => (
|
||||||
{personSelectName(this.state.creator)}
|
<option value={creator.person_view.person.id}>
|
||||||
</option>
|
{personSelectName(creator.person_view)}
|
||||||
)}
|
</option>
|
||||||
|
),
|
||||||
|
none: <></>,
|
||||||
|
})}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -704,19 +792,21 @@ export class Search extends Component<any, SearchState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
resultsCount(): number {
|
resultsCount(): number {
|
||||||
let res = this.state.searchResponse;
|
let searchCount = this.state.searchResponse
|
||||||
let resObj = this.state.resolveObjectResponse;
|
.map(
|
||||||
let resObjCount =
|
r =>
|
||||||
resObj?.post || resObj?.person || resObj?.community || resObj?.comment
|
r.posts?.length +
|
||||||
? 1
|
r.comments?.length +
|
||||||
: 0;
|
r.communities?.length +
|
||||||
return (
|
r.users?.length
|
||||||
res?.posts?.length +
|
)
|
||||||
res?.comments?.length +
|
.unwrapOr(0);
|
||||||
res?.communities?.length +
|
|
||||||
res?.users?.length +
|
let resObjCount = this.state.resolveObjectResponse
|
||||||
resObjCount
|
.map(r => (r.post || r.person || r.community || r.comment ? 1 : 0))
|
||||||
);
|
.unwrapOr(0);
|
||||||
|
|
||||||
|
return resObjCount + searchCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePageChange(page: number) {
|
handlePageChange(page: number) {
|
||||||
|
@ -724,30 +814,34 @@ export class Search extends Component<any, SearchState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
search() {
|
search() {
|
||||||
let form: SearchForm = {
|
let community_id: Option<number> =
|
||||||
q: this.state.q,
|
this.state.communityId == 0 ? None : Some(this.state.communityId);
|
||||||
type_: this.state.type_,
|
let creator_id: Option<number> =
|
||||||
sort: this.state.sort,
|
this.state.creatorId == 0 ? None : Some(this.state.creatorId);
|
||||||
listing_type: this.state.listingType,
|
|
||||||
page: this.state.page,
|
|
||||||
limit: fetchLimit,
|
|
||||||
auth: authField(false),
|
|
||||||
};
|
|
||||||
if (this.state.communityId !== 0) {
|
|
||||||
form.community_id = this.state.communityId;
|
|
||||||
}
|
|
||||||
if (this.state.creatorId !== 0) {
|
|
||||||
form.creator_id = this.state.creatorId;
|
|
||||||
}
|
|
||||||
|
|
||||||
let resolveObjectForm: ResolveObject = {
|
console.log(community_id.unwrapOr(-22));
|
||||||
|
|
||||||
|
let form = new SearchForm({
|
||||||
q: this.state.q,
|
q: this.state.q,
|
||||||
auth: authField(false),
|
community_id,
|
||||||
};
|
community_name: None,
|
||||||
|
creator_id,
|
||||||
|
type_: Some(this.state.type_),
|
||||||
|
sort: Some(this.state.sort),
|
||||||
|
listing_type: Some(this.state.listingType),
|
||||||
|
page: Some(this.state.page),
|
||||||
|
limit: Some(fetchLimit),
|
||||||
|
auth: auth(false).ok(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let resolveObjectForm = new ResolveObject({
|
||||||
|
q: this.state.q,
|
||||||
|
auth: auth(false).ok(),
|
||||||
|
});
|
||||||
|
|
||||||
if (this.state.q != "") {
|
if (this.state.q != "") {
|
||||||
this.state.searchResponse = null;
|
this.state.searchResponse = None;
|
||||||
this.state.resolveObjectResponse = null;
|
this.state.resolveObjectResponse = None;
|
||||||
this.state.loading = true;
|
this.state.loading = true;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
WebSocketService.Instance.send(wsClient.search(form));
|
WebSocketService.Instance.send(wsClient.search(form));
|
||||||
|
@ -890,50 +984,56 @@ export class Search extends Component<any, SearchState> {
|
||||||
let op = wsUserOp(msg);
|
let op = wsUserOp(msg);
|
||||||
if (msg.error) {
|
if (msg.error) {
|
||||||
if (msg.error == "couldnt_find_object") {
|
if (msg.error == "couldnt_find_object") {
|
||||||
this.state.resolveObjectResponse = {
|
this.state.resolveObjectResponse = Some({
|
||||||
comment: null,
|
comment: None,
|
||||||
post: null,
|
post: None,
|
||||||
community: null,
|
community: None,
|
||||||
person: null,
|
person: None,
|
||||||
};
|
});
|
||||||
this.checkFinishedLoading();
|
this.checkFinishedLoading();
|
||||||
} else {
|
} else {
|
||||||
toast(i18n.t(msg.error), "danger");
|
toast(i18n.t(msg.error), "danger");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (op == UserOperation.Search) {
|
} else if (op == UserOperation.Search) {
|
||||||
let data = wsJsonToRes<SearchResponse>(msg).data;
|
let data = wsJsonToRes<SearchResponse>(msg, SearchResponse);
|
||||||
this.state.searchResponse = data;
|
this.state.searchResponse = Some(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).data;
|
let data = wsJsonToRes<CommentResponse>(msg, CommentResponse);
|
||||||
createCommentLikeRes(
|
createCommentLikeRes(
|
||||||
data.comment_view,
|
data.comment_view,
|
||||||
this.state.searchResponse?.comments
|
this.state.searchResponse.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).data;
|
let data = wsJsonToRes<PostResponse>(msg, PostResponse);
|
||||||
createPostLikeFindRes(data.post_view, this.state.searchResponse?.posts);
|
createPostLikeFindRes(
|
||||||
|
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>(msg).data;
|
let data = wsJsonToRes<ListCommunitiesResponse>(
|
||||||
|
msg,
|
||||||
|
ListCommunitiesResponse
|
||||||
|
);
|
||||||
this.state.communities = data.communities;
|
this.state.communities = data.communities;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
this.setupCommunityFilter();
|
this.setupCommunityFilter();
|
||||||
} else if (op == UserOperation.ResolveObject) {
|
} else if (op == UserOperation.ResolveObject) {
|
||||||
let data = wsJsonToRes<ResolveObjectResponse>(msg).data;
|
let data = wsJsonToRes<ResolveObjectResponse>(msg, ResolveObjectResponse);
|
||||||
this.state.resolveObjectResponse = data;
|
this.state.resolveObjectResponse = Some(data);
|
||||||
this.checkFinishedLoading();
|
this.checkFinishedLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
checkFinishedLoading() {
|
checkFinishedLoading() {
|
||||||
if (
|
if (
|
||||||
this.state.searchResponse != null &&
|
this.state.searchResponse.isSome() &&
|
||||||
this.state.resolveObjectResponse != null
|
this.state.resolveObjectResponse.isSome()
|
||||||
) {
|
) {
|
||||||
this.state.loading = false;
|
this.state.loading = false;
|
||||||
this.setState(this.state);
|
this.setState(this.state);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Either, Option } from "@sniptt/monads";
|
||||||
import {
|
import {
|
||||||
CommentView,
|
CommentView,
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
|
@ -5,6 +6,9 @@ import {
|
||||||
PersonMentionView,
|
PersonMentionView,
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This contains serialized data, it needs to be deserialized before use.
|
||||||
|
*/
|
||||||
export interface IsoData {
|
export interface IsoData {
|
||||||
path: string;
|
path: string;
|
||||||
routeData: any[];
|
routeData: any[];
|
||||||
|
@ -23,9 +27,9 @@ declare global {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InitialFetchRequest {
|
export interface InitialFetchRequest {
|
||||||
auth: string;
|
auth: Option<string>;
|
||||||
path: string;
|
|
||||||
client: LemmyHttp;
|
client: LemmyHttp;
|
||||||
|
path: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CommentNode {
|
export interface CommentNode {
|
||||||
|
@ -35,11 +39,10 @@ export interface CommentNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PostFormParams {
|
export interface PostFormParams {
|
||||||
name: string;
|
name: Option<string>;
|
||||||
url?: string;
|
url: Option<string>;
|
||||||
body?: string;
|
body: Option<string>;
|
||||||
community_name?: string;
|
nameOrId: Option<Either<string, number>>;
|
||||||
community_id?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CommentSortType {
|
export enum CommentSortType {
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
// 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";
|
||||||
import { BehaviorSubject, Subject } from "rxjs";
|
import { BehaviorSubject, Subject } from "rxjs";
|
||||||
import { isHttps } from "../env";
|
import { isHttps } from "../env";
|
||||||
|
import { i18n } from "../i18next";
|
||||||
|
import { isBrowser, toast } from "../utils";
|
||||||
|
|
||||||
interface Claims {
|
interface Claims {
|
||||||
sub: number;
|
sub: number;
|
||||||
|
@ -11,11 +14,16 @@ interface Claims {
|
||||||
iat: number;
|
iat: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface JwtInfo {
|
||||||
|
claims: Claims;
|
||||||
|
jwt: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class UserService {
|
export class UserService {
|
||||||
private static _instance: UserService;
|
private static _instance: UserService;
|
||||||
public myUserInfo: MyUserInfo;
|
public myUserInfo: Option<MyUserInfo> = None;
|
||||||
public claims: Claims;
|
public jwtInfo: Option<JwtInfo> = None;
|
||||||
public jwtSub: Subject<string> = new Subject<string>();
|
public jwtSub: Subject<Option<JwtInfo>> = new Subject<Option<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> =
|
||||||
|
@ -24,12 +32,7 @@ export class UserService {
|
||||||
new BehaviorSubject<number>(0);
|
new BehaviorSubject<number>(0);
|
||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
if (this.auth) {
|
this.setJwtInfo();
|
||||||
this.setClaims(this.auth);
|
|
||||||
} else {
|
|
||||||
// setTheme();
|
|
||||||
console.log("No JWT cookie found.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public login(res: LoginResponse) {
|
public login(res: LoginResponse) {
|
||||||
|
@ -37,26 +40,42 @@ export class UserService {
|
||||||
expires.setDate(expires.getDate() + 365);
|
expires.setDate(expires.getDate() + 365);
|
||||||
IsomorphicCookie.save("jwt", res.jwt, { expires, secure: isHttps });
|
IsomorphicCookie.save("jwt", res.jwt, { expires, secure: isHttps });
|
||||||
console.log("jwt cookie set");
|
console.log("jwt cookie set");
|
||||||
this.setClaims(res.jwt);
|
this.setJwtInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
public logout() {
|
public logout() {
|
||||||
this.claims = undefined;
|
this.jwtInfo = None;
|
||||||
this.myUserInfo = undefined;
|
this.myUserInfo = None;
|
||||||
// setTheme();
|
this.jwtSub.next(this.jwtInfo);
|
||||||
this.jwtSub.next("");
|
|
||||||
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.host;
|
document.cookie = "jwt=; Max-Age=0; path=/; domain=" + location.host;
|
||||||
|
location.reload(); // TODO may not be necessary anymore
|
||||||
console.log("Logged out.");
|
console.log("Logged out.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public get auth(): string {
|
public auth(throwErr = true): Result<string, string> {
|
||||||
return IsomorphicCookie.load("jwt");
|
// Can't use match to convert to result for some reason
|
||||||
|
let jwt = this.jwtInfo.map(j => j.jwt);
|
||||||
|
if (jwt.isSome()) {
|
||||||
|
return Ok(jwt.unwrap());
|
||||||
|
} else {
|
||||||
|
let msg = "No JWT cookie found";
|
||||||
|
if (throwErr && isBrowser()) {
|
||||||
|
console.log(msg);
|
||||||
|
toast(i18n.t("not_logged_in"), "danger");
|
||||||
|
}
|
||||||
|
return Err(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private setClaims(jwt: string) {
|
private setJwtInfo() {
|
||||||
this.claims = jwt_decode(jwt);
|
let jwt = IsomorphicCookie.load("jwt");
|
||||||
this.jwtSub.next(jwt);
|
|
||||||
|
if (jwt) {
|
||||||
|
let jwtInfo: JwtInfo = { jwt, claims: jwt_decode(jwt) };
|
||||||
|
this.jwtInfo = Some(jwtInfo);
|
||||||
|
this.jwtSub.next(this.jwtInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static get Instance() {
|
public static get Instance() {
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { PersonViewSafe, WebSocketJsonResponse } from "lemmy-js-client";
|
|
||||||
import { Observable } from "rxjs";
|
import { Observable } from "rxjs";
|
||||||
import { share } from "rxjs/operators";
|
import { share } from "rxjs/operators";
|
||||||
import {
|
import {
|
||||||
|
@ -15,9 +14,6 @@ export class WebSocketService {
|
||||||
private ws: WS;
|
private ws: WS;
|
||||||
public subject: Observable<any>;
|
public subject: Observable<any>;
|
||||||
|
|
||||||
public admins: PersonViewSafe[];
|
|
||||||
public banned: PersonViewSafe[];
|
|
||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
let firstConnect = true;
|
let firstConnect = true;
|
||||||
|
|
||||||
|
@ -34,7 +30,7 @@ export class WebSocketService {
|
||||||
console.log(`Connected to ${wsUri}`);
|
console.log(`Connected to ${wsUri}`);
|
||||||
|
|
||||||
if (!firstConnect) {
|
if (!firstConnect) {
|
||||||
let res: WebSocketJsonResponse<any> = {
|
let res = {
|
||||||
reconnect: true,
|
reconnect: true,
|
||||||
};
|
};
|
||||||
obs.next(res);
|
obs.next(res);
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { None, 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,
|
||||||
|
@ -5,6 +7,7 @@ import {
|
||||||
CommentReportView,
|
CommentReportView,
|
||||||
CommentView,
|
CommentView,
|
||||||
CommunityBlockView,
|
CommunityBlockView,
|
||||||
|
CommunityModeratorView,
|
||||||
CommunityView,
|
CommunityView,
|
||||||
GetSiteMetadata,
|
GetSiteMetadata,
|
||||||
GetSiteResponse,
|
GetSiteResponse,
|
||||||
|
@ -22,9 +25,6 @@ import {
|
||||||
Search,
|
Search,
|
||||||
SearchType,
|
SearchType,
|
||||||
SortType,
|
SortType,
|
||||||
UserOperation,
|
|
||||||
WebSocketJsonResponse,
|
|
||||||
WebSocketResponse,
|
|
||||||
} from "lemmy-js-client";
|
} from "lemmy-js-client";
|
||||||
import markdown_it from "markdown-it";
|
import markdown_it from "markdown-it";
|
||||||
import markdown_it_container from "markdown-it-container";
|
import markdown_it_container from "markdown-it-container";
|
||||||
|
@ -72,6 +72,7 @@ export const elementUrl = "https://element.io";
|
||||||
|
|
||||||
export const postRefetchSeconds: number = 60 * 1000;
|
export const postRefetchSeconds: number = 60 * 1000;
|
||||||
export const fetchLimit = 20;
|
export const fetchLimit = 20;
|
||||||
|
export const trendingFetchLimit = 6;
|
||||||
export const mentionDropdownFetchLimit = 10;
|
export const mentionDropdownFetchLimit = 10;
|
||||||
|
|
||||||
export const relTags = "noopener nofollow";
|
export const relTags = "noopener nofollow";
|
||||||
|
@ -98,20 +99,6 @@ export function randomStr(
|
||||||
.join("");
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
export function wsJsonToRes<ResponseType>(
|
|
||||||
msg: WebSocketJsonResponse<ResponseType>
|
|
||||||
): WebSocketResponse<ResponseType> {
|
|
||||||
return {
|
|
||||||
op: wsUserOp(msg),
|
|
||||||
data: msg.data,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function wsUserOp(msg: any): UserOperation {
|
|
||||||
let opStr: string = msg.op;
|
|
||||||
return UserOperation[opStr];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const md = new markdown_it({
|
export const md = new markdown_it({
|
||||||
html: false,
|
html: false,
|
||||||
linkify: true,
|
linkify: true,
|
||||||
|
@ -192,30 +179,135 @@ export function futureDaysToUnixTime(days: number): number {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canMod(
|
export function canMod(
|
||||||
myUserInfo: MyUserInfo,
|
mods: Option<CommunityModeratorView[]>,
|
||||||
modIds: number[],
|
admins: Option<PersonViewSafe[]>,
|
||||||
creator_id: number,
|
creator_id: number,
|
||||||
|
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.
|
||||||
if (myUserInfo) {
|
let adminsThenMods = admins
|
||||||
let yourIndex = modIds.findIndex(
|
.unwrapOr([])
|
||||||
id => id == myUserInfo.local_user_view.person.id
|
.map(a => a.person.id)
|
||||||
);
|
.concat(mods.unwrapOr([]).map(m => m.moderator.id));
|
||||||
if (yourIndex == -1) {
|
|
||||||
return false;
|
return myUserInfo.match({
|
||||||
} else {
|
some: me => {
|
||||||
// onSelf +1 on mod actions not for yourself, IE ban, remove, etc
|
let myIndex = adminsThenMods.findIndex(
|
||||||
modIds = modIds.slice(0, yourIndex + (onSelf ? 0 : 1));
|
id => id == me.local_user_view.person.id
|
||||||
return !modIds.includes(creator_id);
|
);
|
||||||
}
|
if (myIndex == -1) {
|
||||||
} else {
|
return false;
|
||||||
return false;
|
} else {
|
||||||
}
|
// onSelf +1 on mod actions not for yourself, IE ban, remove, etc
|
||||||
|
adminsThenMods = adminsThenMods.slice(0, myIndex + (onSelf ? 0 : 1));
|
||||||
|
return !adminsThenMods.includes(creator_id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
none: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isMod(modIds: number[], creator_id: number): boolean {
|
export function canAdmin(
|
||||||
return modIds.includes(creator_id);
|
admins: Option<PersonViewSafe[]>,
|
||||||
|
creator_id: number,
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
|
): boolean {
|
||||||
|
return canMod(None, admins, creator_id, myUserInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isMod(
|
||||||
|
mods: Option<CommunityModeratorView[]>,
|
||||||
|
creator_id: number
|
||||||
|
): boolean {
|
||||||
|
return mods.match({
|
||||||
|
some: mods => mods.map(m => m.moderator.id).includes(creator_id),
|
||||||
|
none: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function amMod(
|
||||||
|
mods: Option<CommunityModeratorView[]>,
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
|
): boolean {
|
||||||
|
return myUserInfo.match({
|
||||||
|
some: mui => isMod(mods, mui.local_user_view.person.id),
|
||||||
|
none: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isAdmin(
|
||||||
|
admins: Option<PersonViewSafe[]>,
|
||||||
|
creator_id: number
|
||||||
|
): boolean {
|
||||||
|
return admins.match({
|
||||||
|
some: admins => admins.map(a => a.person.id).includes(creator_id),
|
||||||
|
none: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function amAdmin(
|
||||||
|
admins: Option<PersonViewSafe[]>,
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
|
): boolean {
|
||||||
|
return myUserInfo.match({
|
||||||
|
some: mui => isAdmin(admins, mui.local_user_view.person.id),
|
||||||
|
none: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function amCommunityCreator(
|
||||||
|
mods: Option<CommunityModeratorView[]>,
|
||||||
|
creator_id: number,
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
|
): boolean {
|
||||||
|
return mods.match({
|
||||||
|
some: mods =>
|
||||||
|
myUserInfo
|
||||||
|
.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(
|
||||||
|
admins: Option<PersonViewSafe[]>,
|
||||||
|
creator_id: number,
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
|
): boolean {
|
||||||
|
return admins.match({
|
||||||
|
some: admins =>
|
||||||
|
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(
|
||||||
|
mods: Option<CommunityModeratorView[]>,
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
|
): boolean {
|
||||||
|
return mods.match({
|
||||||
|
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))/;
|
||||||
|
@ -321,13 +413,14 @@ export function debounce(func: any, wait = 1000, immediate = false) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLanguages(override?: string): string[] {
|
export function getLanguages(
|
||||||
let myUserInfo = UserService.Instance.myUserInfo;
|
override?: string,
|
||||||
let lang =
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
override ||
|
): string[] {
|
||||||
(myUserInfo?.local_user_view.local_user.lang
|
let myLang = myUserInfo
|
||||||
? myUserInfo.local_user_view.local_user.lang
|
.map(m => m.local_user_view.local_user.lang)
|
||||||
: "browser");
|
.unwrapOr("browser");
|
||||||
|
let lang = override || myLang;
|
||||||
|
|
||||||
if (lang == "browser" && isBrowser()) {
|
if (lang == "browser" && isBrowser()) {
|
||||||
return getBrowserLanguages();
|
return getBrowserLanguages();
|
||||||
|
@ -406,24 +499,26 @@ export function objectFlip(obj: any) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showAvatars(): boolean {
|
export function showAvatars(
|
||||||
return (
|
myUserInfo: Option<MyUserInfo> = UserService.Instance.myUserInfo
|
||||||
UserService.Instance.myUserInfo?.local_user_view.local_user.show_avatars ||
|
): boolean {
|
||||||
!UserService.Instance.myUserInfo
|
return myUserInfo
|
||||||
);
|
.map(m => m.local_user_view.local_user.show_avatars)
|
||||||
|
.unwrapOr(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showScores(): boolean {
|
export function showScores(
|
||||||
return (
|
myUserInfo: Option<MyUserInfo> = UserService.Instance.myUserInfo
|
||||||
UserService.Instance.myUserInfo?.local_user_view.local_user.show_scores ||
|
): boolean {
|
||||||
!UserService.Instance.myUserInfo
|
return myUserInfo
|
||||||
);
|
.map(m => m.local_user_view.local_user.show_scores)
|
||||||
|
.unwrapOr(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCakeDay(published: string): boolean {
|
export function isCakeDay(published: string): boolean {
|
||||||
// moment(undefined) or moment.utc(undefined) returns the current date/time
|
// moment(undefined) or moment.utc(undefined) returns the current date/time
|
||||||
// moment(null) or moment.utc(null) returns null
|
// moment(null) or moment.utc(null) returns null
|
||||||
const createDate = moment.utc(published || null).local();
|
const createDate = moment.utc(published).local();
|
||||||
const currentDate = moment(new Date());
|
const currentDate = moment(new Date());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -472,7 +567,7 @@ export function pictrsDeleteToast(
|
||||||
|
|
||||||
interface NotifyInfo {
|
interface NotifyInfo {
|
||||||
name: string;
|
name: string;
|
||||||
icon?: string;
|
icon: Option<string>;
|
||||||
link: string;
|
link: string;
|
||||||
body: string;
|
body: string;
|
||||||
}
|
}
|
||||||
|
@ -484,7 +579,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: info.icon ? info.icon : null,
|
avatar: info.icon,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
className: "text-dark",
|
className: "text-dark",
|
||||||
close: true,
|
close: true,
|
||||||
|
@ -666,16 +761,18 @@ async function communitySearch(text: string): Promise<CommunityTribute[]> {
|
||||||
|
|
||||||
export function getListingTypeFromProps(
|
export function getListingTypeFromProps(
|
||||||
props: any,
|
props: any,
|
||||||
defaultListingType: ListingType
|
defaultListingType: ListingType,
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
): ListingType {
|
): ListingType {
|
||||||
return props.match.params.listing_type
|
return props.match.params.listing_type
|
||||||
? routeListingTypeToEnum(props.match.params.listing_type)
|
? routeListingTypeToEnum(props.match.params.listing_type)
|
||||||
: UserService.Instance.myUserInfo
|
: myUserInfo.match({
|
||||||
? Object.values(ListingType)[
|
some: me =>
|
||||||
UserService.Instance.myUserInfo.local_user_view.local_user
|
Object.values(ListingType)[
|
||||||
.default_listing_type
|
me.local_user_view.local_user.default_listing_type
|
||||||
]
|
],
|
||||||
: defaultListingType;
|
none: defaultListingType,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getListingTypeFromPropsNoDefault(props: any): ListingType {
|
export function getListingTypeFromPropsNoDefault(props: any): ListingType {
|
||||||
|
@ -691,15 +788,19 @@ export function getDataTypeFromProps(props: any): DataType {
|
||||||
: DataType.Post;
|
: DataType.Post;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSortTypeFromProps(props: any): SortType {
|
export function getSortTypeFromProps(
|
||||||
|
props: any,
|
||||||
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
|
): SortType {
|
||||||
return props.match.params.sort
|
return props.match.params.sort
|
||||||
? routeSortTypeToEnum(props.match.params.sort)
|
? routeSortTypeToEnum(props.match.params.sort)
|
||||||
: UserService.Instance.myUserInfo
|
: myUserInfo.match({
|
||||||
? Object.values(SortType)[
|
some: mui =>
|
||||||
UserService.Instance.myUserInfo.local_user_view.local_user
|
Object.values(SortType)[
|
||||||
.default_sort_type
|
mui.local_user_view.local_user.default_sort_type
|
||||||
]
|
],
|
||||||
: SortType.Active;
|
none: SortType.Active,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPageFromProps(props: any): number {
|
export function getPageFromProps(props: any): number {
|
||||||
|
@ -744,42 +845,53 @@ export function saveCommentRes(data: CommentView, comments: CommentView[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Should only use the return now, no state?
|
||||||
export function updatePersonBlock(
|
export function updatePersonBlock(
|
||||||
data: BlockPersonResponse
|
data: BlockPersonResponse,
|
||||||
): PersonBlockView[] {
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
if (data.blocked) {
|
): Option<PersonBlockView[]> {
|
||||||
UserService.Instance.myUserInfo.person_blocks.push({
|
return myUserInfo.match({
|
||||||
person: UserService.Instance.myUserInfo.local_user_view.person,
|
some: (mui: MyUserInfo) => {
|
||||||
target: data.person_view.person,
|
if (data.blocked) {
|
||||||
});
|
mui.person_blocks.push({
|
||||||
toast(`${i18n.t("blocked")} ${data.person_view.person.name}`);
|
person: mui.local_user_view.person,
|
||||||
} else {
|
target: data.person_view.person,
|
||||||
UserService.Instance.myUserInfo.person_blocks =
|
});
|
||||||
UserService.Instance.myUserInfo.person_blocks.filter(
|
toast(`${i18n.t("blocked")} ${data.person_view.person.name}`);
|
||||||
i => i.target.id != data.person_view.person.id
|
} else {
|
||||||
);
|
mui.person_blocks = mui.person_blocks.filter(
|
||||||
toast(`${i18n.t("unblocked")} ${data.person_view.person.name}`);
|
i => i.target.id != data.person_view.person.id
|
||||||
}
|
);
|
||||||
return UserService.Instance.myUserInfo.person_blocks;
|
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,
|
||||||
): CommunityBlockView[] {
|
myUserInfo = UserService.Instance.myUserInfo
|
||||||
if (data.blocked) {
|
): Option<CommunityBlockView[]> {
|
||||||
UserService.Instance.myUserInfo.community_blocks.push({
|
return myUserInfo.match({
|
||||||
person: UserService.Instance.myUserInfo.local_user_view.person,
|
some: (mui: MyUserInfo) => {
|
||||||
community: data.community_view.community,
|
if (data.blocked) {
|
||||||
});
|
mui.community_blocks.push({
|
||||||
toast(`${i18n.t("blocked")} ${data.community_view.community.name}`);
|
person: mui.local_user_view.person,
|
||||||
} else {
|
community: data.community_view.community,
|
||||||
UserService.Instance.myUserInfo.community_blocks =
|
});
|
||||||
UserService.Instance.myUserInfo.community_blocks.filter(
|
toast(`${i18n.t("blocked")} ${data.community_view.community.name}`);
|
||||||
i => i.community.id != data.community_view.community.id
|
} else {
|
||||||
);
|
mui.community_blocks = mui.community_blocks.filter(
|
||||||
toast(`${i18n.t("unblocked")} ${data.community_view.community.name}`);
|
i => i.community.id != data.community_view.community.id
|
||||||
}
|
);
|
||||||
return UserService.Instance.myUserInfo.community_blocks;
|
toast(`${i18n.t("unblocked")} ${data.community_view.community.name}`);
|
||||||
|
}
|
||||||
|
return Some(mui.community_blocks);
|
||||||
|
},
|
||||||
|
none: None,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createCommentLikeRes(
|
export function createCommentLikeRes(
|
||||||
|
@ -910,7 +1022,8 @@ function commentSort(tree: CommentNodeI[], sort: CommentSortType) {
|
||||||
(a, b) =>
|
(a, b) =>
|
||||||
+a.comment_view.comment.removed - +b.comment_view.comment.removed ||
|
+a.comment_view.comment.removed - +b.comment_view.comment.removed ||
|
||||||
+a.comment_view.comment.deleted - +b.comment_view.comment.deleted ||
|
+a.comment_view.comment.deleted - +b.comment_view.comment.deleted ||
|
||||||
hotRankComment(b.comment_view) - hotRankComment(a.comment_view)
|
hotRankComment(b.comment_view as CommentView) -
|
||||||
|
hotRankComment(a.comment_view as CommentView)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -953,6 +1066,7 @@ export function buildCommentsTree(
|
||||||
let node: CommentNodeI = {
|
let node: CommentNodeI = {
|
||||||
comment_view: comment_view,
|
comment_view: comment_view,
|
||||||
children: [],
|
children: [],
|
||||||
|
depth: 0,
|
||||||
};
|
};
|
||||||
map.set(comment_view.comment.id, { ...node });
|
map.set(comment_view.comment.id, { ...node });
|
||||||
}
|
}
|
||||||
|
@ -960,15 +1074,18 @@ export function buildCommentsTree(
|
||||||
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 = comment_view.comment.parent_id;
|
let parent_id = comment_view.comment.parent_id;
|
||||||
if (parent_id) {
|
parent_id.match({
|
||||||
let parent = map.get(parent_id);
|
some: parentId => {
|
||||||
// Necessary because blocked comment might not exist
|
let parent = map.get(parentId);
|
||||||
if (parent) {
|
// Necessary because blocked comment might not exist
|
||||||
parent.children.push(child);
|
if (parent) {
|
||||||
}
|
parent.children.push(child);
|
||||||
} else {
|
}
|
||||||
tree.push(child);
|
},
|
||||||
}
|
none: () => {
|
||||||
|
tree.push(child);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
setDepth(child);
|
setDepth(child);
|
||||||
}
|
}
|
||||||
|
@ -993,35 +1110,41 @@ export function insertCommentIntoTree(tree: CommentNodeI[], cv: CommentView) {
|
||||||
depth: 0,
|
depth: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (cv.comment.parent_id) {
|
cv.comment.parent_id.match({
|
||||||
let parentComment = searchCommentTree(tree, cv.comment.parent_id);
|
some: parentId => {
|
||||||
if (parentComment) {
|
let parentComment = searchCommentTree(tree, parentId);
|
||||||
node.depth = parentComment.depth + 1;
|
parentComment.match({
|
||||||
parentComment.children.unshift(node);
|
some: pComment => {
|
||||||
}
|
node.depth = pComment.depth + 1;
|
||||||
} else {
|
pComment.children.unshift(node);
|
||||||
tree.unshift(node);
|
},
|
||||||
}
|
none: void 0,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
none: () => {
|
||||||
|
tree.unshift(node);
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function searchCommentTree(
|
export function searchCommentTree(
|
||||||
tree: CommentNodeI[],
|
tree: CommentNodeI[],
|
||||||
id: number
|
id: number
|
||||||
): CommentNodeI {
|
): Option<CommentNodeI> {
|
||||||
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 node;
|
return Some(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const child of node.children) {
|
for (const child of node.children) {
|
||||||
const res = searchCommentTree([child], id);
|
let res = searchCommentTree([child], id);
|
||||||
|
|
||||||
if (res) {
|
if (res.isSome()) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const colorList: string[] = [
|
export const colorList: string[] = [
|
||||||
|
@ -1044,7 +1167,7 @@ export function hostname(url: string): string {
|
||||||
|
|
||||||
export function validTitle(title?: string): boolean {
|
export function validTitle(title?: string): boolean {
|
||||||
// Initial title is null, minimum length is taken care of by textarea's minLength={3}
|
// Initial title is null, minimum length is taken care of by textarea's minLength={3}
|
||||||
if (title === null || title.length < 3) return true;
|
if (!title || title.length < 3) return true;
|
||||||
|
|
||||||
const regex = new RegExp(/.*\S.*/, "g");
|
const regex = new RegExp(/.*\S.*/, "g");
|
||||||
|
|
||||||
|
@ -1068,11 +1191,51 @@ export function isBrowser() {
|
||||||
return typeof window !== "undefined";
|
return typeof window !== "undefined";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setIsoData(context: any): IsoData {
|
export function setIsoData<Type1, Type2, Type3, Type4, Type5>(
|
||||||
let isoData: IsoData = isBrowser()
|
context: any,
|
||||||
? window.isoData
|
cls1?: ClassConstructor<Type1>,
|
||||||
: context.router.staticContext;
|
cls2?: ClassConstructor<Type2>,
|
||||||
return isoData;
|
cls3?: ClassConstructor<Type3>,
|
||||||
|
cls4?: ClassConstructor<Type4>,
|
||||||
|
cls5?: ClassConstructor<Type5>
|
||||||
|
): IsoData {
|
||||||
|
// If its the browser, you need to deserialize the data from the window
|
||||||
|
if (isBrowser()) {
|
||||||
|
let json = window.isoData;
|
||||||
|
let routeData = json.routeData;
|
||||||
|
let routeDataOut: any[] = [];
|
||||||
|
|
||||||
|
// 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 isoData: IsoData = {
|
||||||
|
path: json.path,
|
||||||
|
site_res: convertWindowJson(GetSiteResponse, json.site_res),
|
||||||
|
routeData: routeDataOut,
|
||||||
|
};
|
||||||
|
return isoData;
|
||||||
|
} else return context.router.staticContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
export function wsSubscribe(parseMessage: any): Subscription {
|
||||||
|
@ -1089,24 +1252,6 @@ export function wsSubscribe(parseMessage: any): Subscription {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setOptionalAuth(obj: any, auth = UserService.Instance.auth) {
|
|
||||||
if (auth) {
|
|
||||||
obj.auth = auth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function authField(
|
|
||||||
throwErr = true,
|
|
||||||
auth = UserService.Instance.auth
|
|
||||||
): string {
|
|
||||||
if (auth == null && throwErr) {
|
|
||||||
toast(i18n.t("not_logged_in"), "danger");
|
|
||||||
throw "Not logged in";
|
|
||||||
} else {
|
|
||||||
return auth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
moment.updateLocale("en", {
|
moment.updateLocale("en", {
|
||||||
relativeTime: {
|
relativeTime: {
|
||||||
future: "in %s",
|
future: "in %s",
|
||||||
|
@ -1141,7 +1286,9 @@ export function restoreScrollPosition(context: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showLocal(isoData: IsoData): boolean {
|
export function showLocal(isoData: IsoData): boolean {
|
||||||
return isoData.site_res.federated_instances?.linked.length > 0;
|
return isoData.site_res.federated_instances
|
||||||
|
.map(f => f.linked.length > 0)
|
||||||
|
.unwrapOr(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ChoicesValue {
|
interface ChoicesValue {
|
||||||
|
@ -1168,12 +1315,15 @@ export function personToChoice(pvs: PersonViewSafe): ChoicesValue {
|
||||||
export async function fetchCommunities(q: string) {
|
export async function fetchCommunities(q: string) {
|
||||||
let form: Search = {
|
let form: Search = {
|
||||||
q,
|
q,
|
||||||
type_: SearchType.Communities,
|
type_: Some(SearchType.Communities),
|
||||||
sort: SortType.TopAll,
|
sort: Some(SortType.TopAll),
|
||||||
listing_type: ListingType.All,
|
listing_type: Some(ListingType.All),
|
||||||
page: 1,
|
page: Some(1),
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
auth: authField(false),
|
community_id: None,
|
||||||
|
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);
|
||||||
|
@ -1182,12 +1332,15 @@ export async function fetchCommunities(q: string) {
|
||||||
export async function fetchUsers(q: string) {
|
export async function fetchUsers(q: string) {
|
||||||
let form: Search = {
|
let form: Search = {
|
||||||
q,
|
q,
|
||||||
type_: SearchType.Users,
|
type_: Some(SearchType.Users),
|
||||||
sort: SortType.TopAll,
|
sort: Some(SortType.TopAll),
|
||||||
listing_type: ListingType.All,
|
listing_type: Some(ListingType.All),
|
||||||
page: 1,
|
page: Some(1),
|
||||||
limit: fetchLimit,
|
limit: Some(fetchLimit),
|
||||||
auth: authField(false),
|
community_id: None,
|
||||||
|
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);
|
||||||
|
@ -1233,7 +1386,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 || pvs.person.name;
|
let pName = pvs.person.display_name.unwrapOr(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}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1254,9 +1407,11 @@ export function numToSI(value: number): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isBanned(ps: PersonSafe): boolean {
|
export function isBanned(ps: PersonSafe): boolean {
|
||||||
|
let expires = ps.ban_expires;
|
||||||
// Add Z to convert from UTC date
|
// Add Z to convert from UTC date
|
||||||
if (ps.ban_expires) {
|
// TODO this check probably isn't necessary anymore
|
||||||
if (ps.banned && new Date(ps.ban_expires + "Z") > new Date()) {
|
if (expires.isSome()) {
|
||||||
|
if (ps.banned && new Date(expires.unwrap() + "Z") > new Date()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1271,3 +1426,15 @@ export function pushNotNull(array: any[], new_item?: any) {
|
||||||
array.push(...new_item);
|
array.push(...new_item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function auth(throwErr = true): Result<string, string> {
|
||||||
|
return UserService.Instance.auth(throwErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enableDownvotes(siteRes: GetSiteResponse): boolean {
|
||||||
|
return siteRes.site_view.map(s => s.site.enable_downvotes).unwrapOr(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enableNsfw(siteRes: GetSiteResponse): boolean {
|
||||||
|
return siteRes.site_view.map(s => s.site.enable_nsfw).unwrapOr(false);
|
||||||
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noFallthroughCasesInSwitch": true
|
"experimentalDecorators": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*",
|
"src/**/*",
|
||||||
|
|
148
yarn.lock
148
yarn.lock
|
@ -95,6 +95,15 @@
|
||||||
jsesc "^2.5.1"
|
jsesc "^2.5.1"
|
||||||
source-map "^0.5.0"
|
source-map "^0.5.0"
|
||||||
|
|
||||||
|
"@babel/generator@^7.18.2":
|
||||||
|
version "7.18.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d"
|
||||||
|
integrity sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.18.2"
|
||||||
|
"@jridgewell/gen-mapping" "^0.3.0"
|
||||||
|
jsesc "^2.5.1"
|
||||||
|
|
||||||
"@babel/helper-annotate-as-pure@^7.16.7":
|
"@babel/helper-annotate-as-pure@^7.16.7":
|
||||||
version "7.16.7"
|
version "7.16.7"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862"
|
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862"
|
||||||
|
@ -156,6 +165,19 @@
|
||||||
"@babel/helper-replace-supers" "^7.16.7"
|
"@babel/helper-replace-supers" "^7.16.7"
|
||||||
"@babel/helper-split-export-declaration" "^7.16.7"
|
"@babel/helper-split-export-declaration" "^7.16.7"
|
||||||
|
|
||||||
|
"@babel/helper-create-class-features-plugin@^7.18.0":
|
||||||
|
version "7.18.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.0.tgz#fac430912606331cb075ea8d82f9a4c145a4da19"
|
||||||
|
integrity sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-annotate-as-pure" "^7.16.7"
|
||||||
|
"@babel/helper-environment-visitor" "^7.16.7"
|
||||||
|
"@babel/helper-function-name" "^7.17.9"
|
||||||
|
"@babel/helper-member-expression-to-functions" "^7.17.7"
|
||||||
|
"@babel/helper-optimise-call-expression" "^7.16.7"
|
||||||
|
"@babel/helper-replace-supers" "^7.16.7"
|
||||||
|
"@babel/helper-split-export-declaration" "^7.16.7"
|
||||||
|
|
||||||
"@babel/helper-create-regexp-features-plugin@^7.16.7":
|
"@babel/helper-create-regexp-features-plugin@^7.16.7":
|
||||||
version "7.16.7"
|
version "7.16.7"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.7.tgz#0cb82b9bac358eb73bfbd73985a776bfa6b14d48"
|
resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.7.tgz#0cb82b9bac358eb73bfbd73985a776bfa6b14d48"
|
||||||
|
@ -185,6 +207,11 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.16.7"
|
"@babel/types" "^7.16.7"
|
||||||
|
|
||||||
|
"@babel/helper-environment-visitor@^7.18.2":
|
||||||
|
version "7.18.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz#8a6d2dedb53f6bf248e31b4baf38739ee4a637bd"
|
||||||
|
integrity sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ==
|
||||||
|
|
||||||
"@babel/helper-explode-assignable-expression@^7.16.7":
|
"@babel/helper-explode-assignable-expression@^7.16.7":
|
||||||
version "7.16.7"
|
version "7.16.7"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a"
|
resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a"
|
||||||
|
@ -230,6 +257,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/types" "^7.16.7"
|
"@babel/types" "^7.16.7"
|
||||||
|
|
||||||
|
"@babel/helper-member-expression-to-functions@^7.17.7":
|
||||||
|
version "7.17.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4"
|
||||||
|
integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/types" "^7.17.0"
|
||||||
|
|
||||||
"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7":
|
"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7":
|
||||||
version "7.16.7"
|
version "7.16.7"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437"
|
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437"
|
||||||
|
@ -277,6 +311,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5"
|
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5"
|
||||||
integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==
|
integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==
|
||||||
|
|
||||||
|
"@babel/helper-plugin-utils@^7.17.12":
|
||||||
|
version "7.17.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96"
|
||||||
|
integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==
|
||||||
|
|
||||||
"@babel/helper-remap-async-to-generator@^7.16.8":
|
"@babel/helper-remap-async-to-generator@^7.16.8":
|
||||||
version "7.16.8"
|
version "7.16.8"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3"
|
resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3"
|
||||||
|
@ -297,6 +336,17 @@
|
||||||
"@babel/traverse" "^7.16.7"
|
"@babel/traverse" "^7.16.7"
|
||||||
"@babel/types" "^7.16.7"
|
"@babel/types" "^7.16.7"
|
||||||
|
|
||||||
|
"@babel/helper-replace-supers@^7.18.2":
|
||||||
|
version "7.18.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.2.tgz#41fdfcc9abaf900e18ba6e5931816d9062a7b2e0"
|
||||||
|
integrity sha512-XzAIyxx+vFnrOxiQrToSUOzUOn0e1J2Li40ntddek1Y69AXUTXoDJ40/D5RdjFu7s7qHiaeoTiempZcbuVXh2Q==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-environment-visitor" "^7.18.2"
|
||||||
|
"@babel/helper-member-expression-to-functions" "^7.17.7"
|
||||||
|
"@babel/helper-optimise-call-expression" "^7.16.7"
|
||||||
|
"@babel/traverse" "^7.18.2"
|
||||||
|
"@babel/types" "^7.18.2"
|
||||||
|
|
||||||
"@babel/helper-simple-access@^7.16.7":
|
"@babel/helper-simple-access@^7.16.7":
|
||||||
version "7.16.7"
|
version "7.16.7"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7"
|
resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7"
|
||||||
|
@ -387,6 +437,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef"
|
||||||
integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==
|
integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==
|
||||||
|
|
||||||
|
"@babel/parser@^7.18.5":
|
||||||
|
version "7.18.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.5.tgz#337062363436a893a2d22faa60be5bb37091c83c"
|
||||||
|
integrity sha512-YZWVaglMiplo7v8f1oMQ5ZPQr0vn7HPeZXxXWsxXJRjGVrzUFn9OxFQl1sb5wzfootjA/yChhW84BV+383FSOw==
|
||||||
|
|
||||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7":
|
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7":
|
||||||
version "7.16.7"
|
version "7.16.7"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz#4eda6d6c2a0aa79c70fa7b6da67763dfe2141050"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz#4eda6d6c2a0aa79c70fa7b6da67763dfe2141050"
|
||||||
|
@ -429,6 +484,18 @@
|
||||||
"@babel/helper-plugin-utils" "^7.16.7"
|
"@babel/helper-plugin-utils" "^7.16.7"
|
||||||
"@babel/plugin-syntax-class-static-block" "^7.14.5"
|
"@babel/plugin-syntax-class-static-block" "^7.14.5"
|
||||||
|
|
||||||
|
"@babel/plugin-proposal-decorators@^7.18.2":
|
||||||
|
version "7.18.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.18.2.tgz#dbe4086d2d42db489399783c3aa9272e9700afd4"
|
||||||
|
integrity sha512-kbDISufFOxeczi0v4NQP3p5kIeW6izn/6klfWBrIIdGZZe4UpHR+QU03FAoWjGGd9SUXAwbw2pup1kaL4OQsJQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-create-class-features-plugin" "^7.18.0"
|
||||||
|
"@babel/helper-plugin-utils" "^7.17.12"
|
||||||
|
"@babel/helper-replace-supers" "^7.18.2"
|
||||||
|
"@babel/helper-split-export-declaration" "^7.16.7"
|
||||||
|
"@babel/plugin-syntax-decorators" "^7.17.12"
|
||||||
|
charcodes "^0.2.0"
|
||||||
|
|
||||||
"@babel/plugin-proposal-dynamic-import@^7.16.7":
|
"@babel/plugin-proposal-dynamic-import@^7.16.7":
|
||||||
version "7.16.7"
|
version "7.16.7"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2"
|
||||||
|
@ -552,6 +619,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/helper-plugin-utils" "^7.14.5"
|
"@babel/helper-plugin-utils" "^7.14.5"
|
||||||
|
|
||||||
|
"@babel/plugin-syntax-decorators@^7.17.12":
|
||||||
|
version "7.17.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.17.12.tgz#02e8f678602f0af8222235271efea945cfdb018a"
|
||||||
|
integrity sha512-D1Hz0qtGTza8K2xGyEdVNCYLdVHukAcbQr4K3/s6r/esadyEriZovpJimQOpu8ju4/jV8dW/1xdaE0UpDroidw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-plugin-utils" "^7.17.12"
|
||||||
|
|
||||||
"@babel/plugin-syntax-dynamic-import@^7.8.3":
|
"@babel/plugin-syntax-dynamic-import@^7.8.3":
|
||||||
version "7.8.3"
|
version "7.8.3"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
|
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
|
||||||
|
@ -1092,6 +1166,22 @@
|
||||||
debug "^4.1.0"
|
debug "^4.1.0"
|
||||||
globals "^11.1.0"
|
globals "^11.1.0"
|
||||||
|
|
||||||
|
"@babel/traverse@^7.18.2":
|
||||||
|
version "7.18.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.5.tgz#94a8195ad9642801837988ab77f36e992d9a20cd"
|
||||||
|
integrity sha512-aKXj1KT66sBj0vVzk6rEeAO6Z9aiiQ68wfDgge3nHhA/my6xMM/7HGQUNumKZaoa2qUPQ5whJG9aAifsxUKfLA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/code-frame" "^7.16.7"
|
||||||
|
"@babel/generator" "^7.18.2"
|
||||||
|
"@babel/helper-environment-visitor" "^7.18.2"
|
||||||
|
"@babel/helper-function-name" "^7.17.9"
|
||||||
|
"@babel/helper-hoist-variables" "^7.16.7"
|
||||||
|
"@babel/helper-split-export-declaration" "^7.16.7"
|
||||||
|
"@babel/parser" "^7.18.5"
|
||||||
|
"@babel/types" "^7.18.4"
|
||||||
|
debug "^4.1.0"
|
||||||
|
globals "^11.1.0"
|
||||||
|
|
||||||
"@babel/types@^7", "@babel/types@^7.0.0-beta.54", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.4.4":
|
"@babel/types@^7", "@babel/types@^7.0.0-beta.54", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.4.4":
|
||||||
version "7.16.8"
|
version "7.16.8"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.8.tgz#0ba5da91dd71e0a4e7781a30f22770831062e3c1"
|
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.8.tgz#0ba5da91dd71e0a4e7781a30f22770831062e3c1"
|
||||||
|
@ -1108,6 +1198,14 @@
|
||||||
"@babel/helper-validator-identifier" "^7.16.7"
|
"@babel/helper-validator-identifier" "^7.16.7"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
|
"@babel/types@^7.18.2", "@babel/types@^7.18.4":
|
||||||
|
version "7.18.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.4.tgz#27eae9b9fd18e9dccc3f9d6ad051336f307be354"
|
||||||
|
integrity sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/helper-validator-identifier" "^7.16.7"
|
||||||
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@discoveryjs/json-ext@^0.5.0":
|
"@discoveryjs/json-ext@^0.5.0":
|
||||||
version "0.5.6"
|
version "0.5.6"
|
||||||
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz#d5e0706cf8c6acd8c6032f8d54070af261bbbb2f"
|
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz#d5e0706cf8c6acd8c6032f8d54070af261bbbb2f"
|
||||||
|
@ -1151,11 +1249,25 @@
|
||||||
update-notifier "^2.2.0"
|
update-notifier "^2.2.0"
|
||||||
yargs "^8.0.2"
|
yargs "^8.0.2"
|
||||||
|
|
||||||
|
"@jridgewell/gen-mapping@^0.3.0":
|
||||||
|
version "0.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9"
|
||||||
|
integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/set-array" "^1.0.0"
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
"@jridgewell/trace-mapping" "^0.3.9"
|
||||||
|
|
||||||
"@jridgewell/resolve-uri@^3.0.3":
|
"@jridgewell/resolve-uri@^3.0.3":
|
||||||
version "3.0.5"
|
version "3.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c"
|
resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c"
|
||||||
integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==
|
integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==
|
||||||
|
|
||||||
|
"@jridgewell/set-array@^1.0.0":
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea"
|
||||||
|
integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec@^1.4.10":
|
"@jridgewell/sourcemap-codec@^1.4.10":
|
||||||
version "1.4.11"
|
version "1.4.11"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec"
|
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec"
|
||||||
|
@ -1169,6 +1281,14 @@
|
||||||
"@jridgewell/resolve-uri" "^3.0.3"
|
"@jridgewell/resolve-uri" "^3.0.3"
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.10"
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
|
||||||
|
"@jridgewell/trace-mapping@^0.3.9":
|
||||||
|
version "0.3.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea"
|
||||||
|
integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/resolve-uri" "^3.0.3"
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.4.10"
|
||||||
|
|
||||||
"@leichtgewicht/ip-codec@^2.0.1":
|
"@leichtgewicht/ip-codec@^2.0.1":
|
||||||
version "2.0.3"
|
version "2.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz#0300943770e04231041a51bd39f0439b5c7ab4f0"
|
resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.3.tgz#0300943770e04231041a51bd39f0439b5c7ab4f0"
|
||||||
|
@ -1200,6 +1320,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.2.tgz#830beaec4b4091a9e9398ac50f865ddea52186b9"
|
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.2.tgz#830beaec4b4091a9e9398ac50f865ddea52186b9"
|
||||||
integrity sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==
|
integrity sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==
|
||||||
|
|
||||||
|
"@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"
|
||||||
|
@ -2254,6 +2379,11 @@ chalk@^4.0.0:
|
||||||
ansi-styles "^4.1.0"
|
ansi-styles "^4.1.0"
|
||||||
supports-color "^7.1.0"
|
supports-color "^7.1.0"
|
||||||
|
|
||||||
|
charcodes@^0.2.0:
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/charcodes/-/charcodes-0.2.0.tgz#5208d327e6cc05f99eb80ffc814707572d1f14e4"
|
||||||
|
integrity sha512-Y4kiDb+AM4Ecy58YkuZrrSRJBDQdQ2L+NyS1vHHFtNtUjgutcZfx3yp1dAONI/oPaPmyGfCLx5CxL+zauIMyKQ==
|
||||||
|
|
||||||
check-password-strength@^2.0.5:
|
check-password-strength@^2.0.5:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/check-password-strength/-/check-password-strength-2.0.5.tgz#bb10da01d24bd69e5e629c5cea2a6b729e5061af"
|
resolved "https://registry.yarnpkg.com/check-password-strength/-/check-password-strength-2.0.5.tgz#bb10da01d24bd69e5e629c5cea2a6b729e5061af"
|
||||||
|
@ -2323,6 +2453,11 @@ 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 sha1-dKv9YZ3zcLnVSrFEdVaOl91kwME=
|
integrity sha1-dKv9YZ3zcLnVSrFEdVaOl91kwME=
|
||||||
|
|
||||||
|
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.1"
|
version "2.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
|
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
|
||||||
|
@ -4813,10 +4948,10 @@ lcid@^1.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
invert-kv "^1.0.0"
|
invert-kv "^1.0.0"
|
||||||
|
|
||||||
lemmy-js-client@0.16.4:
|
lemmy-js-client@0.17.0-rc.30:
|
||||||
version "0.16.4"
|
version "0.17.0-rc.30"
|
||||||
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.16.4.tgz#d24bae2b0d93c4d13eb4a5e5ddceaa2999f94740"
|
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.30.tgz#91cc926e662a5cd27f87cd2e6cdfcd210176745a"
|
||||||
integrity sha512-EFHl6tbFZ0jk8VE68bgZOXoWuNHVzfcsyoAEZeHP6f8PkQ1g9zjxB/e3b5cIG2fFzOLsYIDh2w/SJy21WkFiiA==
|
integrity sha512-AcG8IZNNTa54BAXEqsL/QNlyPPwLntRLWpIOw9S3u84824d5inL7UCKnyx0UMbQklUuH/D3E2K9WNmZiUdvr3A==
|
||||||
|
|
||||||
levn@^0.4.1:
|
levn@^0.4.1:
|
||||||
version "0.4.1"
|
version "0.4.1"
|
||||||
|
@ -6673,6 +6808,11 @@ 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@^9.0.0:
|
regenerate-unicode-properties@^9.0.0:
|
||||||
version "9.0.0"
|
version "9.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326"
|
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326"
|
||||||
|
|
Loading…
Reference in a new issue